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::StringToLowerASCII(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 if (protocol_version
< SPDY3
) {
118 // TODO(bcn): Remove this code now that SPDY/2 is deprecated.
119 (*headers
)["version"] = kHttpProtocolVersion
;
120 (*headers
)["method"] = info
.method
;
121 (*headers
)["host"] = GetHostAndOptionalPort(info
.url
);
122 if (info
.method
== "CONNECT") {
123 (*headers
)["url"] = GetHostAndPort(info
.url
);
125 (*headers
)["scheme"] = info
.url
.scheme();
126 (*headers
)["url"] = direct
? info
.url
.PathForRequest()
127 : HttpUtil::SpecForRequest(info
.url
);
130 if (protocol_version
< HTTP2
) {
131 (*headers
)[":version"] = kHttpProtocolVersion
;
132 (*headers
)[":host"] = GetHostAndOptionalPort(info
.url
);
134 (*headers
)[":authority"] = GetHostAndOptionalPort(info
.url
);
136 (*headers
)[":method"] = info
.method
;
137 if (info
.method
== "CONNECT") {
138 // TODO(bnc): https://crbug.com/433784
139 (*headers
)[":path"] = GetHostAndPort(info
.url
);
141 (*headers
)[":scheme"] = info
.url
.scheme();
142 (*headers
)[":path"] = info
.url
.PathForRequest();
147 void CreateSpdyHeadersFromHttpResponse(
148 const HttpResponseHeaders
& response_headers
,
149 SpdyMajorVersion protocol_version
,
150 SpdyHeaderBlock
* headers
) {
151 std::string status_key
= (protocol_version
>= SPDY3
) ? ":status" : "status";
152 std::string version_key
=
153 (protocol_version
>= SPDY3
) ? ":version" : "version";
155 const std::string status_line
= response_headers
.GetStatusLine();
156 std::string::const_iterator after_version
=
157 std::find(status_line
.begin(), status_line
.end(), ' ');
158 if (protocol_version
< HTTP2
) {
159 (*headers
)[version_key
] = std::string(status_line
.begin(), after_version
);
161 (*headers
)[status_key
] = std::string(after_version
+ 1, status_line
.end());
164 std::string raw_name
, value
;
165 while (response_headers
.EnumerateHeaderLines(&iter
, &raw_name
, &value
)) {
166 std::string name
= base::StringToLowerASCII(raw_name
);
167 AddSpdyHeader(name
, value
, headers
);
171 static_assert(HIGHEST
- LOWEST
< 4 && HIGHEST
- MINIMUM_PRIORITY
< 5,
172 "request priority incompatible with spdy");
174 SpdyPriority
ConvertRequestPriorityToSpdyPriority(
175 const RequestPriority priority
,
176 SpdyMajorVersion protocol_version
) {
177 DCHECK_GE(priority
, MINIMUM_PRIORITY
);
178 DCHECK_LE(priority
, MAXIMUM_PRIORITY
);
179 return static_cast<SpdyPriority
>(MAXIMUM_PRIORITY
- priority
);
182 NET_EXPORT_PRIVATE RequestPriority
ConvertSpdyPriorityToRequestPriority(
183 SpdyPriority priority
,
184 SpdyMajorVersion protocol_version
) {
185 // Handle invalid values gracefully.
186 // Note that SpdyPriority is not an enum, hence the magic constants.
187 return (priority
>= 5) ?
188 IDLE
: static_cast<RequestPriority
>(4 - priority
);
191 GURL
GetUrlFromHeaderBlock(const SpdyHeaderBlock
& headers
,
192 SpdyMajorVersion protocol_version
,
194 DCHECK_LE(SPDY3
, protocol_version
);
195 SpdyHeaderBlock::const_iterator it
= headers
.find(":scheme");
196 if (it
== headers
.end())
198 std::string url
= it
->second
;
201 it
= headers
.find(protocol_version
>= HTTP2
? ":authority" : ":host");
202 if (it
== headers
.end())
204 url
.append(it
->second
);
206 it
= headers
.find(":path");
207 if (it
== headers
.end())
209 url
.append(it
->second
);