void WebProcessor::SendErrorResponse(const char* errCode, bool printWarning) { const char* RESPONSE_HEADER = "HTTP/1.0 %s\r\n" "Connection: close\r\n" "Content-Length: %i\r\n" "Content-Type: text/html\r\n" "Server: nzbget-%s\r\n" "\r\n"; if (printWarning) { warn("Web-Server: %s, Resource: %s", errCode, *m_url); } BString<1024> responseBody("<html><head><title>%s</title></head><body>Error: %s</body></html>", errCode, errCode); int pageContentLen = responseBody.Length(); BString<1024> responseHeader(RESPONSE_HEADER, errCode, pageContentLen, Util::VersionRevision()); // Send the response answer m_connection->Send(responseHeader, responseHeader.Length()); m_connection->Send(responseBody, pageContentLen); }
void WebProcessor::SendBodyResponse(const char* body, int bodyLen, const char* contentType) { const char* RESPONSE_HEADER = "HTTP/1.1 200 OK\r\n" "Connection: close\r\n" "Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n" "Access-Control-Allow-Origin: %s\r\n" "Access-Control-Allow-Credentials: true\r\n" "Access-Control-Max-Age: 86400\r\n" "Access-Control-Allow-Headers: Content-Type, Authorization\r\n" "X-Auth-Token: %s\r\n" "Content-Length: %i\r\n" "%s" // Content-Type: xxx "%s" // Content-Encoding: gzip "Server: nzbget-%s\r\n" "\r\n"; #ifndef DISABLE_GZIP CharBuffer gbuf; bool gzip = m_gzip && bodyLen > MAX_UNCOMPRESSED_SIZE; if (gzip) { uint32 outLen = ZLib::GZipLen(bodyLen); gbuf.Reserve(outLen); int gzippedLen = ZLib::GZip(body, bodyLen, *gbuf, outLen); if (gzippedLen > 0 && gzippedLen < bodyLen) { body = gbuf; bodyLen = gzippedLen; } else { gzip = false; } } #else bool gzip = false; #endif BString<1024> contentTypeHeader; if (contentType) { contentTypeHeader.Format("Content-Type: %s\r\n", contentType); } BString<1024> responseHeader(RESPONSE_HEADER, m_origin.Str(), m_serverAuthToken[m_userAccess], bodyLen, *contentTypeHeader, gzip ? "Content-Encoding: gzip\r\n" : "", Util::VersionRevision()); // Send the request answer m_connection->Send(responseHeader, responseHeader.Length()); m_connection->Send(body, bodyLen); }
void WebProcessor::SendRedirectResponse(const char* url) { const char* REDIRECT_RESPONSE_HEADER = "HTTP/1.0 301 Moved Permanently\r\n" "Location: %s\r\n" "Connection: close\r\n" "Server: nzbget-%s\r\n" "\r\n"; BString<1024> responseHeader(REDIRECT_RESPONSE_HEADER, url, Util::VersionRevision()); // Send the response answer debug("ResponseHeader=%s", *responseHeader); m_connection->Send(responseHeader, responseHeader.Length()); }
void WebProcessor::SendAuthResponse() { const char* AUTH_RESPONSE_HEADER = "HTTP/1.0 401 Unauthorized\r\n" "WWW-Authenticate: Basic realm=\"NZBGet\"\r\n" "Connection: close\r\n" "Content-Type: text/plain\r\n" "Server: nzbget-%s\r\n" "\r\n"; BString<1024> responseHeader(AUTH_RESPONSE_HEADER, Util::VersionRevision()); // Send the response answer debug("ResponseHeader=%s", *responseHeader); m_connection->Send(responseHeader, responseHeader.Length()); }
void WebProcessor::SendOptionsResponse() { const char* OPTIONS_RESPONSE_HEADER = "HTTP/1.1 200 OK\r\n" "Connection: close\r\n" //"Content-Type: plain/text\r\n" "Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n" "Access-Control-Allow-Origin: %s\r\n" "Access-Control-Allow-Credentials: true\r\n" "Access-Control-Max-Age: 86400\r\n" "Access-Control-Allow-Headers: Content-Type, Authorization\r\n" "Server: nzbget-%s\r\n" "\r\n"; BString<1024> responseHeader(OPTIONS_RESPONSE_HEADER, m_origin.Str(), Util::VersionRevision()); // Send the response answer debug("ResponseHeader=%s", *responseHeader); m_connection->Send(responseHeader, responseHeader.Length()); }
void QHttpSocketEngine::slotSocketReadNotification() { Q_D(QHttpSocketEngine); if (d->state != Connected && d->socket->bytesAvailable() == 0) return; if (d->state == Connected) { // Forward as a read notification. if (d->readNotificationEnabled) emitReadNotification(); return; } readResponseContent: if (d->state == ReadResponseContent) { char dummybuffer[4096]; while (d->pendingResponseData) { int read = d->socket->read(dummybuffer, qMin(sizeof(dummybuffer), (size_t)d->pendingResponseData)); if (read >= 0) dummybuffer[read] = 0; if (read == 0) return; if (read == -1) { d->socket->disconnectFromHost(); emitWriteNotification(); return; } d->pendingResponseData -= read; } if (d->pendingResponseData > 0) return; d->state = SendAuthentication; slotSocketConnected(); return; } // Still in handshake mode. Wait until we've got a full response. bool done = false; do { d->readBuffer += d->socket->readLine(); } while (!(done = d->readBuffer.endsWith("\r\n\r\n")) && d->socket->canReadLine()); if (!done) { // Wait for more. return; } if (!d->readBuffer.startsWith("HTTP/1.")) { // protocol error, this isn't HTTP d->readBuffer.clear(); d->socket->close(); setState(QAbstractSocket::UnconnectedState); setError(QAbstractSocket::ProxyProtocolError, tr("Did not receive HTTP response from proxy")); emitConnectionNotification(); return; } QHttpResponseHeader responseHeader(QString::fromLatin1(d->readBuffer)); d->readBuffer.clear(); // we parsed the proxy protocol response. from now on direct socket reading will be done int statusCode = responseHeader.statusCode(); QAuthenticatorPrivate *priv = 0; if (statusCode == 200) { d->state = Connected; setLocalAddress(d->socket->localAddress()); setLocalPort(d->socket->localPort()); setState(QAbstractSocket::ConnectedState); d->authenticator.detach(); priv = QAuthenticatorPrivate::getPrivate(d->authenticator); priv->hasFailed = false; } else if (statusCode == 407) { if (d->credentialsSent) { //407 response again means the provided username/password were invalid. d->authenticator = QAuthenticator(); //this is needed otherwise parseHttpResponse won't set the state, and then signal isn't emitted. d->authenticator.detach(); priv = QAuthenticatorPrivate::getPrivate(d->authenticator); priv->hasFailed = true; } else if (d->authenticator.isNull()) d->authenticator.detach(); priv = QAuthenticatorPrivate::getPrivate(d->authenticator); priv->parseHttpResponse(responseHeader, true); if (priv->phase == QAuthenticatorPrivate::Invalid) { // problem parsing the reply d->socket->close(); setState(QAbstractSocket::UnconnectedState); setError(QAbstractSocket::ProxyProtocolError, tr("Error parsing authentication request from proxy")); emitConnectionNotification(); return; } bool willClose; QString proxyConnectionHeader = responseHeader.value(QLatin1String("Proxy-Connection")); // Although most proxies use the unofficial Proxy-Connection header, the Connection header // from http spec is also allowed. if (proxyConnectionHeader.isEmpty()) proxyConnectionHeader = responseHeader.value(QLatin1String("Connection")); proxyConnectionHeader = proxyConnectionHeader.toLower(); if (proxyConnectionHeader == QLatin1String("close")) { willClose = true; } else if (proxyConnectionHeader == QLatin1String("keep-alive")) { willClose = false; } else { // no Proxy-Connection header, so use the default // HTTP 1.1's default behaviour is to keep persistent connections // HTTP 1.0 or earlier, so we expect the server to close willClose = (responseHeader.majorVersion() * 0x100 + responseHeader.minorVersion()) <= 0x0100; } if (willClose) { // the server will disconnect, so let's avoid receiving an error // especially since the signal below may trigger a new event loop d->socket->disconnectFromHost(); d->socket->readAll(); } if (priv->phase == QAuthenticatorPrivate::Done) emit proxyAuthenticationRequired(d->proxy, &d->authenticator); // priv->phase will get reset to QAuthenticatorPrivate::Start if the authenticator got modified in the signal above. if (priv->phase == QAuthenticatorPrivate::Done) { setError(QAbstractSocket::ProxyAuthenticationRequiredError, tr("Authentication required")); d->socket->disconnectFromHost(); } else { // close the connection if it isn't already and reconnect using the chosen authentication method d->state = SendAuthentication; if (willClose) { d->socket->connectToHost(d->proxy.hostName(), d->proxy.port()); } else { bool ok; int contentLength = responseHeader.value(QLatin1String("Content-Length")).toInt(&ok); if (ok && contentLength > 0) { d->state = ReadResponseContent; d->pendingResponseData = contentLength; goto readResponseContent; } else { d->state = SendAuthentication; slotSocketConnected(); } } return; } } else { d->socket->close(); setState(QAbstractSocket::UnconnectedState); if (statusCode == 403 || statusCode == 405) { // 403 Forbidden // 405 Method Not Allowed setError(QAbstractSocket::SocketAccessError, tr("Proxy denied connection")); } else if (statusCode == 404) { // 404 Not Found: host lookup error setError(QAbstractSocket::HostNotFoundError, QAbstractSocket::tr("Host not found")); } else if (statusCode == 503) { // 503 Service Unavailable: Connection Refused setError(QAbstractSocket::ConnectionRefusedError, QAbstractSocket::tr("Connection refused")); } else { // Some other reply //qWarning("UNEXPECTED RESPONSE: [%s]", responseHeader.toString().toLatin1().data()); setError(QAbstractSocket::ProxyProtocolError, tr("Error communicating with HTTP proxy")); } } // The handshake is done; notify that we're connected (or failed to connect) emitConnectionNotification(); }
void QHttpSocketEngine::slotSocketReadNotification() { Q_D(QHttpSocketEngine); if (d->state != Connected && d->socket->bytesAvailable() == 0) return; if (d->state == Connected) { // Forward as a read notification. if (d->readNotificationEnabled) emitReadNotification(); return; } readResponseContent: if (d->state == ReadResponseContent) { char dummybuffer[4096]; while (d->pendingResponseData) { int read = d->socket->read(dummybuffer, qMin(sizeof(dummybuffer), (size_t)d->pendingResponseData)); dummybuffer[read] = 0; if (read == 0) return; if (read == -1) { d->socket->disconnectFromHost(); emitWriteNotification(); return; } d->pendingResponseData -= read; } if (d->pendingResponseData > 0) return; d->state = SendAuthentication; slotSocketConnected(); return; } // Still in handshake mode. Wait until we've got a full response. bool done = false; do { d->readBuffer += d->socket->readLine(); } while (!(done = d->readBuffer.endsWith("\r\n\r\n")) && d->socket->canReadLine()); if (!done) { // Wait for more. return; } QHttpResponseHeader responseHeader(QString::fromLatin1(d->readBuffer)); d->readBuffer.clear(); int statusCode = responseHeader.statusCode(); if (statusCode == 200) { d->state = Connected; } else if (statusCode == 503) { // 503 Service Unavailable: Connection Refused d->socket->close(); setState(QAbstractSocket::UnconnectedState); setError(QAbstractSocket::ConnectionRefusedError, QAbstractSocket::tr("Connection refused")); } else if (statusCode == 407) { if (d->authenticator.isNull()) d->authenticator.detach(); QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(d->authenticator); priv->parseHttpResponse(responseHeader, true); if (priv->phase == QAuthenticatorPrivate::Done) emit proxyAuthenticationRequired(d->proxy, &d->authenticator); // priv->phase will get reset to QAuthenticatorPrivate::Start if the authenticator got modified in the signal above. if (priv->phase == QAuthenticatorPrivate::Done) { setError(QAbstractSocket::ProxyAuthenticationRequiredError, tr("Authentication required")); d->socket->disconnectFromHost(); } else { // close the connection if it isn't already and reconnect using the chose authentication method d->state = SendAuthentication; bool willClose = (responseHeader.value(QLatin1String("Proxy-Connection")).toLower() == QLatin1String("close")); if (willClose) { d->socket->disconnectFromHost(); d->socket->readAll(); d->socket->connectToHost(d->proxy.hostName(), d->proxy.port()); } else { bool ok; int contentLength = responseHeader.value(QLatin1String("Content-Length")).toInt(&ok); if (ok && contentLength > 0) { d->state = ReadResponseContent; d->pendingResponseData = contentLength; goto readResponseContent; } else { d->state = SendAuthentication; slotSocketConnected(); } } return; } } else { qWarning("UNEXPECTED RESPONSE: [%s]", responseHeader.toString().toLatin1().data()); d->socket->disconnectFromHost(); } // The handshake is done; request a new connection attempt by sending a write // notification. emitWriteNotification(); }
void InspectorServerRequestHandlerQt::tcpReadyRead() { WebKit::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 = WebKit::QHttpRequestHeader(QString::fromLatin1(m_data)); if (header.isValid()) { m_path = header.path(); m_contentType = header.contentType().toLatin1(); m_contentLength = header.contentLength(); if (header.hasKey(QLatin1String("Upgrade")) && (header.value(QLatin1String("Upgrade")) == QLatin1String("WebSocket"))) isWebSocket = true; m_data.clear(); } } } if (m_endOfHeaders) { QStringList pathAndQuery = m_path.split(QLatin1Char('?')); m_path = pathAndQuery[0]; QStringList words = m_path.split(QLatin1Char('/')); if (isWebSocket) { // switch to websocket-style WebSocketService messaging if (m_tcpConnection) { m_tcpConnection->disconnect(SIGNAL(readyRead())); connect(m_tcpConnection, SIGNAL(readyRead()), SLOT(webSocketReadyRead()), Qt::QueuedConnection); QByteArray key3 = m_tcpConnection->read(8); quint32 number1 = parseWebSocketChallengeNumber(header.value(QLatin1String("Sec-WebSocket-Key1"))); quint32 number2 = parseWebSocketChallengeNumber(header.value(QLatin1String("Sec-WebSocket-Key2"))); char responseData[16]; generateWebSocketChallengeResponse(number1, number2, (unsigned char*)key3.data(), (unsigned char*)responseData); QByteArray response(responseData, sizeof(responseData)); WebKit::QHttpResponseHeader responseHeader(101, QLatin1String("WebSocket Protocol Handshake"), 1, 1); responseHeader.setValue(QLatin1String("Upgrade"), header.value(QLatin1String("Upgrade"))); responseHeader.setValue(QLatin1String("Connection"), header.value(QLatin1String("Connection"))); responseHeader.setValue(QLatin1String("Sec-WebSocket-Origin"), header.value(QLatin1String("Origin"))); responseHeader.setValue(QLatin1String("Sec-WebSocket-Location"), (QLatin1String("ws://") + header.value(QLatin1String("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(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.isEmpty() || (m_path == QString(QLatin1Char('/')))) { QString indexHtml = QLatin1String("<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::fromLatin1("<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(QLatin1String("</ul></body></html>")); response = indexHtml.toLatin1(); } else { QString path = QString::fromLatin1(":%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"); } } WebKit::QHttpResponseHeader responseHeader(code, text, 1, 0); responseHeader.setContentLength(response.size()); if (!m_contentType.isEmpty()) responseHeader.setContentType(QString::fromLatin1(m_contentType)); QByteArray asciiHeader = responseHeader.toString().toLatin1(); m_tcpConnection->write(asciiHeader); m_tcpConnection->write(response); m_tcpConnection->flush(); m_tcpConnection->close(); return; } }