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"
23 bool SpdyHeadersToHttpResponse(const SpdyHeaderBlock
& headers
,
24 SpdyMajorVersion protocol_version
,
25 HttpResponseInfo
* response
) {
26 std::string status_key
= (protocol_version
>= SPDY3
) ? ":status" : "status";
27 std::string version_key
=
28 (protocol_version
>= SPDY3
) ? ":version" : "version";
32 // The "status" header is required. "version" is required below SPDY4.
33 SpdyHeaderBlock::const_iterator it
;
34 it
= headers
.find(status_key
);
35 if (it
== headers
.end())
39 if (protocol_version
>= SPDY4
) {
42 it
= headers
.find(version_key
);
43 if (it
== headers
.end())
47 std::string
raw_headers(version
);
48 raw_headers
.push_back(' ');
49 raw_headers
.append(status
);
50 raw_headers
.push_back('\0');
51 for (it
= headers
.begin(); it
!= headers
.end(); ++it
) {
52 // For each value, if the server sends a NUL-separated
53 // list of values, we separate that back out into
54 // individual headers for each value in the list.
56 // Set-Cookie "foo\0bar"
60 std::string value
= it
->second
;
64 end
= value
.find('\0', start
);
66 if (end
!= value
.npos
)
67 tval
= value
.substr(start
, (end
- start
));
69 tval
= value
.substr(start
);
70 if (protocol_version
>= 3 && it
->first
[0] == ':')
71 raw_headers
.append(it
->first
.substr(1));
73 raw_headers
.append(it
->first
);
74 raw_headers
.push_back(':');
75 raw_headers
.append(tval
);
76 raw_headers
.push_back('\0');
78 } while (end
!= value
.npos
);
81 response
->headers
= new HttpResponseHeaders(raw_headers
);
82 response
->was_fetched_via_spdy
= true;
86 void CreateSpdyHeadersFromHttpRequest(const HttpRequestInfo
& info
,
87 const HttpRequestHeaders
& request_headers
,
88 SpdyHeaderBlock
* headers
,
89 SpdyMajorVersion protocol_version
,
92 HttpRequestHeaders::Iterator
it(request_headers
);
93 while (it
.GetNext()) {
94 std::string name
= StringToLowerASCII(it
.name());
95 if (name
== "connection" || name
== "proxy-connection" ||
96 name
== "transfer-encoding" || name
== "host") {
99 if (headers
->find(name
) == headers
->end()) {
100 (*headers
)[name
] = it
.value();
102 std::string new_value
= (*headers
)[name
];
103 new_value
.append(1, '\0'); // +=() doesn't append 0's
104 new_value
+= it
.value();
105 (*headers
)[name
] = new_value
;
108 static const char kHttpProtocolVersion
[] = "HTTP/1.1";
110 if (protocol_version
< SPDY3
) {
111 (*headers
)["version"] = kHttpProtocolVersion
;
112 (*headers
)["method"] = info
.method
;
113 (*headers
)["host"] = GetHostAndOptionalPort(info
.url
);
114 (*headers
)["scheme"] = info
.url
.scheme();
116 (*headers
)["url"] = HttpUtil::PathForRequest(info
.url
);
118 (*headers
)["url"] = HttpUtil::SpecForRequest(info
.url
);
120 if (protocol_version
< SPDY4
) {
121 (*headers
)[":version"] = kHttpProtocolVersion
;
122 (*headers
)[":host"] = GetHostAndOptionalPort(info
.url
);
124 (*headers
)[":authority"] = GetHostAndOptionalPort(info
.url
);
126 (*headers
)[":method"] = info
.method
;
127 (*headers
)[":scheme"] = info
.url
.scheme();
128 (*headers
)[":path"] = HttpUtil::PathForRequest(info
.url
);
132 COMPILE_ASSERT(HIGHEST
- LOWEST
< 4 &&
133 HIGHEST
- MINIMUM_PRIORITY
< 5,
134 request_priority_incompatible_with_spdy
);
136 SpdyPriority
ConvertRequestPriorityToSpdyPriority(
137 const RequestPriority priority
,
138 SpdyMajorVersion protocol_version
) {
139 DCHECK_GE(priority
, MINIMUM_PRIORITY
);
140 DCHECK_LE(priority
, MAXIMUM_PRIORITY
);
141 if (protocol_version
== SPDY2
) {
142 // SPDY 2 only has 2 bits of priority, but we have 5 RequestPriorities.
143 // Map IDLE => 3, LOWEST => 2, LOW => 2, MEDIUM => 1, HIGHEST => 0.
144 if (priority
> LOWEST
) {
145 return static_cast<SpdyPriority
>(HIGHEST
- priority
);
147 return static_cast<SpdyPriority
>(HIGHEST
- priority
- 1);
150 return static_cast<SpdyPriority
>(HIGHEST
- priority
);
154 NET_EXPORT_PRIVATE RequestPriority
ConvertSpdyPriorityToRequestPriority(
155 SpdyPriority priority
,
156 SpdyMajorVersion protocol_version
) {
157 // Handle invalid values gracefully, and pick LOW to map 2 back
159 SpdyPriority idle_cutoff
= (protocol_version
== SPDY2
) ? 3 : 5;
160 return (priority
>= idle_cutoff
) ?
161 IDLE
: static_cast<RequestPriority
>(HIGHEST
- priority
);
164 GURL
GetUrlFromHeaderBlock(const SpdyHeaderBlock
& headers
,
165 SpdyMajorVersion protocol_version
,
167 // SPDY 2 server push urls are specified in a single "url" header.
168 if (pushed
&& protocol_version
== SPDY2
) {
170 SpdyHeaderBlock::const_iterator it
;
171 it
= headers
.find("url");
172 if (it
!= headers
.end())
177 const char* scheme_header
= protocol_version
>= SPDY3
? ":scheme" : "scheme";
178 const char* host_header
= protocol_version
>= SPDY4
? ":authority" :
179 (protocol_version
>= SPDY3
? ":host" : "host");
180 const char* path_header
= protocol_version
>= SPDY3
? ":path" : "url";
183 std::string host_port
;
185 SpdyHeaderBlock::const_iterator it
;
186 it
= headers
.find(scheme_header
);
187 if (it
!= headers
.end())
189 it
= headers
.find(host_header
);
190 if (it
!= headers
.end())
191 host_port
= it
->second
;
192 it
= headers
.find(path_header
);
193 if (it
!= headers
.end())
196 std::string url
= (scheme
.empty() || host_port
.empty() || path
.empty())
198 : scheme
+ "://" + host_port
+ path
;