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 164 COMPILE_ASSERT(HIGHEST - LOWEST < 4 && 165 HIGHEST - MINIMUM_PRIORITY < 5, 166 request_priority_incompatible_with_spdy); 167 168 SpdyPriority ConvertRequestPriorityToSpdyPriority( 169 const RequestPriority priority, 170 SpdyMajorVersion protocol_version) { 171 DCHECK_GE(priority, MINIMUM_PRIORITY); 172 DCHECK_LE(priority, MAXIMUM_PRIORITY); 173 if (protocol_version == SPDY2) { 174 // SPDY 2 only has 2 bits of priority, but we have 5 RequestPriorities. 175 // Map IDLE => 3, LOWEST => 2, LOW => 2, MEDIUM => 1, HIGHEST => 0. 176 if (priority > LOWEST) { 177 return static_cast<SpdyPriority>(HIGHEST - priority); 178 } else { 179 return static_cast<SpdyPriority>(HIGHEST - priority - 1); 180 } 181 } else { 182 return static_cast<SpdyPriority>(HIGHEST - priority); 183 } 184 } 185 186 NET_EXPORT_PRIVATE RequestPriority ConvertSpdyPriorityToRequestPriority( 187 SpdyPriority priority, 188 SpdyMajorVersion protocol_version) { 189 // Handle invalid values gracefully, and pick LOW to map 2 back 190 // to for SPDY/2. 191 SpdyPriority idle_cutoff = (protocol_version == SPDY2) ? 3 : 5; 192 return (priority >= idle_cutoff) ? 193 IDLE : static_cast<RequestPriority>(HIGHEST - priority); 194 } 195 196 GURL GetUrlFromHeaderBlock(const SpdyHeaderBlock& headers, 197 SpdyMajorVersion protocol_version, 198 bool pushed) { 199 // SPDY 2 server push urls are specified in a single "url" header. 200 if (pushed && protocol_version == SPDY2) { 201 std::string url; 202 SpdyHeaderBlock::const_iterator it; 203 it = headers.find("url"); 204 if (it != headers.end()) 205 url = it->second; 206 return GURL(url); 207 } 208 209 const char* scheme_header = protocol_version >= SPDY3 ? ":scheme" : "scheme"; 210 const char* host_header = protocol_version >= SPDY4 ? ":authority" : 211 (protocol_version >= SPDY3 ? ":host" : "host"); 212 const char* path_header = protocol_version >= SPDY3 ? ":path" : "url"; 213 214 std::string scheme; 215 std::string host_port; 216 std::string path; 217 SpdyHeaderBlock::const_iterator it; 218 it = headers.find(scheme_header); 219 if (it != headers.end()) 220 scheme = it->second; 221 it = headers.find(host_header); 222 if (it != headers.end()) 223 host_port = it->second; 224 it = headers.find(path_header); 225 if (it != headers.end()) 226 path = it->second; 227 228 std::string url = (scheme.empty() || host_port.empty() || path.empty()) 229 ? std::string() 230 : scheme + "://" + host_port + path; 231 return GURL(url); 232 } 233 234 } // namespace net 235