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 int protocol_version, 25 HttpResponseInfo* response) { 26 std::string status_key = (protocol_version >= 3) ? ":status" : "status"; 27 std::string version_key = (protocol_version >= 3) ? ":version" : "version"; 28 std::string version; 29 std::string status; 30 31 // The "status" and "version" headers are required. 32 SpdyHeaderBlock::const_iterator it; 33 it = headers.find(status_key); 34 if (it == headers.end()) 35 return false; 36 status = it->second; 37 38 it = headers.find(version_key); 39 if (it == headers.end()) 40 return false; 41 version = it->second; 42 43 std::string raw_headers(version); 44 raw_headers.push_back(' '); 45 raw_headers.append(status); 46 raw_headers.push_back('\0'); 47 for (it = headers.begin(); it != headers.end(); ++it) { 48 // For each value, if the server sends a NUL-separated 49 // list of values, we separate that back out into 50 // individual headers for each value in the list. 51 // e.g. 52 // Set-Cookie "foo\0bar" 53 // becomes 54 // Set-Cookie: foo\0 55 // Set-Cookie: bar\0 56 std::string value = it->second; 57 size_t start = 0; 58 size_t end = 0; 59 do { 60 end = value.find('\0', start); 61 std::string tval; 62 if (end != value.npos) 63 tval = value.substr(start, (end - start)); 64 else 65 tval = value.substr(start); 66 if (protocol_version >= 3 && it->first[0] == ':') 67 raw_headers.append(it->first.substr(1)); 68 else 69 raw_headers.append(it->first); 70 raw_headers.push_back(':'); 71 raw_headers.append(tval); 72 raw_headers.push_back('\0'); 73 start = end + 1; 74 } while (end != value.npos); 75 } 76 77 response->headers = new HttpResponseHeaders(raw_headers); 78 response->was_fetched_via_spdy = true; 79 return true; 80 } 81 82 void CreateSpdyHeadersFromHttpRequest(const HttpRequestInfo& info, 83 const HttpRequestHeaders& request_headers, 84 SpdyHeaderBlock* headers, 85 int protocol_version, 86 bool direct) { 87 88 HttpRequestHeaders::Iterator it(request_headers); 89 while (it.GetNext()) { 90 std::string name = StringToLowerASCII(it.name()); 91 if (name == "connection" || name == "proxy-connection" || 92 name == "transfer-encoding") { 93 continue; 94 } 95 if (headers->find(name) == headers->end()) { 96 (*headers)[name] = it.value(); 97 } else { 98 std::string new_value = (*headers)[name]; 99 new_value.append(1, '\0'); // +=() doesn't append 0's 100 new_value += it.value(); 101 (*headers)[name] = new_value; 102 } 103 } 104 static const char kHttpProtocolVersion[] = "HTTP/1.1"; 105 106 if (protocol_version < 3) { 107 (*headers)["version"] = kHttpProtocolVersion; 108 (*headers)["method"] = info.method; 109 (*headers)["host"] = GetHostAndOptionalPort(info.url); 110 (*headers)["scheme"] = info.url.scheme(); 111 if (direct) 112 (*headers)["url"] = HttpUtil::PathForRequest(info.url); 113 else 114 (*headers)["url"] = HttpUtil::SpecForRequest(info.url); 115 } else { 116 (*headers)[":version"] = kHttpProtocolVersion; 117 (*headers)[":method"] = info.method; 118 (*headers)[":host"] = GetHostAndOptionalPort(info.url); 119 (*headers)[":scheme"] = info.url.scheme(); 120 (*headers)[":path"] = HttpUtil::PathForRequest(info.url); 121 headers->erase("host"); // this is kinda insane, spdy 3 spec. 122 } 123 124 } 125 126 COMPILE_ASSERT(HIGHEST - LOWEST < 4 && 127 HIGHEST - MINIMUM_PRIORITY < 5, 128 request_priority_incompatible_with_spdy); 129 130 SpdyPriority ConvertRequestPriorityToSpdyPriority( 131 const RequestPriority priority, 132 int protocol_version) { 133 DCHECK_GE(priority, MINIMUM_PRIORITY); 134 DCHECK_LT(priority, NUM_PRIORITIES); 135 if (protocol_version == 2) { 136 // SPDY 2 only has 2 bits of priority, but we have 5 RequestPriorities. 137 // Map IDLE => 3, LOWEST => 2, LOW => 2, MEDIUM => 1, HIGHEST => 0. 138 if (priority > LOWEST) { 139 return static_cast<SpdyPriority>(HIGHEST - priority); 140 } else { 141 return static_cast<SpdyPriority>(HIGHEST - priority - 1); 142 } 143 } else { 144 return static_cast<SpdyPriority>(HIGHEST - priority); 145 } 146 } 147 148 NET_EXPORT_PRIVATE RequestPriority ConvertSpdyPriorityToRequestPriority( 149 SpdyPriority priority, 150 int protocol_version) { 151 // Handle invalid values gracefully, and pick LOW to map 2 back 152 // to for SPDY/2. 153 SpdyPriority idle_cutoff = (protocol_version == 2) ? 3 : 5; 154 return (priority >= idle_cutoff) ? 155 IDLE : static_cast<RequestPriority>(HIGHEST - priority); 156 } 157 158 GURL GetUrlFromHeaderBlock(const SpdyHeaderBlock& headers, 159 int protocol_version, 160 bool pushed) { 161 // SPDY 2 server push urls are specified in a single "url" header. 162 if (pushed && protocol_version == 2) { 163 std::string url; 164 SpdyHeaderBlock::const_iterator it; 165 it = headers.find("url"); 166 if (it != headers.end()) 167 url = it->second; 168 return GURL(url); 169 } 170 171 const char* scheme_header = protocol_version >= 3 ? ":scheme" : "scheme"; 172 const char* host_header = protocol_version >= 3 ? ":host" : "host"; 173 const char* path_header = protocol_version >= 3 ? ":path" : "url"; 174 175 std::string scheme; 176 std::string host_port; 177 std::string path; 178 SpdyHeaderBlock::const_iterator it; 179 it = headers.find(scheme_header); 180 if (it != headers.end()) 181 scheme = it->second; 182 it = headers.find(host_header); 183 if (it != headers.end()) 184 host_port = it->second; 185 it = headers.find(path_header); 186 if (it != headers.end()) 187 path = it->second; 188 189 std::string url = (scheme.empty() || host_port.empty() || path.empty()) 190 ? std::string() 191 : scheme + "://" + host_port + path; 192 return GURL(url); 193 } 194 195 bool ShouldShowHttpHeaderValue(const std::string& header_name) { 196 #if defined(SPDY_PROXY_AUTH_ORIGIN) 197 if (header_name == "proxy-authorization") 198 return false; 199 #endif 200 return true; 201 } 202 203 } // namespace net 204