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 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"; 29 std::string version; 30 std::string status; 31 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()) 36 return false; 37 status = it->second; 38 39 if (protocol_version >= SPDY4) { 40 version = "HTTP/1.1"; 41 } else { 42 it = headers.find(version_key); 43 if (it == headers.end()) 44 return false; 45 version = it->second; 46 } 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. 55 // e.g. 56 // Set-Cookie "foo\0bar" 57 // becomes 58 // Set-Cookie: foo\0 59 // Set-Cookie: bar\0 60 std::string value = it->second; 61 size_t start = 0; 62 size_t end = 0; 63 do { 64 end = value.find('\0', start); 65 std::string tval; 66 if (end != value.npos) 67 tval = value.substr(start, (end - start)); 68 else 69 tval = value.substr(start); 70 if (protocol_version >= 3 && it->first[0] == ':') 71 raw_headers.append(it->first.substr(1)); 72 else 73 raw_headers.append(it->first); 74 raw_headers.push_back(':'); 75 raw_headers.append(tval); 76 raw_headers.push_back('\0'); 77 start = end + 1; 78 } while (end != value.npos); 79 } 80 81 response->headers = new HttpResponseHeaders(raw_headers); 82 response->was_fetched_via_spdy = true; 83 return true; 84 } 85 86 void CreateSpdyHeadersFromHttpRequest(const HttpRequestInfo& info, 87 const HttpRequestHeaders& request_headers, 88 SpdyHeaderBlock* headers, 89 SpdyMajorVersion protocol_version, 90 bool direct) { 91 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") { 97 continue; 98 } 99 if (headers->find(name) == headers->end()) { 100 (*headers)[name] = it.value(); 101 } else { 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; 106 } 107 } 108 static const char kHttpProtocolVersion[] = "HTTP/1.1"; 109 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(); 115 if (direct) 116 (*headers)["url"] = HttpUtil::PathForRequest(info.url); 117 else 118 (*headers)["url"] = HttpUtil::SpecForRequest(info.url); 119 } else { 120 if (protocol_version < SPDY4) { 121 (*headers)[":version"] = kHttpProtocolVersion; 122 (*headers)[":host"] = GetHostAndOptionalPort(info.url); 123 } else { 124 (*headers)[":authority"] = GetHostAndOptionalPort(info.url); 125 } 126 (*headers)[":method"] = info.method; 127 (*headers)[":scheme"] = info.url.scheme(); 128 (*headers)[":path"] = HttpUtil::PathForRequest(info.url); 129 } 130 } 131 132 COMPILE_ASSERT(HIGHEST - LOWEST < 4 && 133 HIGHEST - MINIMUM_PRIORITY < 5, 134 request_priority_incompatible_with_spdy); 135 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); 146 } else { 147 return static_cast<SpdyPriority>(HIGHEST - priority - 1); 148 } 149 } else { 150 return static_cast<SpdyPriority>(HIGHEST - priority); 151 } 152 } 153 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 158 // to for SPDY/2. 159 SpdyPriority idle_cutoff = (protocol_version == SPDY2) ? 3 : 5; 160 return (priority >= idle_cutoff) ? 161 IDLE : static_cast<RequestPriority>(HIGHEST - priority); 162 } 163 164 GURL GetUrlFromHeaderBlock(const SpdyHeaderBlock& headers, 165 SpdyMajorVersion protocol_version, 166 bool pushed) { 167 // SPDY 2 server push urls are specified in a single "url" header. 168 if (pushed && protocol_version == SPDY2) { 169 std::string url; 170 SpdyHeaderBlock::const_iterator it; 171 it = headers.find("url"); 172 if (it != headers.end()) 173 url = it->second; 174 return GURL(url); 175 } 176 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"; 181 182 std::string scheme; 183 std::string host_port; 184 std::string path; 185 SpdyHeaderBlock::const_iterator it; 186 it = headers.find(scheme_header); 187 if (it != headers.end()) 188 scheme = it->second; 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()) 194 path = it->second; 195 196 std::string url = (scheme.empty() || host_port.empty() || path.empty()) 197 ? std::string() 198 : scheme + "://" + host_port + path; 199 return GURL(url); 200 } 201 202 } // namespace net 203