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                                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