123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- // 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/spdy/spdy_http_utils.h"
- #include <string>
- #include <vector>
- #include "base/strings/abseil_string_conversions.h"
- #include "base/strings/escape.h"
- #include "base/strings/strcat.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/strings/string_piece.h"
- #include "base/strings/string_split.h"
- #include "base/strings/string_util.h"
- #include "base/time/time.h"
- #include "net/base/load_flags.h"
- #include "net/base/url_util.h"
- #include "net/http/http_request_headers.h"
- #include "net/http/http_request_info.h"
- #include "net/http/http_response_headers.h"
- #include "net/http/http_response_info.h"
- #include "net/http/http_util.h"
- namespace net {
- namespace {
- void AddSpdyHeader(const std::string& name,
- const std::string& value,
- spdy::Http2HeaderBlock* headers) {
- if (headers->find(name) == headers->end()) {
- (*headers)[name] = value;
- } else {
- (*headers)[name] = base::StrCat(
- {(*headers)[name].as_string(), base::StringPiece("\0", 1), value});
- }
- }
- } // namespace
- int SpdyHeadersToHttpResponse(const spdy::Http2HeaderBlock& headers,
- HttpResponseInfo* response) {
- // The ":status" header is required.
- spdy::Http2HeaderBlock::const_iterator it =
- headers.find(spdy::kHttp2StatusHeader);
- if (it == headers.end())
- return ERR_INCOMPLETE_HTTP2_HEADERS;
- const auto status = base::StringViewToStringPiece(it->second);
- std::string raw_headers =
- base::StrCat({"HTTP/1.1 ", status, base::StringPiece("\0", 1)});
- for (it = headers.begin(); it != headers.end(); ++it) {
- const auto name = base::StringViewToStringPiece(it->first);
- DCHECK_GT(name.size(), 0u);
- if (name[0] == ':') {
- // https://tools.ietf.org/html/rfc7540#section-8.1.2.4
- // Skip pseudo headers.
- continue;
- }
- // For each value, if the server sends a NUL-separated
- // list of values, we separate that back out into
- // individual headers for each value in the list.
- // e.g.
- // Set-Cookie "foo\0bar"
- // becomes
- // Set-Cookie: foo\0
- // Set-Cookie: bar\0
- const auto value = base::StringViewToStringPiece(it->second);
- size_t start = 0;
- size_t end = 0;
- do {
- end = value.find('\0', start);
- base::StringPiece tval;
- if (end != value.npos)
- tval = value.substr(start, (end - start));
- else
- tval = value.substr(start);
- base::StrAppend(&raw_headers,
- {name, ":", tval, base::StringPiece("\0", 1)});
- start = end + 1;
- } while (end != value.npos);
- }
- response->headers = base::MakeRefCounted<HttpResponseHeaders>(raw_headers);
- // When there are multiple location headers the response is a potential
- // response smuggling attack.
- if (HttpUtil::HeadersContainMultipleCopiesOfField(*response->headers,
- "location")) {
- return ERR_RESPONSE_HEADERS_MULTIPLE_LOCATION;
- }
- response->was_fetched_via_spdy = true;
- return OK;
- }
- void CreateSpdyHeadersFromHttpRequest(const HttpRequestInfo& info,
- const HttpRequestHeaders& request_headers,
- spdy::Http2HeaderBlock* headers) {
- (*headers)[spdy::kHttp2MethodHeader] = info.method;
- if (info.method == "CONNECT") {
- (*headers)[spdy::kHttp2AuthorityHeader] = GetHostAndPort(info.url);
- } else {
- (*headers)[spdy::kHttp2AuthorityHeader] = GetHostAndOptionalPort(info.url);
- (*headers)[spdy::kHttp2SchemeHeader] = info.url.scheme();
- (*headers)[spdy::kHttp2PathHeader] = info.url.PathForRequest();
- }
- HttpRequestHeaders::Iterator it(request_headers);
- while (it.GetNext()) {
- std::string name = base::ToLowerASCII(it.name());
- if (name.empty() || name[0] == ':' || name == "connection" ||
- name == "proxy-connection" || name == "transfer-encoding" ||
- name == "host") {
- continue;
- }
- AddSpdyHeader(name, it.value(), headers);
- }
- }
- void CreateSpdyHeadersFromHttpRequestForWebSocket(
- const GURL& url,
- const HttpRequestHeaders& request_headers,
- spdy::Http2HeaderBlock* headers) {
- (*headers)[spdy::kHttp2MethodHeader] = "CONNECT";
- (*headers)[spdy::kHttp2AuthorityHeader] = GetHostAndOptionalPort(url);
- (*headers)[spdy::kHttp2SchemeHeader] = "https";
- (*headers)[spdy::kHttp2PathHeader] = url.PathForRequest();
- (*headers)[spdy::kHttp2ProtocolHeader] = "websocket";
- HttpRequestHeaders::Iterator it(request_headers);
- while (it.GetNext()) {
- std::string name = base::ToLowerASCII(it.name());
- if (name.empty() || name[0] == ':' || name == "upgrade" ||
- name == "connection" || name == "proxy-connection" ||
- name == "transfer-encoding" || name == "host") {
- continue;
- }
- AddSpdyHeader(name, it.value(), headers);
- }
- }
- static_assert(HIGHEST - LOWEST < 4 && HIGHEST - MINIMUM_PRIORITY < 6,
- "request priority incompatible with spdy");
- spdy::SpdyPriority ConvertRequestPriorityToSpdyPriority(
- const RequestPriority priority) {
- DCHECK_GE(priority, MINIMUM_PRIORITY);
- DCHECK_LE(priority, MAXIMUM_PRIORITY);
- return static_cast<spdy::SpdyPriority>(MAXIMUM_PRIORITY - priority +
- spdy::kV3HighestPriority);
- }
- NET_EXPORT_PRIVATE RequestPriority
- ConvertSpdyPriorityToRequestPriority(spdy::SpdyPriority priority) {
- // Handle invalid values gracefully.
- return ((priority - spdy::kV3HighestPriority) >
- (MAXIMUM_PRIORITY - MINIMUM_PRIORITY))
- ? IDLE
- : static_cast<RequestPriority>(
- MAXIMUM_PRIORITY - (priority - spdy::kV3HighestPriority));
- }
- NET_EXPORT_PRIVATE void ConvertHeaderBlockToHttpRequestHeaders(
- const spdy::Http2HeaderBlock& spdy_headers,
- HttpRequestHeaders* http_headers) {
- for (const auto& it : spdy_headers) {
- base::StringPiece key = base::StringViewToStringPiece(it.first);
- if (key[0] == ':') {
- key.remove_prefix(1);
- }
- std::vector<base::StringPiece> values =
- base::SplitStringPiece(base::StringViewToStringPiece(it.second), "\0",
- base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
- for (const auto& value : values) {
- http_headers->SetHeader(key, value);
- }
- }
- }
- } // namespace net
|