• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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 
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
11 #include "base/time/time.h"
12 #include "net/base/escape.h"
13 #include "net/base/load_flags.h"
14 #include "net/base/net_util.h"
15 #include "net/http/http_request_headers.h"
16 #include "net/http/http_request_info.h"
17 #include "net/http/http_response_headers.h"
18 #include "net/http/http_response_info.h"
19 #include "net/http/http_util.h"
20 
21 namespace net {
22 
23 namespace {
24 
AddSpdyHeader(const std::string & name,const std::string & value,SpdyHeaderBlock * headers)25 void AddSpdyHeader(const std::string& name,
26                    const std::string& value,
27                    SpdyHeaderBlock* headers) {
28   if (headers->find(name) == headers->end()) {
29     (*headers)[name] = value;
30   } else {
31     (*headers)[name] += '\0' + value;
32   }
33 }
34 
35 } // namespace
36 
SpdyHeadersToHttpResponse(const SpdyHeaderBlock & headers,SpdyMajorVersion protocol_version,HttpResponseInfo * response)37 bool SpdyHeadersToHttpResponse(const SpdyHeaderBlock& headers,
38                                SpdyMajorVersion protocol_version,
39                                HttpResponseInfo* response) {
40   std::string status_key = (protocol_version >= SPDY3) ? ":status" : "status";
41   std::string version_key =
42       (protocol_version >= SPDY3) ? ":version" : "version";
43   std::string version;
44   std::string status;
45 
46   // The "status" header is required. "version" is required below SPDY4.
47   SpdyHeaderBlock::const_iterator it;
48   it = headers.find(status_key);
49   if (it == headers.end())
50     return false;
51   status = it->second;
52 
53   if (protocol_version >= SPDY4) {
54     version = "HTTP/1.1";
55   } else {
56     it = headers.find(version_key);
57     if (it == headers.end())
58       return false;
59     version = it->second;
60   }
61   std::string raw_headers(version);
62   raw_headers.push_back(' ');
63   raw_headers.append(status);
64   raw_headers.push_back('\0');
65   for (it = headers.begin(); it != headers.end(); ++it) {
66     // For each value, if the server sends a NUL-separated
67     // list of values, we separate that back out into
68     // individual headers for each value in the list.
69     // e.g.
70     //    Set-Cookie "foo\0bar"
71     // becomes
72     //    Set-Cookie: foo\0
73     //    Set-Cookie: bar\0
74     std::string value = it->second;
75     size_t start = 0;
76     size_t end = 0;
77     do {
78       end = value.find('\0', start);
79       std::string tval;
80       if (end != value.npos)
81         tval = value.substr(start, (end - start));
82       else
83         tval = value.substr(start);
84       if (protocol_version >= 3 && it->first[0] == ':')
85         raw_headers.append(it->first.substr(1));
86       else
87         raw_headers.append(it->first);
88       raw_headers.push_back(':');
89       raw_headers.append(tval);
90       raw_headers.push_back('\0');
91       start = end + 1;
92     } while (end != value.npos);
93   }
94 
95   response->headers = new HttpResponseHeaders(raw_headers);
96   response->was_fetched_via_spdy = true;
97   return true;
98 }
99 
CreateSpdyHeadersFromHttpRequest(const HttpRequestInfo & info,const HttpRequestHeaders & request_headers,SpdyMajorVersion protocol_version,bool direct,SpdyHeaderBlock * headers)100 void CreateSpdyHeadersFromHttpRequest(const HttpRequestInfo& info,
101                                       const HttpRequestHeaders& request_headers,
102                                       SpdyMajorVersion protocol_version,
103                                       bool direct,
104                                       SpdyHeaderBlock* headers) {
105 
106   HttpRequestHeaders::Iterator it(request_headers);
107   while (it.GetNext()) {
108     std::string name = base::StringToLowerASCII(it.name());
109     if (name == "connection" || name == "proxy-connection" ||
110         name == "transfer-encoding" || name == "host") {
111       continue;
112     }
113     AddSpdyHeader(name, it.value(), headers);
114   }
115   static const char kHttpProtocolVersion[] = "HTTP/1.1";
116 
117   if (protocol_version < SPDY3) {
118     (*headers)["version"] = kHttpProtocolVersion;
119     (*headers)["method"] = info.method;
120     (*headers)["host"] = GetHostAndOptionalPort(info.url);
121     (*headers)["scheme"] = info.url.scheme();
122     if (direct)
123       (*headers)["url"] = HttpUtil::PathForRequest(info.url);
124     else
125       (*headers)["url"] = HttpUtil::SpecForRequest(info.url);
126   } else {
127     if (protocol_version < SPDY4) {
128       (*headers)[":version"] = kHttpProtocolVersion;
129       (*headers)[":host"] = GetHostAndOptionalPort(info.url);
130     } else {
131       (*headers)[":authority"] = GetHostAndOptionalPort(info.url);
132     }
133     (*headers)[":method"] = info.method;
134     (*headers)[":scheme"] = info.url.scheme();
135     (*headers)[":path"] = HttpUtil::PathForRequest(info.url);
136   }
137 }
138 
CreateSpdyHeadersFromHttpResponse(const HttpResponseHeaders & response_headers,SpdyMajorVersion protocol_version,SpdyHeaderBlock * headers)139 void CreateSpdyHeadersFromHttpResponse(
140     const HttpResponseHeaders& response_headers,
141     SpdyMajorVersion protocol_version,
142     SpdyHeaderBlock* headers) {
143   std::string status_key = (protocol_version >= SPDY3) ? ":status" : "status";
144   std::string version_key =
145       (protocol_version >= SPDY3) ? ":version" : "version";
146 
147   const std::string status_line = response_headers.GetStatusLine();
148   std::string::const_iterator after_version =
149       std::find(status_line.begin(), status_line.end(), ' ');
150   if (protocol_version < SPDY4) {
151     (*headers)[version_key] = std::string(status_line.begin(), after_version);
152   }
153   (*headers)[status_key] = std::string(after_version + 1, status_line.end());
154 
155   void* iter = NULL;
156   std::string raw_name, value;
157   while (response_headers.EnumerateHeaderLines(&iter, &raw_name, &value)) {
158     std::string name = base::StringToLowerASCII(raw_name);
159     AddSpdyHeader(name, value, headers);
160   }
161 }
162 
163 
164 COMPILE_ASSERT(HIGHEST - LOWEST < 4 &&
165                HIGHEST - MINIMUM_PRIORITY < 5,
166                request_priority_incompatible_with_spdy);
167 
ConvertRequestPriorityToSpdyPriority(const RequestPriority priority,SpdyMajorVersion protocol_version)168 SpdyPriority ConvertRequestPriorityToSpdyPriority(
169     const RequestPriority priority,
170     SpdyMajorVersion protocol_version) {
171   DCHECK_GE(priority, MINIMUM_PRIORITY);
172   DCHECK_LE(priority, MAXIMUM_PRIORITY);
173   if (protocol_version == SPDY2) {
174     // SPDY 2 only has 2 bits of priority, but we have 5 RequestPriorities.
175     // Map IDLE => 3, LOWEST => 2, LOW => 2, MEDIUM => 1, HIGHEST => 0.
176     if (priority > LOWEST) {
177       return static_cast<SpdyPriority>(HIGHEST - priority);
178     } else {
179       return static_cast<SpdyPriority>(HIGHEST - priority - 1);
180     }
181   } else {
182     return static_cast<SpdyPriority>(HIGHEST - priority);
183   }
184 }
185 
ConvertSpdyPriorityToRequestPriority(SpdyPriority priority,SpdyMajorVersion protocol_version)186 NET_EXPORT_PRIVATE RequestPriority ConvertSpdyPriorityToRequestPriority(
187     SpdyPriority priority,
188     SpdyMajorVersion protocol_version) {
189   // Handle invalid values gracefully, and pick LOW to map 2 back
190   // to for SPDY/2.
191   SpdyPriority idle_cutoff = (protocol_version == SPDY2) ? 3 : 5;
192   return (priority >= idle_cutoff) ?
193       IDLE : static_cast<RequestPriority>(HIGHEST - priority);
194 }
195 
GetUrlFromHeaderBlock(const SpdyHeaderBlock & headers,SpdyMajorVersion protocol_version,bool pushed)196 GURL GetUrlFromHeaderBlock(const SpdyHeaderBlock& headers,
197                            SpdyMajorVersion protocol_version,
198                            bool pushed) {
199   // SPDY 2 server push urls are specified in a single "url" header.
200   if (pushed && protocol_version == SPDY2) {
201       std::string url;
202       SpdyHeaderBlock::const_iterator it;
203       it = headers.find("url");
204       if (it != headers.end())
205         url = it->second;
206       return GURL(url);
207   }
208 
209   const char* scheme_header = protocol_version >= SPDY3 ? ":scheme" : "scheme";
210   const char* host_header = protocol_version >= SPDY4 ? ":authority" :
211       (protocol_version >= SPDY3 ? ":host" : "host");
212   const char* path_header = protocol_version >= SPDY3 ? ":path" : "url";
213 
214   std::string scheme;
215   std::string host_port;
216   std::string path;
217   SpdyHeaderBlock::const_iterator it;
218   it = headers.find(scheme_header);
219   if (it != headers.end())
220     scheme = it->second;
221   it = headers.find(host_header);
222   if (it != headers.end())
223     host_port = it->second;
224   it = headers.find(path_header);
225   if (it != headers.end())
226     path = it->second;
227 
228   std::string url = (scheme.empty() || host_port.empty() || path.empty())
229                         ? std::string()
230                         : scheme + "://" + host_port + path;
231   return GURL(url);
232 }
233 
234 }  // namespace net
235