blob: af0084f04419f3e1e3b8b2435b42d879195216ae [file] [log] [blame]
// Copyright 2017 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 "services/network/loader_util.h"
#include <string>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "net/base/load_flags.h"
#include "net/base/mime_sniffer.h"
#include "net/http/http_raw_request_headers.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_util.h"
#include "net/url_request/url_request.h"
#include "services/network/public/cpp/http_raw_request_response_info.h"
#include "services/network/public/cpp/network_switches.h"
#include "services/network/public/cpp/resource_response.h"
#include "services/network/public/mojom/http_raw_request_response_info.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "url/gurl.h"
namespace network {
const char kAcceptHeader[] = "Accept";
const char kFrameAcceptHeader[] =
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,"
"image/apng,*/*;q=0.8";
const char kDefaultAcceptHeader[] = "*/*";
// Concerning headers that consumers probably shouldn't be allowed to set.
// Gathering numbers on these before adding them to kUnsafeHeaders.
const struct {
const char* name;
ConcerningHeaderId histogram_id;
} kConcerningHeaders[] = {
{net::HttpRequestHeaders::kConnection, ConcerningHeaderId::kConnection},
{net::HttpRequestHeaders::kCookie, ConcerningHeaderId::kCookie},
{"Date", ConcerningHeaderId::kDate},
{"Expect", ConcerningHeaderId::kExpect},
// The referer is passed in from the caller on a per-request basis, but
// there's a separate field for it that should be used instead.
{net::HttpRequestHeaders::kReferer, ConcerningHeaderId::kReferer},
{"Via", ConcerningHeaderId::kVia},
};
bool ShouldSniffContent(net::URLRequest* url_request,
const mojom::URLResponseHead& response) {
const std::string& mime_type = response.mime_type;
std::string content_type_options;
url_request->GetResponseHeaderByName("x-content-type-options",
&content_type_options);
bool sniffing_blocked =
base::LowerCaseEqualsASCII(content_type_options, "nosniff");
bool we_would_like_to_sniff =
net::ShouldSniffMimeType(url_request->url(), mime_type);
if (!sniffing_blocked && we_would_like_to_sniff) {
// We're going to look at the data before deciding what the content type
// is. That means we need to delay sending the response started IPC.
VLOG(1) << "To buffer: " << url_request->url().spec();
return true;
}
return false;
}
mojom::HttpRawRequestResponseInfoPtr BuildRawRequestResponseInfo(
const net::URLRequest& request,
const net::HttpRawRequestHeaders& raw_request_headers,
const net::HttpResponseHeaders* raw_response_headers) {
auto info = mojom::HttpRawRequestResponseInfo::New();
const net::HttpResponseInfo& response_info = request.response_info();
// Unparsed headers only make sense if they were sent as text, i.e. HTTP 1.x.
bool report_headers_text =
!response_info.DidUseQuic() && !response_info.was_fetched_via_spdy;
for (const auto& pair : raw_request_headers.headers()) {
info->request_headers.push_back(
mojom::HttpRawHeaderPair::New(pair.first, pair.second));
}
std::string request_line = raw_request_headers.request_line();
if (report_headers_text && !request_line.empty()) {
std::string text = std::move(request_line);
for (const auto& pair : raw_request_headers.headers()) {
if (!pair.second.empty()) {
base::StringAppendF(&text, "%s: %s\r\n", pair.first.c_str(),
pair.second.c_str());
} else {
base::StringAppendF(&text, "%s:\r\n", pair.first.c_str());
}
}
info->request_headers_text = std::move(text);
}
if (!raw_response_headers)
raw_response_headers = request.response_headers();
if (raw_response_headers) {
info->http_status_code = raw_response_headers->response_code();
info->http_status_text = raw_response_headers->GetStatusText();
std::string name;
std::string value;
for (size_t it = 0;
raw_response_headers->EnumerateHeaderLines(&it, &name, &value);) {
info->response_headers.push_back(
mojom::HttpRawHeaderPair::New(name, value));
}
if (report_headers_text) {
info->response_headers_text =
net::HttpUtil::ConvertHeadersBackToHTTPResponse(
raw_response_headers->raw_headers());
}
}
return info;
}
void LogConcerningRequestHeaders(const net::HttpRequestHeaders& request_headers,
bool added_during_redirect) {
net::HttpRequestHeaders::Iterator it(request_headers);
bool concerning_header_found = false;
while (it.GetNext()) {
for (const auto& header : kConcerningHeaders) {
if (base::EqualsCaseInsensitiveASCII(header.name, it.name())) {
concerning_header_found = true;
if (added_during_redirect) {
UMA_HISTOGRAM_ENUMERATION(
"NetworkService.ConcerningRequestHeader.HeaderAddedOnRedirect",
header.histogram_id);
} else {
UMA_HISTOGRAM_ENUMERATION(
"NetworkService.ConcerningRequestHeader.HeaderPresentOnStart",
header.histogram_id);
}
}
}
}
if (added_during_redirect) {
UMA_HISTOGRAM_BOOLEAN(
"NetworkService.ConcerningRequestHeader.AddedOnRedirect",
concerning_header_found);
} else {
UMA_HISTOGRAM_BOOLEAN(
"NetworkService.ConcerningRequestHeader.PresentOnStart",
concerning_header_found);
}
}
} // namespace network