Example #1
0
HttpResponse::HttpResponse(TcpClientSocket& socket)
	: buffer(socket)
	, ischunked(false)
	, contentlength(nlen)
{
	raw_statusline = buffer.getline();
	if (raw_statusline.empty()) { throw http_exception("Empty response"); }

	parseResponseLine(raw_statusline);

	std::vector<std::string> raw_headers;
	for (std::string line = buffer.getline(); !line.empty(); line = buffer.getline()) {
		if (('\x20' == line[0]) || ('\t' == line[0])) {
			auto last = raw_headers.rbegin();
			if (raw_headers.rend() == last) { throw http_exception("HTTP request can't start with a whitespace"); }
			if (last->find(':') == std::string::npos) { throw http_exception("Header name can't reside on multiple lines"); }
			*last += line;
		}
		else {
			raw_headers.push_back(line);
		}
	}

	parseHeaders(raw_headers);
}
Example #2
0
void HttpResponse::parseResponseLine(const std::string& reqline) {
	auto reqline_begin = std::begin(reqline), reqline_end = std::end(reqline), 
		sel_begin = reqline_begin, sel_end = sel_begin;

	if (sel_end == reqline_end) { throw http_exception("Empty response"); }
	if ('\x20' == *sel_end) { throw http_exception("Response can't start with whitespace"); }
	while ((sel_end != reqline_end) && ('\x20' != *sel_end)) { ++sel_end; }
	if (sel_end == reqline_end) { throw http_exception("Status code is absent"); }
	version = std::string(sel_begin, sel_end);
	if ((version != "HTTP/1.0") && (version != "HTTP/1.1")) {
		throw http_exception("Unsupported HTTP version");
	}

	sel_begin = sel_end;
	++sel_begin;
	sel_end = sel_begin;

	if (sel_end == reqline_end) { throw http_exception("Status code is absent"); }
	if ('\x20' == *sel_end) { throw http_exception("Multiple spaces are not allowed in status line"); }
	while ((sel_end != reqline_end) && ('\x20' != *sel_end)) { ++sel_end; }
	if (sel_end == reqline_end) { throw http_exception("Reason phrase is absent"); }
	std::string status_raw = std::string(sel_begin, sel_end);
	if (!strtonum(status_raw, status) &&  ((status < 100) || (status > 999))) {
		throw http_exception("Invalid status"); 
	}
	
	sel_begin = sel_end;
	++sel_begin;
	sel_end = reqline_end;
	reason = std::string(sel_begin, sel_end);
}
Example #3
0
uri details::_http_request::relative_uri() const
{
    // If the listener path is empty, then just return the request URI.
    if(m_listener_path.empty() || m_listener_path == _XPLATSTR("/"))
    {
        return m_uri.resource();
    }

    utility::string_t prefix = uri::decode(m_listener_path);
    utility::string_t path = uri::decode(m_uri.resource().to_string());
    if(path.empty())
    {
        path = _XPLATSTR("/");
    }

    auto pos = path.find(prefix);
    if (pos == 0)
    {
        return uri(uri::encode_uri(path.erase(0, prefix.length())));
    }
    else
    {
        throw http_exception(_XPLATSTR("Error: request was not prefixed with listener uri"));
    }    
}
Example #4
0
void request_context::report_exception(std::exception_ptr exceptionPtr)
{
    auto response_impl = m_response._get_impl();

    // If cancellation has been triggered then ignore any errors.
    if (m_request._cancellation_token().is_canceled())
    {
        exceptionPtr = std::make_exception_ptr(http_exception((int)std::errc::operation_canceled, std::generic_category()));
    }

    // First try to complete the headers with an exception.
    if (m_request_completion.set_exception(exceptionPtr))
    {
        // Complete the request with no msg body. The exception
        // should only be propagated to one of the tce.
        response_impl->_complete(0);
    }
    else
    {
        // Complete the request with an exception
        response_impl->_complete(0, exceptionPtr);
    }

    finish();
}
Example #5
0
pplx::task<void> details::_http_request::reply(const http_response &response)
{
    if(pplx::details::atomic_increment(m_initiated_response) != 1l)
    {
        throw http_exception(U("Error: trying to send multiple responses to an HTTP request"));
    }
    return _reply_impl(response);
}
Example #6
0
void HttpResponse::parseHeaders(const std::vector<std::string>& raw_headers) {
	for (auto it = std::begin(raw_headers), eit = std::end(raw_headers); it != eit; ++it) {
		size_t colon = it->find(':');
		if (std::string::npos == colon) { throw http_exception("Header must have value"); }
		std::string 
			key(it->begin(), it->begin() + colon),
			value(it->begin() + (colon + 1), it->end());

		canonicalizeHeaderName(key);
		trim(value);

		if (value.empty()) { throw http_exception("Header must have value"); }

		auto& d = headers[key];
		d.insert(std::end(d), value);
	}

	auto transfer_encoding = headers.find("Transfer-Encoding");
	if (std::end(headers) != transfer_encoding) {
		std::string lastencoding = *transfer_encoding->second.rbegin();
		toLower(lastencoding, CP_ISO8859_1);
		if ("chunked" != lastencoding) { throw http_exception("\"chunked\" must be the last transfer encoding applied to the message"); }
		ischunked = true;
		return;
	}

	// "Transfer-Encoding: chunked" is not present
	auto content_length = headers.find("Content-Length");
	if (std::end(headers) != content_length) {
		if (content_length->second.size() > 1) { throw http_exception("Multiple Content-Length headers are not allowed"); }
		
		size_t temp;
		if (!strtonum(*content_length->second.begin(), temp) || nlen == temp) { throw http_exception("Invalid Content-Length"); }
		contentlength = temp;
		return;
	}

	// Neither "Transfer-Endcoding: chunked" nor valid Content-Length are present
	auto connection = headers.find("Connection");
	if (std::end(headers) != connection) {	
		for (auto it = std::begin(connection->second), eit = std::end(connection->second); it != eit; ++it) {
			std::string temp = *it;
			toLower(temp, CP_ISO8859_1);
			if (temp == "close") { return; }
		}
	}

	// Neither "Transfer-Endcoding: chunked", valid Content-Length, nor "Connection: close" are present -- that's malformed HTTP/1.1 response
	throw http_exception("Can't obtain message length");	
}
Example #7
0
		virtual void on_post(http_server_context& /*context*/)
		{
			BOOST_THROW_EXCEPTION(http_exception(http_status_code::method_not_allowed));
		}
Example #8
0
		void on_connected(socket& /*socket*/, std::iostream& stream)
		{
			for (std::size_t keep_alive_count = config_.maximum_keep_alive_requests; keep_alive_count != 0; --keep_alive_count)
			{
				stream.exceptions(std::ios_base::badbit);

				if (stream.peek() == EOF)
				{
					stream.clear();
					break;
				}

				stream.exceptions(std::ios_base::eofbit | std::ios_base::failbit | std::ios_base::badbit);

				http_server_context context(stream, config_);
				http_request& req = context.request();
				http_response& res = context.response();

				bool head = false;
				bool keep_alive = false;

				try
				{
					context.read_headers();

					switch (req.get_method())
					{
					case http_method::head:
						head = true;
						break;
					case http_method::get:
					case http_method::post:
						break;
					default:
						BOOST_THROW_EXCEPTION(http_exception(http_status_code::method_not_allowed));
					}

					if (req.is_connection_keep_alive())
					{
						res.set_keep_alive_requests(keep_alive_count);
						keep_alive = true;
					}

					context.validate_headers();

					if (config_.logger)
						config_.logger->http_server_request(req);

					try
					{
						switch (req.get_method())
						{
						case http_method::head:
						case http_method::get:
							derived().on_get(context, head);
							break;
						case http_method::post:
							derived().on_post(context);
							break;
						default:
							BOOST_THROW_EXCEPTION(http_exception(http_status_code::method_not_allowed));
						}
					}
					catch(http_exception&)
					{
						throw;
					}
					catch (interrupted_exception&)
					{
						throw;
					}
					catch (std::exception&)
					{
						BOOST_THROW_EXCEPTION(http_exception(http_status_code::internal_service_error));
					}
				}
				catch (http_exception& e)
				{
					if (config_.logger)
						config_.logger->exception();

					res.set_status_code(e.status_code());

					try
					{
						auto it = config_.error_document.find(e.status_code());
						if (it != config_.error_document.end())
							BOOST_THROW_EXCEPTION(http_exception(http_status_code::not_found));

						req.set_path(it->second);

						derived().on_get(context, head);
					}
					catch (http_exception&)
					{
						null_content().on_get(context, head);
					}
				}

				context.finish_write();

				if (!keep_alive)
					break;
			}
		}
Example #9
0
		void on_get(http_server_context& /*context*/, bool /*head*/)
		{
			BOOST_THROW_EXCEPTION(http_exception(http_status_code::method_not_allowed));
		}
Example #10
0
    // Start sending request.
    void send_request(_In_ const std::shared_ptr<request_context> &request)
    {
        http_request &msg = request->m_request;
        auto winrt_context = std::static_pointer_cast<winrt_request_context>(request);

        if (!validate_method(msg.method()))
        {
            request->report_exception(http_exception(L"The method string is invalid."));
            return;
        }

        if (msg.method() == http::methods::TRCE)
        {
            // Not supported by WinInet. Generate a more specific exception than what WinInet does.
            request->report_exception(http_exception(L"TRACE is not supported"));
            return;
        }

        const size_t content_length = msg._get_impl()->_get_content_length();
        if (content_length == std::numeric_limits<size_t>::max())
        {
            // IXHR2 does not allow transfer encoding chunked. So the user is expected to set the content length
            request->report_exception(http_exception(L"Content length is not specified in the http headers"));
            return;
        }

        // Start sending HTTP request.
        HRESULT hr = CoCreateInstance(
            __uuidof(FreeThreadedXMLHTTP60),
            nullptr,
            CLSCTX_INPROC,
            __uuidof(IXMLHTTPRequest2),
            reinterpret_cast<void**>(winrt_context->m_hRequest.GetAddressOf()));
        if (FAILED(hr))
        {
            request->report_error(hr, L"Failure to create IXMLHTTPRequest2 instance");
            return;
        }

        utility::string_t encoded_resource = http::uri_builder(m_uri).append(msg.relative_uri()).to_string();

        const auto &config = client_config();
        const auto &client_cred = config.credentials();
        const auto &proxy = config.proxy();
        const auto &proxy_cred = proxy.credentials();
        if (!proxy.is_default())
        {
            request->report_exception(http_exception(L"Only a default proxy server is supported"));
            return;
        }

        // New scope to ensure plain text password is cleared as soon as possible.
        {
            utility::string_t username, proxy_username;
            const utility::char_t *password = nullptr;
            const utility::char_t *proxy_password = nullptr;
            ::web::details::plaintext_string password_plaintext, proxy_password_plaintext;

            if (client_cred.is_set())
            {
                username = client_cred.username();
                password_plaintext = client_cred.decrypt();
                password = password_plaintext->c_str();
            }
            if (proxy_cred.is_set())
            {
                proxy_username = proxy_cred.username();
                proxy_password_plaintext = proxy_cred.decrypt();
                proxy_password = proxy_password_plaintext->c_str();
            }

            hr = winrt_context->m_hRequest->Open(
                msg.method().c_str(),
                encoded_resource.c_str(),
                Make<HttpRequestCallback>(winrt_context).Get(),
                username.c_str(),
                password,
                proxy_username.c_str(),
                proxy_password);
        }
        if (FAILED(hr))
        {
            request->report_error(hr, L"Failure to open HTTP request");
            return;
        }

        // Suppress automatic prompts for user credentials, since they are already provided.
        hr = winrt_context->m_hRequest->SetProperty(XHR_PROP_NO_CRED_PROMPT, TRUE);
        if (FAILED(hr))
        {
            request->report_error(hr, L"Failure to set no credentials prompt property");
            return;
        }

        const auto timeout = config.timeout();
        const int secs = static_cast<int>(timeout.count());
        hr = winrt_context->m_hRequest->SetProperty(XHR_PROP_TIMEOUT, secs * 1000);
        if (FAILED(hr))
        {
            request->report_error(hr, L"Failure to set HTTP request properties");
            return;
        }

        // Add headers.
        for (const auto &hdr : msg.headers())
        {
            winrt_context->m_hRequest->SetRequestHeader(hdr.first.c_str(), hdr.second.c_str());
        }

        // Set response stream.
        hr = winrt_context->m_hRequest->SetCustomResponseStream(Make<IResponseStream>(request).Get());
        if (FAILED(hr))
        {
            request->report_error(hr, L"Failure to set HTTP response stream");
            return;
        }

        // Call the callback function of user customized options
        try
        {
            config.call_user_nativehandle_options(winrt_context->m_hRequest.Get());
        }
        catch (...)
        {
            request->report_exception(std::current_exception());
            return;
        }

        if (content_length == 0)
        {
            hr = winrt_context->m_hRequest->Send(nullptr, 0);
        }
        else
        {
            if ( msg.method() == http::methods::GET || msg.method() == http::methods::HEAD )
            {
                request->report_exception(http_exception(get_with_body));
                return;
            }

            hr = winrt_context->m_hRequest->Send(Make<IRequestStream>(winrt_context, content_length).Get(), content_length);
        }

        if ( FAILED(hr) )
        {
            request->report_error(hr, L"Failure to send HTTP request");
            return;
        }

        // Register for notification on cancellation to abort this request.
        if(msg._cancellation_token() != pplx::cancellation_token::none())
        {
            auto requestHandle = winrt_context->m_hRequest;

            // cancellation callback is unregistered when request is completed.
            winrt_context->m_cancellationRegistration = msg._cancellation_token().register_callback([requestHandle]()
            {
                requestHandle->Abort();
            });
        }
    }