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