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