void start_request()

in Release/src/http/client/http_client_asio.cpp [731:926]


    void start_request()
    {
        if (m_request._cancellation_token().is_canceled())
        {
            request_context::report_error(make_error_code(std::errc::operation_canceled).value(),
                                          "Request canceled by user.");
            return;
        }

        http_proxy_type proxy_type = http_proxy_type::none;
        std::string proxy_host;
        int proxy_port = -1;

        // There is no support for auto-detection of proxies on non-windows platforms, it must be specified explicitly
        // from the client code.
        if (m_http_client->client_config().proxy().is_specified())
        {
            proxy_type =
                m_http_client->base_uri().scheme() == U("https") ? http_proxy_type::ssl_tunnel : http_proxy_type::http;
            auto proxy = m_http_client->client_config().proxy();
            auto proxy_uri = proxy.address();
            proxy_port = proxy_uri.port() == -1 ? 8080 : proxy_uri.port();
            proxy_host = utility::conversions::to_utf8string(proxy_uri.host());
        }

        auto start_http_request_flow = [proxy_type, proxy_host, proxy_port AND_CAPTURE_MEMBER_FUNCTION_POINTERS](
                                           std::shared_ptr<asio_context> ctx) {
            if (ctx->m_request._cancellation_token().is_canceled())
            {
                ctx->request_context::report_error(make_error_code(std::errc::operation_canceled).value(),
                                                   "Request canceled by user.");
                return;
            }

            const auto& base_uri = ctx->m_http_client->base_uri();
            const auto full_uri = uri_builder(base_uri).append(ctx->m_request.relative_uri()).to_uri();

            // For a normal http proxy, we need to specify the full request uri, otherwise just specify the resource
            auto encoded_resource =
                proxy_type == http_proxy_type::http ? full_uri.to_string() : full_uri.resource().to_string();

            if (encoded_resource.empty())
            {
                encoded_resource = U("/");
            }

            const auto& method = ctx->m_request.method();

            // stop injection of headers via method
            // resource should be ok, since it's been encoded
            // and host won't resolve
            if (!::web::http::details::validate_method(method))
            {
                ctx->report_exception(http_exception("The method string is invalid."));
                return;
            }

            std::ostream request_stream(&ctx->m_body_buf);
            request_stream.imbue(std::locale::classic());
            const auto& host = utility::conversions::to_utf8string(base_uri.host());

            request_stream << utility::conversions::to_utf8string(method) << " "
                           << utility::conversions::to_utf8string(encoded_resource) << " "
                           << "HTTP/1.1\r\n";

            int port = base_uri.port();

            if (base_uri.is_port_default())
            {
                port = (ctx->m_connection->is_ssl() ? 443 : 80);
            }

            // Add the Host header if user has not specified it explicitly
            if (!ctx->m_request.headers().has(header_names::host))
            {
                request_stream << "Host: " << host;
                if (!base_uri.is_port_default())
                {
                    request_stream << ":" << port;
                }
                request_stream << CRLF;
            }

            // Extra request headers are constructed here.
            std::string extra_headers;

            // Add header for basic proxy authentication
            if (proxy_type == http_proxy_type::http &&
                ctx->m_http_client->client_config().proxy().credentials().is_set())
            {
                extra_headers.append(ctx->generate_basic_proxy_auth_header());
            }

            if (ctx->m_http_client->client_config().credentials().is_set())
            {
                extra_headers.append(ctx->generate_basic_auth_header());
            }

            extra_headers += utility::conversions::to_utf8string(ctx->get_compression_header());

            // Check user specified transfer-encoding.
            std::string transferencoding;
            if (ctx->m_request.headers().match(header_names::transfer_encoding, transferencoding) &&
                boost::icontains(transferencoding, U("chunked")))
            {
                ctx->m_needChunked = true;
            }
            else if (!ctx->m_request.headers().match(header_names::content_length, ctx->m_content_length))
            {
                // Stream without content length is the signal of requiring transfer encoding chunked.
                if (ctx->m_request.body())
                {
                    ctx->m_needChunked = true;
                    extra_headers.append("Transfer-Encoding:chunked\r\n");
                }
                else if (ctx->m_request.method() == methods::POST || ctx->m_request.method() == methods::PUT)
                {
                    // Some servers do not accept POST/PUT requests with a content length of 0, such as
                    // lighttpd - http://serverfault.com/questions/315849/curl-post-411-length-required
                    // old apache versions - https://issues.apache.org/jira/browse/TS-2902
                    extra_headers.append("Content-Length: 0\r\n");
                }
            }

            if (proxy_type == http_proxy_type::http)
            {
                extra_headers.append("Cache-Control: no-store, no-cache\r\n"
                                     "Pragma: no-cache\r\n");
            }

            request_stream << utility::conversions::to_utf8string(
                ::web::http::details::flatten_http_headers(ctx->m_request.headers()));
            request_stream << extra_headers;
            // Enforce HTTP connection keep alive (even for the old HTTP/1.0 protocol).
            request_stream << "Connection: Keep-Alive\r\n\r\n";

            // Start connection timeout timer.
            if (!ctx->m_timer.has_started())
            {
                ctx->m_timer.start();
            }

            if (ctx->m_connection->is_reused() || proxy_type == http_proxy_type::ssl_tunnel)
            {
                // If socket is a reused connection or we're connected via an ssl-tunneling proxy, try to write the
                // request directly. In both cases we have already established a tcp connection.
                ctx->write_request();
            }
            else
            {
                // If the connection is new (unresolved and unconnected socket), then start async
                // call to resolve first, leading eventually to request write.

                // For normal http proxies, we want to connect directly to the proxy server. It will relay our request.
                auto tcp_host = proxy_type == http_proxy_type::http ? proxy_host : host;
                auto tcp_port = proxy_type == http_proxy_type::http ? proxy_port : port;

                tcp::resolver::query query(tcp_host, to_string(tcp_port));
                ctx->m_resolver.async_resolve(query,
                                              boost::bind(&asio_context::handle_resolve,
                                                          ctx,
                                                          boost::asio::placeholders::error,
                                                          boost::asio::placeholders::iterator));
            }

            // Register for notification on cancellation to abort this request.
            if (ctx->m_request._cancellation_token() != pplx::cancellation_token::none())
            {
                // weak_ptr prevents lambda from taking shared ownership of the context.
                // Otherwise context replacement in the handle_status_line() would leak the objects.
                std::weak_ptr<asio_context> ctx_weak(ctx);
                ctx->m_cancellationRegistration = ctx->m_request._cancellation_token().register_callback([ctx_weak]() {
                    if (auto ctx_lock = ctx_weak.lock())
                    {
                        // Shut down transmissions, close the socket and prevent connection from being pooled.
                        ctx_lock->m_connection->close();
                    }
                });
            }
        };

        // Note that we must not try to CONNECT using an already established connection via proxy -- this would send
        // CONNECT to the end server which is definitely not what we want.
        if (proxy_type == http_proxy_type::ssl_tunnel && !m_connection->is_reused())
        {
            // The ssl_tunnel_proxy keeps the context alive and then calls back once the ssl tunnel is established via
            // 'start_http_request_flow'
            std::shared_ptr<ssl_proxy_tunnel> ssl_tunnel =
                std::make_shared<ssl_proxy_tunnel>(shared_from_this(), start_http_request_flow);
            ssl_tunnel->start_proxy_connect();
        }
        else
        {
            start_http_request_flow(shared_from_this());
        }
    }