void HttpServer::handleNormalFile(HttpClientHandler* hdlr,const QHttpRequestHeader & hdr,const QString & path) { QFileInfo fi(path); QString ext = fi.suffix(); if (hdr.hasKey("If-Modified-Since")) { QDateTime dt = parseDate(hdr.value("If-Modified-Since")); if (dt.isValid() && dt < fi.lastModified()) { HttpResponseHeader rhdr(304,hdr.majorVersion(),hdr.minorVersion()); setDefaultResponseHeaders(rhdr,"text/html",true); rhdr.setValue("Cache-Control","max-age=0"); rhdr.setValue("Last-Modified",DateTimeToString(fi.lastModified(),false)); rhdr.setValue("Expires",DateTimeToString(QDateTime::currentDateTime().toUTC().addSecs(3600),false)); hdlr->sendResponse(rhdr); return; } } HttpResponseHeader rhdr(200,hdr.majorVersion(),hdr.minorVersion()); setDefaultResponseHeaders(rhdr,ExtensionToContentType(ext),true); rhdr.setValue("Last-Modified",DateTimeToString(fi.lastModified(),false)); rhdr.setValue("Expires",DateTimeToString(QDateTime::currentDateTime().toUTC().addSecs(3600),false)); rhdr.setValue("Cache-Control","private"); if (!hdlr->sendFile(rhdr,path)) { HttpResponseHeader nhdr(404,hdr.majorVersion(),hdr.minorVersion()); setDefaultResponseHeaders(nhdr,"text/html",false); hdlr->send404(nhdr,path); } }
/*---------------------------------------------------------------------------*/ QVariant HttpRecognizer::comment( IConnection* connection ) { Q_ASSERT (connection); if (!mConnections.contains( connection->networkInfo() )) return "No comment yet"; const HttpConnection con = mConnections.value( connection->networkInfo() ); const QHttpRequestHeader request = con.lastRequestHeader(); const QHttpResponseHeader response = con.lastResponseHeader(); return request.method() + " " + request.value( "host" ) + request.path() + (response.isValid() ? "\nHTTP " + QString::number( response.statusCode() ) + " " + response.reasonPhrase() : "" ); }
void HttpServer::handlePost(HttpClientHandler* hdlr,const QHttpRequestHeader & hdr,const QByteArray & data) { // this is either a file or a login if (hdr.value("Content-Type").startsWith("multipart/form-data")) { handleTorrentPost(hdlr,hdr,data); } else if (!checkLogin(hdr,data)) { QHttpRequestHeader tmp = hdr; tmp.setRequest("GET","/login.html",1,1); handleGet(hdlr,tmp); } else { handleGet(hdlr,hdr,true); } }
bool HttpServer::checkSession(const QHttpRequestHeader & hdr) { // check session in cookie int session_id = 0; if (hdr.hasKey("Cookie")) { QString cookie = hdr.value("Cookie"); int idx = cookie.find("KT_SESSID="); if (idx == -1) return false; QString number; idx += QString("KT_SESSID=").length(); while (idx < cookie.length()) { if (cookie[idx] >= '0' && cookie[idx] <= '9') number += cookie[idx]; else break; idx++; } session_id = number.toInt(); } if (session_id == session.sessionId) { // check if the session hasn't expired yet if(session.last_access.secsTo(QTime::currentTime())<WebInterfacePluginSettings::sessionTTL()) { session.last_access=QTime::currentTime(); } else { return false; } } else return false; return true; }
bool HttpServer::checkSession(const QHttpRequestHeader & hdr) { // check session in cookie int session_id = 0; if (hdr.hasKey("Cookie")) { QString cookie = hdr.value("Cookie"); QRegExp rx("KT_SESSID=(\\d+)",Qt::CaseInsensitive); int pos = 0; while ((pos = rx.indexIn(cookie, pos)) != -1) { session_id = rx.cap(1).toInt(); if (session_id == session.sessionId) break; pos += rx.matchedLength(); } } if (session_id == session.sessionId) { // check if the session hasn't expired yet if(session.last_access.secsTo(QTime::currentTime())<WebInterfacePluginSettings::sessionTTL()) { session.last_access=QTime::currentTime(); } else { return false; } } else return false; return true; }
void HttpServer::handleGet(HttpClientHandler* hdlr,const QHttpRequestHeader & hdr,bool do_not_check_session) { QString file = hdr.path(); if (file == "/") file = "/login.html"; //Out(SYS_WEB|LOG_DEBUG) << "GET " << hdr.path() << endl; KURL url; url.setEncodedPathAndQuery(file); QString path = rootDir + bt::DirSeparator() + WebInterfacePluginSettings::skin() + url.path(); // first check if the file exists (if not send 404) if (!bt::Exists(path)) { HttpResponseHeader rhdr(404); setDefaultResponseHeaders(rhdr,"text/html",false); hdlr->send404(rhdr,path); return; } QFileInfo fi(path); QString ext = fi.extension(); // if it is the login page send that if (file == "/login.html" || file == "/") { session.logged_in = false; ext = "html"; path = rootDir + bt::DirSeparator() + WebInterfacePluginSettings::skin() + "/login.html"; } else if (!session.logged_in && (ext == "html" || ext == "php")) { // for any html or php page, a login is necessary redirectToLoginPage(hdlr); return; } else if (session.logged_in && !do_not_check_session && (ext == "html" || ext == "php")) { // if we are logged in and it's a html or php page, check the session id if (!checkSession(hdr)) { session.logged_in = false; // redirect to login page redirectToLoginPage(hdlr); return; } } if (ext == "html") { HttpResponseHeader rhdr(200); setDefaultResponseHeaders(rhdr,"text/html",true); if (path.endsWith("login.html")) { // clear cookie in case of login page QDateTime dt = QDateTime::currentDateTime().addDays(-1); QString cookie = QString("KT_SESSID=666; expires=%1 +0000").arg(DateTimeToString(dt,true)); rhdr.setValue("Set-Cookie",cookie); } if (!hdlr->sendFile(rhdr,path)) { HttpResponseHeader nhdr(404); setDefaultResponseHeaders(nhdr,"text/html",false); hdlr->send404(nhdr,path); } } else if (ext == "css" || ext == "js" || ext == "png" || ext == "ico" || ext == "gif" || ext == "jpg") { if (hdr.hasKey("If-Modified-Since")) { QDateTime dt = parseDate(hdr.value("If-Modified-Since")); if (dt.isValid() && dt < fi.lastModified()) { HttpResponseHeader rhdr(304); setDefaultResponseHeaders(rhdr,"text/html",true); rhdr.setValue("Cache-Control","max-age=0"); rhdr.setValue("Last-Modified",DateTimeToString(fi.lastModified(),false)); rhdr.setValue("Expires",DateTimeToString(QDateTime::currentDateTime(Qt::UTC).addSecs(3600),false)); hdlr->sendResponse(rhdr); return; } } HttpResponseHeader rhdr(200); setDefaultResponseHeaders(rhdr,ExtensionToContentType(ext),true); rhdr.setValue("Last-Modified",DateTimeToString(fi.lastModified(),false)); rhdr.setValue("Expires",DateTimeToString(QDateTime::currentDateTime(Qt::UTC).addSecs(3600),false)); rhdr.setValue("Cache-Control","private"); if (!hdlr->sendFile(rhdr,path)) { HttpResponseHeader nhdr(404); setDefaultResponseHeaders(nhdr,"text/html",false); hdlr->send404(nhdr,path); } } else if (ext == "php") { bool redirect = false; bool shutdown = false; if (url.queryItems().count() > 0 && session.logged_in) redirect = php_i->exec(url,shutdown); if (shutdown) { // first send back login page redirectToLoginPage(hdlr); QTimer::singleShot(1000,kapp,SLOT(quit())); } else if (redirect) { HttpResponseHeader rhdr(301); setDefaultResponseHeaders(rhdr,"text/html",true); rhdr.setValue("Location",url.encodedPathAndQuery()); hdlr->executePHPScript(php_i,rhdr,WebInterfacePluginSettings::phpExecutablePath(), path,url.queryItems()); } else { HttpResponseHeader rhdr(200); setDefaultResponseHeaders(rhdr,"text/html",true); hdlr->executePHPScript(php_i,rhdr,WebInterfacePluginSettings::phpExecutablePath(), path,url.queryItems()); } } else { HttpResponseHeader rhdr(404); setDefaultResponseHeaders(rhdr,"text/html",false); hdlr->send404(rhdr,path); } }
/*! * Handles incoming HTTP requests and dispatches them to the appropriate service. * * The \a requestID is an opaque value generated by the connector. * * Subclasses may override this function to perform preprocessing on each * request, but they must call the base class implementation in order to * generate and dispatch the appropriate events. * * To facilitate use with multi-threaded applications, the event will remain * valid until a response is posted. */ void QxtHttpSessionManager::incomingRequest(quint32 requestID, const QHttpRequestHeader& header, QxtWebContent* content) { QMultiHash<QString, QString> cookies; foreach(const QString& cookie, header.allValues("cookie")) // QHttpHeader is case-insensitive, thankfully { foreach(const QString& kv, cookie.split("; ")) { int pos = kv.indexOf('='); if (pos == -1) continue; cookies.insert(kv.left(pos), kv.mid(pos + 1)); } } int sessionID; QString sessionCookie = cookies.value(qxt_d().sessionCookieName); qxt_d().sessionLock.lock(); if (qxt_d().sessionKeys.contains(sessionCookie)) { sessionID = qxt_d().sessionKeys[sessionCookie]; if(!sessionID && header.majorVersion() > 0 && qxt_d().autoCreateSession) sessionID = newSession(); } else if (header.majorVersion() > 0 && qxt_d().autoCreateSession) { sessionID = newSession(); } else { sessionID = 0; } QIODevice* device = connector()->getRequestConnection(requestID); QxtHttpSessionManagerPrivate::ConnectionState& state = qxt_d().connectionState[device]; state.sessionID = sessionID; state.httpMajorVersion = header.majorVersion(); state.httpMinorVersion = header.minorVersion(); if (state.httpMajorVersion == 0 || (state.httpMajorVersion == 1 && state.httpMinorVersion == 0) || header.value("connection").toLower() == "close") state.keepAlive = false; else state.keepAlive = true; qxt_d().sessionLock.unlock(); QxtWebRequestEvent* event = new QxtWebRequestEvent(sessionID, requestID, QUrl::fromEncoded(header.path().toUtf8())); qxt_d().eventLock.lock(); qxt_d().pendingRequests.insert(QPair<int,int>(sessionID, requestID), event); qxt_d().eventLock.unlock(); QTcpSocket* socket = qobject_cast<QTcpSocket*>(device); if (socket) { event->remoteAddress = socket->peerAddress(); #if defined(QT_SECURETRANSPORT) || !defined(QT_NO_OPENSSL) QSslSocket* sslSocket = qobject_cast<QSslSocket*>(socket); if(sslSocket) { event->isSecure = true; event->clientCertificate = sslSocket->peerCertificate(); } #endif } event->method = header.method(); event->cookies = cookies; event->url.setScheme("http"); if (event->url.host().isEmpty()) event->url.setHost(header.value("host")); if (event->url.port() == -1) event->url.setPort(port()); event->contentType = header.contentType(); event->content = content; typedef QPair<QString, QString> StringPair; foreach(const StringPair& line, header.values()) { if (line.first.toLower() == "cookie") continue; event->headers.insert(line.first, line.second); } event->headers.insert("X-Request-Protocol", "HTTP/" + QString::number(state.httpMajorVersion) + '.' + QString::number(state.httpMinorVersion)); if (sessionID && session(sessionID)) { QxtAbstractWebService *service = session(sessionID); if(content) content->setParent(service); // Set content ownership to the service service->pageRequestedEvent(event); } else if (qxt_d().staticService) { qxt_d().staticService->pageRequestedEvent(event); } else { postEvent(new QxtWebErrorEvent(0, requestID, 500, "Internal Configuration Error")); } }
void InspectorServerRequestHandlerQt::tcpReadyRead() { QHttpRequestHeader header; bool isWebSocket = false; if (!m_tcpConnection) return; if (!m_endOfHeaders) { while (m_tcpConnection->bytesAvailable() && !m_endOfHeaders) { QByteArray line = m_tcpConnection->readLine(); m_data.append(line); if (line == "\r\n") m_endOfHeaders = true; } if (m_endOfHeaders) { header = QHttpRequestHeader(QString::fromLatin1(m_data)); if (header.isValid()) { m_path = header.path(); m_contentType = header.contentType().toLatin1(); m_contentLength = header.contentLength(); if (header.hasKey("Upgrade") && (header.value("Upgrade") == QLatin1String("WebSocket"))) isWebSocket = true; m_data.clear(); } } } if (m_endOfHeaders) { QStringList pathAndQuery = m_path.split("?"); m_path = pathAndQuery[0]; QStringList words = m_path.split(QString::fromLatin1("/")); if (isWebSocket) { // switch to websocket-style WebSocketService messaging if (m_tcpConnection) { m_tcpConnection->disconnect(SIGNAL(readyRead())); connect(m_tcpConnection, SIGNAL(readyRead()), SLOT(webSocketReadyRead())); QByteArray key3 = m_tcpConnection->read(8); quint32 number1 = parseWebSocketChallengeNumber(header.value("Sec-WebSocket-Key1")); quint32 number2 = parseWebSocketChallengeNumber(header.value("Sec-WebSocket-Key2")); char responseData[16]; generateWebSocketChallengeResponse(number1, number2, (unsigned char*)key3.data(), (unsigned char*)responseData); QByteArray response(responseData, sizeof(responseData)); QHttpResponseHeader responseHeader(101, "WebSocket Protocol Handshake", 1, 1); responseHeader.setValue("Upgrade", header.value("Upgrade")); responseHeader.setValue("Connection", header.value("Connection")); responseHeader.setValue("Sec-WebSocket-Origin", header.value("Origin")); responseHeader.setValue("Sec-WebSocket-Location", ("ws://" + header.value("Host") + m_path)); responseHeader.setContentLength(response.size()); m_tcpConnection->write(responseHeader.toString().toLatin1()); m_tcpConnection->write(response); m_tcpConnection->flush(); if ((words.size() == 4) && (words[1] == QString::fromLatin1("devtools")) && (words[2] == QString::fromLatin1("page"))) { int pageNum = words[3].toInt(); m_inspectorClient = m_server->inspectorClientForPage(pageNum); // Attach remoteFrontendChannel to inspector, also transferring ownership. if (m_inspectorClient) m_inspectorClient->attachAndReplaceRemoteFrontend(new RemoteFrontendChannel(this)); } } return; } if (m_contentLength && (m_tcpConnection->bytesAvailable() < m_contentLength)) return; QByteArray content = m_tcpConnection->read(m_contentLength); m_endOfHeaders = false; QByteArray response; int code = 200; QString text = QString::fromLatin1("OK"); // If no path is specified, generate an index page. if ((m_path == "") || (m_path == "/")) { QString indexHtml = "<html><head><title>Remote Web Inspector</title></head><body><ul>\n"; for (QMap<int, InspectorClientQt* >::const_iterator it = m_server->m_inspectorClients.begin(); it != m_server->m_inspectorClients.end(); ++it) { indexHtml.append(QString("<li><a href=\"/webkit/inspector/inspector.html?page=%1\">%2</li>\n") .arg(it.key()) .arg(it.value()->m_inspectedWebPage->mainFrame()->url().toString())); } indexHtml.append("</ul></body></html>"); response = indexHtml.toLatin1(); } else { QString path = QString(":%1").arg(m_path); QFile file(path); // It seems that there should be an enum or define for these status codes somewhere in Qt or WebKit, // but grep fails to turn one up. // QNetwork uses the numeric values directly. if (file.exists()) { file.open(QIODevice::ReadOnly); response = file.readAll(); } else { code = 404; text = QString::fromLatin1("Not OK"); } } QHttpResponseHeader responseHeader(code, text, 1, 0); responseHeader.setContentLength(response.size()); if (!m_contentType.isEmpty()) responseHeader.setContentType(QString::fromLatin1(m_contentType)); QByteArray asciiHeader = responseHeader.toString().toAscii(); m_tcpConnection->write(asciiHeader); m_tcpConnection->write(response); m_tcpConnection->flush(); m_tcpConnection->close(); return; } }