• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/spdy/spdy_http_utils.h"
6 
7 #include <string>
8 #include <vector>
9 
10 #include "base/strings/escape.h"
11 #include "base/strings/strcat.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_piece.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "base/time/time.h"
17 #include "net/base/load_flags.h"
18 #include "net/base/url_util.h"
19 #include "net/http/http_request_headers.h"
20 #include "net/http/http_request_info.h"
21 #include "net/http/http_response_headers.h"
22 #include "net/http/http_response_info.h"
23 #include "net/http/http_util.h"
24 
25 namespace net {
26 
27 namespace {
28 
AddSpdyHeader(const std::string & name,const std::string & value,spdy::Http2HeaderBlock * headers)29 void AddSpdyHeader(const std::string& name,
30                    const std::string& value,
31                    spdy::Http2HeaderBlock* headers) {
32   if (headers->find(name) == headers->end()) {
33     (*headers)[name] = value;
34   } else {
35     (*headers)[name] = base::StrCat(
36         {(*headers)[name].as_string(), base::StringPiece("\0", 1), value});
37   }
38 }
39 
40 }  // namespace
41 
SpdyHeadersToHttpResponse(const spdy::Http2HeaderBlock & headers,HttpResponseInfo * response)42 int SpdyHeadersToHttpResponse(const spdy::Http2HeaderBlock& headers,
43                               HttpResponseInfo* response) {
44   // The ":status" header is required.
45   spdy::Http2HeaderBlock::const_iterator it =
46       headers.find(spdy::kHttp2StatusHeader);
47   if (it == headers.end())
48     return ERR_INCOMPLETE_HTTP2_HEADERS;
49 
50   const auto status = it->second;
51   std::string raw_headers =
52       base::StrCat({"HTTP/1.1 ", status, base::StringPiece("\0", 1)});
53   for (const auto& [name, value] : headers) {
54     DCHECK_GT(name.size(), 0u);
55     if (name[0] == ':') {
56       // https://tools.ietf.org/html/rfc7540#section-8.1.2.4
57       // Skip pseudo headers.
58       continue;
59     }
60     // For each value, if the server sends a NUL-separated
61     // list of values, we separate that back out into
62     // individual headers for each value in the list.
63     // e.g.
64     //    Set-Cookie "foo\0bar"
65     // becomes
66     //    Set-Cookie: foo\0
67     //    Set-Cookie: bar\0
68     size_t start = 0;
69     size_t end = 0;
70     do {
71       end = value.find('\0', start);
72       base::StringPiece tval;
73       if (end != value.npos)
74         tval = value.substr(start, (end - start));
75       else
76         tval = value.substr(start);
77       base::StrAppend(&raw_headers,
78                       {name, ":", tval, base::StringPiece("\0", 1)});
79       start = end + 1;
80     } while (end != value.npos);
81   }
82 
83   response->headers = base::MakeRefCounted<HttpResponseHeaders>(raw_headers);
84 
85   // When there are multiple location headers the response is a potential
86   // response smuggling attack.
87   if (HttpUtil::HeadersContainMultipleCopiesOfField(*response->headers,
88                                                     "location")) {
89     return ERR_RESPONSE_HEADERS_MULTIPLE_LOCATION;
90   }
91 
92   response->was_fetched_via_spdy = true;
93   return OK;
94 }
95 
CreateSpdyHeadersFromHttpRequest(const HttpRequestInfo & info,const HttpRequestHeaders & request_headers,spdy::Http2HeaderBlock * headers)96 void CreateSpdyHeadersFromHttpRequest(const HttpRequestInfo& info,
97                                       const HttpRequestHeaders& request_headers,
98                                       spdy::Http2HeaderBlock* headers) {
99   (*headers)[spdy::kHttp2MethodHeader] = info.method;
100   if (info.method == "CONNECT") {
101     (*headers)[spdy::kHttp2AuthorityHeader] = GetHostAndPort(info.url);
102   } else {
103     (*headers)[spdy::kHttp2AuthorityHeader] = GetHostAndOptionalPort(info.url);
104     (*headers)[spdy::kHttp2SchemeHeader] = info.url.scheme();
105     (*headers)[spdy::kHttp2PathHeader] = info.url.PathForRequest();
106   }
107 
108   HttpRequestHeaders::Iterator it(request_headers);
109   while (it.GetNext()) {
110     std::string name = base::ToLowerASCII(it.name());
111     if (name.empty() || name[0] == ':' || name == "connection" ||
112         name == "proxy-connection" || name == "transfer-encoding" ||
113         name == "host") {
114       continue;
115     }
116     AddSpdyHeader(name, it.value(), headers);
117   }
118 }
119 
CreateSpdyHeadersFromHttpRequestForWebSocket(const GURL & url,const HttpRequestHeaders & request_headers,spdy::Http2HeaderBlock * headers)120 void CreateSpdyHeadersFromHttpRequestForWebSocket(
121     const GURL& url,
122     const HttpRequestHeaders& request_headers,
123     spdy::Http2HeaderBlock* headers) {
124   (*headers)[spdy::kHttp2MethodHeader] = "CONNECT";
125   (*headers)[spdy::kHttp2AuthorityHeader] = GetHostAndOptionalPort(url);
126   (*headers)[spdy::kHttp2SchemeHeader] = "https";
127   (*headers)[spdy::kHttp2PathHeader] = url.PathForRequest();
128   (*headers)[spdy::kHttp2ProtocolHeader] = "websocket";
129 
130   HttpRequestHeaders::Iterator it(request_headers);
131   while (it.GetNext()) {
132     std::string name = base::ToLowerASCII(it.name());
133     if (name.empty() || name[0] == ':' || name == "upgrade" ||
134         name == "connection" || name == "proxy-connection" ||
135         name == "transfer-encoding" || name == "host") {
136       continue;
137     }
138     AddSpdyHeader(name, it.value(), headers);
139   }
140 }
141 
142 static_assert(HIGHEST - LOWEST < 4 && HIGHEST - MINIMUM_PRIORITY < 6,
143               "request priority incompatible with spdy");
144 
ConvertRequestPriorityToSpdyPriority(const RequestPriority priority)145 spdy::SpdyPriority ConvertRequestPriorityToSpdyPriority(
146     const RequestPriority priority) {
147   DCHECK_GE(priority, MINIMUM_PRIORITY);
148   DCHECK_LE(priority, MAXIMUM_PRIORITY);
149   return static_cast<spdy::SpdyPriority>(MAXIMUM_PRIORITY - priority +
150                                          spdy::kV3HighestPriority);
151 }
152 
153 NET_EXPORT_PRIVATE RequestPriority
ConvertSpdyPriorityToRequestPriority(spdy::SpdyPriority priority)154 ConvertSpdyPriorityToRequestPriority(spdy::SpdyPriority priority) {
155   // Handle invalid values gracefully.
156   return ((priority - spdy::kV3HighestPriority) >
157           (MAXIMUM_PRIORITY - MINIMUM_PRIORITY))
158              ? IDLE
159              : static_cast<RequestPriority>(
160                    MAXIMUM_PRIORITY - (priority - spdy::kV3HighestPriority));
161 }
162 
ConvertHeaderBlockToHttpRequestHeaders(const spdy::Http2HeaderBlock & spdy_headers,HttpRequestHeaders * http_headers)163 NET_EXPORT_PRIVATE void ConvertHeaderBlockToHttpRequestHeaders(
164     const spdy::Http2HeaderBlock& spdy_headers,
165     HttpRequestHeaders* http_headers) {
166   for (const auto& it : spdy_headers) {
167     base::StringPiece key = it.first;
168     if (key[0] == ':') {
169       key.remove_prefix(1);
170     }
171     std::vector<base::StringPiece> values = base::SplitStringPiece(
172         it.second, "\0", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
173     for (const auto& value : values) {
174       http_headers->SetHeader(key, value);
175     }
176   }
177 }
178 
179 }  // namespace net
180