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