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 SPDY4.
47 SpdyHeaderBlock::const_iterator it
;
48 it
= headers
.find(status_key
);
49 if (it
== headers
.end())
53 if (protocol_version
>= SPDY4
) {
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 (*headers
)["version"] = kHttpProtocolVersion
;
119 (*headers
)["method"] = info
.method
;
120 (*headers
)["host"] = GetHostAndOptionalPort(info
.url
);
121 (*headers
)["scheme"] = info
.url
.scheme();
123 (*headers
)["url"] = HttpUtil::PathForRequest(info
.url
);
125 (*headers
)["url"] = HttpUtil::SpecForRequest(info
.url
);
127 if (protocol_version
< SPDY4
) {
128 (*headers
)[":version"] = kHttpProtocolVersion
;
129 (*headers
)[":host"] = GetHostAndOptionalPort(info
.url
);
131 (*headers
)[":authority"] = GetHostAndOptionalPort(info
.url
);
133 (*headers
)[":method"] = info
.method
;
134 (*headers
)[":scheme"] = info
.url
.scheme();
135 (*headers
)[":path"] = HttpUtil::PathForRequest(info
.url
);
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";
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
);
153 (*headers
)[status_key
] = std::string(after_version
+ 1, status_line
.end());
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
);
163 static_assert(HIGHEST
- LOWEST
< 4 && HIGHEST
- MINIMUM_PRIORITY
< 5,
164 "request priority incompatible with spdy");
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
);
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
);
183 GURL
GetUrlFromHeaderBlock(const SpdyHeaderBlock
& headers
,
184 SpdyMajorVersion protocol_version
,
186 DCHECK_LE(SPDY3
, protocol_version
);
187 SpdyHeaderBlock::const_iterator it
= headers
.find(":scheme");
188 if (it
== headers
.end())
190 std::string url
= it
->second
;
193 it
= headers
.find(protocol_version
>= SPDY4
? ":authority" : ":host");
194 if (it
== headers
.end())
196 url
.append(it
->second
);
198 it
= headers
.find(":path");
199 if (it
== headers
.end())
201 url
.append(it
->second
);