blob: e646fe1e9a438e6207ad96616a2381189c01dd2e [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/http/proxy_client_socket.h"
#include <unordered_set>
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "net/base/host_port_pair.h"
#include "net/base/net_errors.h"
#include "net/http/http_auth_controller.h"
#include "net/http/http_request_info.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
#include "url/gurl.h"
namespace net {
void ProxyClientSocket::SetStreamPriority(RequestPriority priority) {}
// static
void ProxyClientSocket::BuildTunnelRequest(
const HostPortPair& endpoint,
const HttpRequestHeaders& auth_headers,
const std::string& user_agent,
std::string* request_line,
HttpRequestHeaders* request_headers) {
// RFC 7230 Section 5.4 says a client MUST send a Host header field in all
// HTTP/1.1 request messages, and Host SHOULD be the first header field
// following the request-line. Add "Proxy-Connection: keep-alive" for compat
// with HTTP/1.0 proxies such as Squid (required for NTLM authentication).
std::string host_and_port = endpoint.ToString();
*request_line =
base::StringPrintf("CONNECT %s HTTP/1.1\r\n", host_and_port.c_str());
request_headers->SetHeader(HttpRequestHeaders::kHost, host_and_port);
request_headers->SetHeader(HttpRequestHeaders::kProxyConnection,
"keep-alive");
if (!user_agent.empty())
request_headers->SetHeader(HttpRequestHeaders::kUserAgent, user_agent);
request_headers->MergeFrom(auth_headers);
}
// static
int ProxyClientSocket::HandleProxyAuthChallenge(
HttpAuthController* auth,
HttpResponseInfo* response,
const NetLogWithSource& net_log) {
DCHECK(response->headers.get());
int rv = auth->HandleAuthChallenge(response->headers, response->ssl_info,
false, true, net_log);
response->auth_challenge = auth->auth_info();
if (rv == OK)
return ERR_PROXY_AUTH_REQUESTED;
return rv;
}
// static
bool ProxyClientSocket::SanitizeProxyAuth(HttpResponseInfo* response) {
DCHECK(response && response->headers.get());
// Copy status line and all hop-by-hop headers to preserve keep-alive
// behavior.
const char* kHeadersToKeep[] = {
"connection", "proxy-connection", "keep-alive", "trailer",
"transfer-encoding", "upgrade",
"content-length",
"proxy-authenticate",
};
// Create a list of all present header not in |kHeadersToKeep|, and then
// remove them.
size_t iter = 0;
std::string header_name;
std::string header_value;
std::unordered_set<std::string> headers_to_remove;
while (response->headers->EnumerateHeaderLines(&iter, &header_name,
&header_value)) {
bool remove = true;
for (const char* header : kHeadersToKeep) {
if (base::EqualsCaseInsensitiveASCII(header, header_name)) {
remove = false;
break;
}
}
if (remove)
headers_to_remove.insert(header_name);
}
response->headers->RemoveHeaders(headers_to_remove);
return true;
}
// static
bool ProxyClientSocket::SanitizeProxyRedirect(HttpResponseInfo* response) {
DCHECK(response && response->headers.get());
std::string location;
if (!response->headers->IsRedirect(&location))
return false;
// Return minimal headers; set "Content-Length: 0" to ignore response body.
std::string fake_response_headers = base::StringPrintf(
"HTTP/1.0 302 Found\n"
"Location: %s\n"
"Content-Length: 0\n"
"Connection: close\n"
"\n",
location.c_str());
std::string raw_headers =
HttpUtil::AssembleRawHeaders(fake_response_headers.data(),
fake_response_headers.length());
response->headers = new HttpResponseHeaders(raw_headers);
return true;
}
} // namespace net