OLD | NEW |
| (Empty) |
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 | |
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 | |
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 | |
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 | |
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 static_assert(HIGHEST - LOWEST < 4 && HIGHEST - MINIMUM_PRIORITY < 5, | |
164 "request priority incompatible with spdy"); | |
165 | |
166 SpdyPriority ConvertRequestPriorityToSpdyPriority( | |
167 const RequestPriority priority, | |
168 SpdyMajorVersion protocol_version) { | |
169 DCHECK_GE(priority, MINIMUM_PRIORITY); | |
170 DCHECK_LE(priority, MAXIMUM_PRIORITY); | |
171 return static_cast<SpdyPriority>(MAXIMUM_PRIORITY - priority); | |
172 } | |
173 | |
174 NET_EXPORT_PRIVATE RequestPriority ConvertSpdyPriorityToRequestPriority( | |
175 SpdyPriority priority, | |
176 SpdyMajorVersion protocol_version) { | |
177 // Handle invalid values gracefully. | |
178 // Note that SpdyPriority is not an enum, hence the magic constants. | |
179 return (priority >= 5) ? | |
180 IDLE : static_cast<RequestPriority>(4 - priority); | |
181 } | |
182 | |
183 GURL GetUrlFromHeaderBlock(const SpdyHeaderBlock& headers, | |
184 SpdyMajorVersion protocol_version, | |
185 bool pushed) { | |
186 const char* scheme_header = protocol_version >= SPDY3 ? ":scheme" : "scheme"; | |
187 const char* host_header = protocol_version >= SPDY4 ? ":authority" : | |
188 (protocol_version >= SPDY3 ? ":host" : "host"); | |
189 const char* path_header = protocol_version >= SPDY3 ? ":path" : "url"; | |
190 | |
191 std::string scheme; | |
192 std::string host_port; | |
193 std::string path; | |
194 SpdyHeaderBlock::const_iterator it; | |
195 it = headers.find(scheme_header); | |
196 if (it != headers.end()) | |
197 scheme = it->second; | |
198 it = headers.find(host_header); | |
199 if (it != headers.end()) | |
200 host_port = it->second; | |
201 it = headers.find(path_header); | |
202 if (it != headers.end()) | |
203 path = it->second; | |
204 | |
205 std::string url = (scheme.empty() || host_port.empty() || path.empty()) | |
206 ? std::string() | |
207 : scheme + "://" + host_port + path; | |
208 return GURL(url); | |
209 } | |
210 | |
211 } // namespace net | |
OLD | NEW |