Пример #1
0
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;
}
Пример #2
0
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;
}
Пример #3
0
status_t
BGopherRequest::_ProtocolLoop()
{
    if (fSocket == NULL)
        return B_NO_MEMORY;

    if (!_ResolveHostName(fUrl.Host(), fUrl.HasPort() ? fUrl.Port() : 70)) {
        _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR,
                   "Unable to resolve hostname (%s), aborting.",
                   fUrl.Host().String());
        return B_SERVER_NOT_FOUND;
    }

    _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Connection to %s on port %d.",
               fUrl.Authority().String(), fRemoteAddr.Port());
    status_t connectError = fSocket->Connect(fRemoteAddr);

    if (connectError != B_OK) {
        _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, "Socket connection error %s",
                   strerror(connectError));
        return connectError;
    }

    //! ProtocolHook:ConnectionOpened
    if (fListener != NULL)
        fListener->ConnectionOpened(this);

    _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT,
               "Connection opened, sending request.");

    _SendRequest();
    _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Request sent.");

    // Receive loop
    bool receiveEnd = false;
    status_t readError = B_OK;
    ssize_t bytesRead = 0;
    //ssize_t bytesReceived = 0;
    //ssize_t bytesTotal = 0;
    bool dataValidated = false;
    BStackOrHeapArray<char, 4096> chunk(kGopherBufferSize);

    while (!fQuit && !receiveEnd) {
        fSocket->WaitForReadable();
        bytesRead = fSocket->Read(chunk, kGopherBufferSize);

        if (bytesRead < 0) {
            readError = bytesRead;
            break;
        } else if (bytesRead == 0)
            receiveEnd = true;

        fInputBuffer.AppendData(chunk, bytesRead);

        if (!dataValidated) {
            size_t i;
            // on error (file doesn't exist, ...) the server sends
            // a faked directory entry with an error message
            if (fInputBuffer.Size() && fInputBuffer.Data()[0] == '3') {
                int tabs = 0;
                bool crlf = false;

                // make sure the buffer only contains printable characters
                // and has at least 3 tabs before a CRLF
                for (i = 0; i < fInputBuffer.Size(); i++) {
                    char c = fInputBuffer.Data()[i];
                    if (c == '\t') {
                        if (!crlf)
                            tabs++;
                    } else if (c == '\r' || c == '\n') {
                        if (tabs < 3)
                            break;
                        crlf = true;
                    } else if (!isprint(fInputBuffer.Data()[i])) {
                        crlf = false;
                        break;
                    }
                }
                if (crlf && tabs > 2 && tabs < 5) {
                    // TODO:
                    //if enough data
                    // else continue
                    fItemType = GOPHER_TYPE_DIRECTORY;
                    readError = B_RESOURCE_NOT_FOUND;
                    // continue parsing the error text anyway
                }
            }
            // special case for buggy(?) Gophernicus/1.5
            static const char *buggy = "Error: File or directory not found!";
            if (fInputBuffer.Size() > strlen(buggy)
                    && !memcmp(fInputBuffer.Data(), buggy, strlen(buggy))) {
                fItemType = GOPHER_TYPE_DIRECTORY;
                readError = B_RESOURCE_NOT_FOUND;
                // continue parsing the error text anyway
                // but it won't look good
            }


            // now we probably have correct data
            dataValidated = true;

            //! ProtocolHook:ResponseStarted
            if (fListener != NULL)
                fListener->ResponseStarted(this);

            // we don't really have headers but well...
            //! ProtocolHook:HeadersReceived
            if (fListener != NULL)
                fListener->HeadersReceived(this);

            // now we can assign MIME type if we know it
            const char *mime = "application/octet-stream";
            for (i = 0; gopher_type_map[i].type != GOPHER_TYPE_NONE; i++) {
                if (gopher_type_map[i].type == fItemType) {
                    mime = gopher_type_map[i].mime;
                    break;
                }
            }
            fResult.SetContentType(mime);
        }

        if (_NeedsParsing())
            _ParseInput(receiveEnd);
        else if (fInputBuffer.Size()) {
            // send input directly
            if (fListener != NULL) {
                fListener->DataReceived(this, (const char *)fInputBuffer.Data(),
                                        fPosition, fInputBuffer.Size());
            }

            fPosition += fInputBuffer.Size();

            // XXX: this is plain stupid, we already copied the data
            // and just want to drop it...
            char *inputTempBuffer = new(std::nothrow) char[bytesRead];
            if (inputTempBuffer == NULL) {
                readError = B_NO_MEMORY;
                break;
            }
            fInputBuffer.RemoveData(inputTempBuffer, fInputBuffer.Size());
            delete[] inputTempBuffer;
        }
    }

    if (fPosition > 0) {
        fResult.SetLength(fPosition);
        if (fListener != NULL)
            fListener->DownloadProgress(this, fPosition, fPosition);
    }

    fSocket->Disconnect();

    if (readError != B_OK)
        return readError;

    return fQuit ? B_INTERRUPTED : B_OK;
}