void QHttpSocketEngine::slotSocketConnected() { Q_D(QHttpSocketEngine); // Send the greeting. const char method[] = "CONNECT"; QByteArray peerAddress = d->peerAddress.toString().toLatin1(); QByteArray path = peerAddress + ':' + QByteArray::number(d->peerPort); QByteArray data = method; data += " "; data += path; data += " HTTP/1.1\r\n"; data += "Proxy-Connection: keep-alive\r\n" "Host: " + peerAddress + "\r\n"; QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(d->authenticator); //qDebug() << "slotSocketConnected: priv=" << priv << (priv ? (int)priv->method : -1); if (priv && priv->method != QAuthenticatorPrivate::None) { data += "Proxy-Authorization: " + priv->calculateResponse(method, path); data += "\r\n"; } data += "\r\n"; // qDebug() << ">>>>>>>> sending request" << this; // qDebug() << data; // qDebug() << ">>>>>>>"; d->socket->write(data); d->state = ConnectSent; }
void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request) { Q_ASSERT(socket); int i = indexOf(socket); // Send "Authorization" header, but not if it's NTLM and the socket is already authenticated. if (channels[i].authMethod != QAuthenticatorPrivate::None) { if (!(channels[i].authMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 401)) { QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator); if (priv && priv->method != QAuthenticatorPrivate::None) { QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false)); request.setHeaderField("Authorization", response); channels[i].authenticationCredentialsSent = true; } } } // Send "Proxy-Authorization" header, but not if it's NTLM and the socket is already authenticated. if (channels[i].proxyAuthMethod != QAuthenticatorPrivate::None) { if (!(channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) { QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator); if (priv && priv->method != QAuthenticatorPrivate::None) { QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false)); request.setHeaderField("Proxy-Authorization", response); channels[i].proxyCredentialsSent = true; } } } }
void QHttpSocketEngine::slotSocketConnected() { Q_D(QHttpSocketEngine); // Send the greeting. const char method[] = "CONNECT "; QByteArray peerAddress = d->peerName.isEmpty() ? d->peerAddress.toString().toLatin1() : QUrl::toAce(d->peerName); QByteArray path = peerAddress + ':' + QByteArray::number(d->peerPort); QByteArray data = method; data += path; data += " HTTP/1.1\r\n"; data += "Proxy-Connection: keep-alive\r\n" "User-Agent: "; QVariant v = property("_q_user-agent"); if (v.isValid()) data += v.toByteArray(); else data += "Mozilla/5.0"; data += "\r\n" "Host: " + peerAddress + "\r\n"; QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(d->authenticator); //qDebug() << "slotSocketConnected: priv=" << priv << (priv ? (int)priv->method : -1); if (priv && priv->method != QAuthenticatorPrivate::None) { data += "Proxy-Authorization: " + priv->calculateResponse(method, path); data += "\r\n"; } data += "\r\n"; // qDebug() << ">>>>>>>> sending request" << this; // qDebug() << data; // qDebug() << ">>>>>>>"; d->socket->write(data); d->state = ConnectSent; }
void QHttpSocketEngine::slotSocketConnected() { Q_D(QHttpSocketEngine); // Send the greeting. const char method[] = "CONNECT"; QByteArray peerAddress = d->peerName.isEmpty() ? d->peerAddress.toString().toLatin1() : QUrl::toAce(d->peerName); QByteArray path = peerAddress + ':' + QByteArray::number(d->peerPort); QByteArray data = method; data += " "; data += path; data += " HTTP/1.1\r\n"; data += "Proxy-Connection: keep-alive\r\n"; data += "Host: " + peerAddress + "\r\n"; if (!d->proxy.hasRawHeader("User-Agent")) data += "User-Agent: Mozilla/5.0\r\n"; foreach (const QByteArray &header, d->proxy.rawHeaderList()) { data += header + ": " + d->proxy.rawHeader(header) + "\r\n"; } QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(d->authenticator); //qDebug() << "slotSocketConnected: priv=" << priv << (priv ? (int)priv->method : -1); if (priv && priv->method != QAuthenticatorPrivate::None) { d->credentialsSent = true; data += "Proxy-Authorization: " + priv->calculateResponse(method, path); data += "\r\n"; } data += "\r\n"; // qDebug() << ">>>>>>>> sending request" << this; // qDebug() << data; // qDebug() << ">>>>>>>"; d->socket->write(data); d->state = ConnectSent; }
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(); }
// handles the authentication for one channel and eventually re-starts the other channels bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend) { Q_ASSERT(socket); Q_ASSERT(reply); resend = false; //create the response header to be used with QAuthenticatorPrivate. QList<QPair<QByteArray, QByteArray> > fields = reply->header(); //find out the type of authentication protocol requested. QAuthenticatorPrivate::Method authMethod = reply->d_func()->authenticationMethod(isProxy); if (authMethod != QAuthenticatorPrivate::None) { int i = indexOf(socket); //Use a single authenticator for all domains. ### change later to use domain/realm QAuthenticator* auth = 0; if (isProxy) { auth = &channels[i].proxyAuthenticator; channels[i].proxyAuthMethod = authMethod; } else { auth = &channels[i].authenticator; channels[i].authMethod = authMethod; } //proceed with the authentication. if (auth->isNull()) auth->detach(); QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth); priv->parseHttpResponse(fields, isProxy); if (priv->phase == QAuthenticatorPrivate::Done) { pauseConnection(); if (!isProxy) { if (channels[i].authenticationCredentialsSent) { auth->detach(); priv = QAuthenticatorPrivate::getPrivate(*auth); priv->hasFailed = true; priv->phase = QAuthenticatorPrivate::Done; channels[i].authenticationCredentialsSent = false; } emit reply->authenticationRequired(reply->request(), auth); #ifndef QT_NO_NETWORKPROXY } else { if (channels[i].proxyCredentialsSent) { auth->detach(); priv = QAuthenticatorPrivate::getPrivate(*auth); priv->hasFailed = true; priv->phase = QAuthenticatorPrivate::Done; channels[i].proxyCredentialsSent = false; } emit reply->proxyAuthenticationRequired(networkProxy, auth); #endif } resumeConnection(); if (priv->phase != QAuthenticatorPrivate::Done) { // send any pending requests copyCredentials(i, auth, isProxy); } } else if (priv->phase == QAuthenticatorPrivate::Start) { // If the url's authenticator has a 'user' set we will end up here (phase is only set to 'Done' by // parseHttpResponse above if 'user' is empty). So if credentials were supplied with the request, // such as in the case of an XMLHttpRequest, this is our only opportunity to cache them. emit reply->cacheCredentials(reply->request(), auth); } // - Changing values in QAuthenticator will reset the 'phase'. Therefore if it is still "Done" // then nothing was filled in by the user or the cache // - If withCredentials has been set to false (e.g. by QtWebKit for a cross-origin XMLHttpRequest) then // we need to bail out if authentication is required. if (priv->phase == QAuthenticatorPrivate::Done || !reply->request().withCredentials()) { // Reset authenticator so the next request on that channel does not get messed up auth = 0; if (isProxy) channels[i].proxyAuthenticator = QAuthenticator(); else channels[i].authenticator = QAuthenticator(); // authentication is cancelled, send the current contents to the user. emit channels[i].reply->headerChanged(); emit channels[i].reply->readyRead(); QNetworkReply::NetworkError errorCode = isProxy ? QNetworkReply::ProxyAuthenticationRequiredError : QNetworkReply::AuthenticationRequiredError; reply->d_func()->errorString = errorDetail(errorCode, socket); emit reply->finishedWithError(errorCode, reply->d_func()->errorString); // ### at this point the reply could be deleted return true; } //resend the request resend = true; return true; } return false; }
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 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; } if (d->state == ConnectSent) { d->reply->d_func()->state = QHttpNetworkReplyPrivate::NothingDoneState; d->state = ReadResponseHeader; } if (d->state == ReadResponseHeader) { bool ok = readHttpHeader(); if (!ok) { // protocol error, this isn't HTTP d->socket->close(); setState(QAbstractSocket::UnconnectedState); setError(QAbstractSocket::ProxyProtocolError, tr("Did not receive HTTP response from proxy")); emitConnectionNotification(); return; } if (d->state == ReadResponseHeader) return; // readHttpHeader() was not done yet, need to wait for more header data } 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) return; if (read == -1) { d->socket->disconnectFromHost(); emitWriteNotification(); return; } d->pendingResponseData -= read; } if (d->pendingResponseData > 0) return; if (d->reply->d_func()->statusCode == 407) d->state = SendAuthentication; } int statusCode = d->reply->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->authenticator.isNull()) d->authenticator.detach(); priv = QAuthenticatorPrivate::getPrivate(d->authenticator); if (d->credentialsSent && priv->phase != QAuthenticatorPrivate::Phase2) { // Remember that (e.g.) NTLM is two-phase, so only reset when the authentication is not currently in progress. //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; } priv->parseHttpResponse(d->reply->header(), 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; QByteArray proxyConnectionHeader = d->reply->headerField("Proxy-Connection"); // Although most proxies use the unofficial Proxy-Connection header, the Connection header // from http spec is also allowed. if (proxyConnectionHeader.isEmpty()) proxyConnectionHeader = d->reply->headerField("Connection"); proxyConnectionHeader = proxyConnectionHeader.toLower(); if (proxyConnectionHeader == "close") { willClose = true; } else if (proxyConnectionHeader == "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 = (d->reply->majorVersion() * 0x100 + d->reply->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(); //We're done with the reply and need to reset it for the next connection delete d->reply; d->reply = new QHttpNetworkReply; } 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 { // send the HTTP CONNECT again 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(); }