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.
5 #include "net/spdy/spdy_http_utils.h"
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"
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
;
31 (*headers
)[name
] += '\0' + value
;
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";
46 // The "status" header is required. "version" is required below HTTP/2.
47 SpdyHeaderBlock::const_iterator it
;
48 it
= headers
.find(status_key
);
49 if (it
== headers
.end())
53 if (protocol_version
>= HTTP2
) {
56 it
= headers
.find(version_key
);
57 if (it
== headers
.end())
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.
70 // Set-Cookie "foo\0bar"
74 std::string value
= it
->second
;
78 end
= value
.find('\0', start
);
80 if (end
!= value
.npos
)
81 tval
= value
.substr(start
, (end
- start
));
83 tval
= value
.substr(start
);
84 if (protocol_version
>= 3 && it
->first
[0] == ':')
85 raw_headers
.append(it
->first
.substr(1));
87 raw_headers
.append(it
->first
);
88 raw_headers
.push_back(':');
89 raw_headers
.append(tval
);
90 raw_headers
.push_back('\0');
92 } while (end
!= value
.npos
);
95 response
->headers
= new HttpResponseHeaders(raw_headers
);
96 response
->was_fetched_via_spdy
= true;
100 void CreateSpdyHeadersFromHttpRequest(const HttpRequestInfo
& info
,
101 const HttpRequestHeaders
& request_headers
,
102 SpdyMajorVersion protocol_version
,
104 SpdyHeaderBlock
* headers
) {
106 HttpRequestHeaders::Iterator
it(request_headers
);
107 while (it
.GetNext()) {
108 std::string name
= base::ToLowerASCII(it
.name());
109 if (name
== "connection" || name
== "proxy-connection" ||
110 name
== "transfer-encoding" || name
== "host") {
113 AddSpdyHeader(name
, it
.value(), headers
);
115 static const char kHttpProtocolVersion
[] = "HTTP/1.1";
117 switch (protocol_version
) {
119 // TODO(bnc): Remove this code now that SPDY/2 is deprecated.
120 (*headers
)["version"] = kHttpProtocolVersion
;
121 (*headers
)["method"] = info
.method
;
122 (*headers
)["host"] = GetHostAndOptionalPort(info
.url
);
123 if (info
.method
== "CONNECT") {
124 (*headers
)["url"] = GetHostAndPort(info
.url
);
126 (*headers
)["scheme"] = info
.url
.scheme();
127 (*headers
)["url"] = direct
? info
.url
.PathForRequest()
128 : HttpUtil::SpecForRequest(info
.url
);
132 (*headers
)[":version"] = kHttpProtocolVersion
;
133 (*headers
)[":host"] = GetHostAndOptionalPort(info
.url
);
134 (*headers
)[":method"] = info
.method
;
135 if (info
.method
== "CONNECT") {
136 (*headers
)[":path"] = GetHostAndPort(info
.url
);
138 (*headers
)[":scheme"] = info
.url
.scheme();
139 (*headers
)[":path"] = info
.url
.PathForRequest();
143 (*headers
)[":method"] = info
.method
;
144 if (info
.method
== "CONNECT") {
145 (*headers
)[":authority"] = GetHostAndPort(info
.url
);
147 (*headers
)[":authority"] = GetHostAndOptionalPort(info
.url
);
148 (*headers
)[":scheme"] = info
.url
.scheme();
149 (*headers
)[":path"] = info
.url
.PathForRequest();
157 void CreateSpdyHeadersFromHttpResponse(
158 const HttpResponseHeaders
& response_headers
,
159 SpdyMajorVersion protocol_version
,
160 SpdyHeaderBlock
* headers
) {
161 std::string status_key
= (protocol_version
>= SPDY3
) ? ":status" : "status";
162 std::string version_key
=
163 (protocol_version
>= SPDY3
) ? ":version" : "version";
165 const std::string status_line
= response_headers
.GetStatusLine();
166 std::string::const_iterator after_version
=
167 std::find(status_line
.begin(), status_line
.end(), ' ');
168 if (protocol_version
< HTTP2
) {
169 (*headers
)[version_key
] = std::string(status_line
.begin(), after_version
);
171 (*headers
)[status_key
] = std::string(after_version
+ 1, status_line
.end());
174 std::string raw_name
, value
;
175 while (response_headers
.EnumerateHeaderLines(&iter
, &raw_name
, &value
)) {
176 std::string name
= base::ToLowerASCII(raw_name
);
177 AddSpdyHeader(name
, value
, headers
);
181 static_assert(HIGHEST
- LOWEST
< 4 && HIGHEST
- MINIMUM_PRIORITY
< 5,
182 "request priority incompatible with spdy");
184 SpdyPriority
ConvertRequestPriorityToSpdyPriority(
185 const RequestPriority priority
,
186 SpdyMajorVersion protocol_version
) {
187 DCHECK_GE(priority
, MINIMUM_PRIORITY
);
188 DCHECK_LE(priority
, MAXIMUM_PRIORITY
);
189 return static_cast<SpdyPriority
>(MAXIMUM_PRIORITY
- priority
);
192 NET_EXPORT_PRIVATE RequestPriority
ConvertSpdyPriorityToRequestPriority(
193 SpdyPriority priority
,
194 SpdyMajorVersion protocol_version
) {
195 // Handle invalid values gracefully.
196 // Note that SpdyPriority is not an enum, hence the magic constants.
197 return (priority
>= 5) ?
198 IDLE
: static_cast<RequestPriority
>(4 - priority
);
201 GURL
GetUrlFromHeaderBlock(const SpdyHeaderBlock
& headers
,
202 SpdyMajorVersion protocol_version
,
204 DCHECK_LE(SPDY3
, protocol_version
);
205 SpdyHeaderBlock::const_iterator it
= headers
.find(":scheme");
206 if (it
== headers
.end())
208 std::string url
= it
->second
;
211 it
= headers
.find(protocol_version
>= HTTP2
? ":authority" : ":host");
212 if (it
== headers
.end())
214 url
.append(it
->second
);
216 it
= headers
.find(":path");
217 if (it
== headers
.end())
219 url
.append(it
->second
);