status_t BHttpRequest::_ProtocolLoop() { // Initialize the request redirection loop int8 maxRedirs = fOptMaxRedirs; bool newRequest; do { newRequest = false; // Result reset fHeaders.Clear(); _ResultHeaders().Clear(); BString host = fUrl.Host(); int port = fSSL ? 443 : 80; if (fUrl.HasPort()) port = fUrl.Port(); if (fContext->UseProxy()) { host = fContext->GetProxyHost(); port = fContext->GetProxyPort(); } status_t result = fInputBuffer.InitCheck(); if (result != B_OK) return result; if (!_ResolveHostName(host, port)) { _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, "Unable to resolve hostname (%s), aborting.", fUrl.Host().String()); return B_SERVER_NOT_FOUND; } status_t requestStatus = _MakeRequest(); if (requestStatus != B_OK) return requestStatus; // Prepare the referer for the next request if needed if (fOptAutoReferer) fOptReferer = fUrl.UrlString(); switch (StatusCodeClass(fResult.StatusCode())) { case B_HTTP_STATUS_CLASS_INFORMATIONAL: // Header 100:continue should have been // handled in the _MakeRequest read loop break; case B_HTTP_STATUS_CLASS_SUCCESS: break; case B_HTTP_STATUS_CLASS_REDIRECTION: { // Redirection has been explicitly disabled if (!fOptFollowLocation) break; int code = fResult.StatusCode(); if (code == B_HTTP_STATUS_MOVED_PERMANENTLY || code == B_HTTP_STATUS_FOUND || code == B_HTTP_STATUS_SEE_OTHER || code == B_HTTP_STATUS_TEMPORARY_REDIRECT) { BString locationUrl = fHeaders["Location"]; fUrl = BUrl(fUrl, locationUrl); // 302 and 303 redirections also convert POST requests to GET // (and remove the posted form data) if ((code == B_HTTP_STATUS_FOUND || code == B_HTTP_STATUS_SEE_OTHER) && fRequestMethod == B_HTTP_POST) { SetMethod(B_HTTP_GET); delete fOptPostFields; fOptPostFields = NULL; delete fOptInputData; fOptInputData = NULL; fOptInputDataSize = 0; } if (--maxRedirs > 0) { newRequest = true; // Redirections may need a switch from http to https. if (fUrl.Protocol() == "https") fSSL = true; else if (fUrl.Protocol() == "http") fSSL = false; _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Following: %s\n", fUrl.UrlString().String()); } } break; } case B_HTTP_STATUS_CLASS_CLIENT_ERROR: if (fResult.StatusCode() == B_HTTP_STATUS_UNAUTHORIZED) { BHttpAuthentication* authentication = &fContext->GetAuthentication(fUrl); status_t status = B_OK; if (authentication->Method() == B_HTTP_AUTHENTICATION_NONE) { // There is no authentication context for this // url yet, so let's create one. BHttpAuthentication newAuth; newAuth.Initialize(fHeaders["WWW-Authenticate"]); fContext->AddAuthentication(fUrl, newAuth); // Get the copy of the authentication we just added. // That copy is owned by the BUrlContext and won't be // deleted (unlike the temporary object above) authentication = &fContext->GetAuthentication(fUrl); } newRequest = false; if (fOptUsername.Length() > 0 && status == B_OK) { // If we received an username and password, add them // to the request. This will either change the // credentials for an existing request, or set them // for a new one we created just above. // // If this request handles HTTP redirections, it will // also automatically retry connecting and send the // login information. authentication->SetUserName(fOptUsername); authentication->SetPassword(fOptPassword); newRequest = true; } } break; case B_HTTP_STATUS_CLASS_SERVER_ERROR: break; default: case B_HTTP_STATUS_CLASS_INVALID: break; } } while (newRequest); _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "%ld headers and %ld bytes of data remaining", fHeaders.CountHeaders(), fInputBuffer.Size()); if (fResult.StatusCode() == 404) return B_RESOURCE_NOT_FOUND; return B_OK; }
status_t BHttpRequest::_ProtocolLoop() { // Initialize the request redirection loop int8 maxRedirs = fOptMaxRedirs; bool newRequest; do { newRequest = false; // Result reset fOutputHeaders.Clear(); fHeaders.Clear(); _ResultHeaders().Clear(); if (!_ResolveHostName()) { _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, "Unable to resolve hostname (%s), aborting.", fUrl.Host().String()); return B_SERVER_NOT_FOUND; } status_t requestStatus = _MakeRequest(); if (requestStatus != B_OK) return requestStatus; // Prepare the referer for the next request if needed if (fOptAutoReferer) fOptReferer = fUrl.UrlString(); switch (StatusCodeClass(fResult.StatusCode())) { case B_HTTP_STATUS_CLASS_INFORMATIONAL: // Header 100:continue should have been // handled in the _MakeRequest read loop break; case B_HTTP_STATUS_CLASS_SUCCESS: break; case B_HTTP_STATUS_CLASS_REDIRECTION: // Redirection has been explicitly disabled if (!fOptFollowLocation) break; // TODO: Some browsers seems to translate POST requests to // GET when following a 302 redirection if (fResult.StatusCode() == B_HTTP_STATUS_MOVED_PERMANENTLY) { BString locationUrl = fHeaders["Location"]; fUrl = BUrl(fUrl, locationUrl); if (--maxRedirs > 0) { newRequest = true; _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Following: %s\n", fUrl.UrlString().String()); } } break; case B_HTTP_STATUS_CLASS_CLIENT_ERROR: if (fResult.StatusCode() == B_HTTP_STATUS_UNAUTHORIZED) { BHttpAuthentication* authentication = &fContext->GetAuthentication(fUrl); status_t status = B_OK; if (authentication->Method() == B_HTTP_AUTHENTICATION_NONE) { // There is no authentication context for this // url yet, so let's create one. authentication = new(std::nothrow) BHttpAuthentication(); if (authentication == NULL) status = B_NO_MEMORY; else { status = authentication->Initialize( fHeaders["WWW-Authenticate"]); fContext->AddAuthentication(fUrl, authentication); } } newRequest = false; if (fOptUsername.Length() > 0 && status == B_OK) { // If we received an username and password, add them // to the request. This will either change the // credentials for an existing request, or set them // for a new one we created just above. // // If this request handles HTTP redirections, it will // also automatically retry connecting and send the // login information. authentication->SetUserName(fOptUsername); authentication->SetPassword(fOptPassword); newRequest = true; } } break; case B_HTTP_STATUS_CLASS_SERVER_ERROR: break; default: case B_HTTP_STATUS_CLASS_INVALID: break; } } while (newRequest); _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "%ld headers and %ld bytes of data remaining", fHeaders.CountHeaders(), fInputBuffer.Size()); if (fResult.StatusCode() == 404) return B_RESOURCE_NOT_FOUND; return B_OK; }