spdy_http_utils.cc 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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. #include "net/spdy/spdy_http_utils.h"
  5. #include <string>
  6. #include <vector>
  7. #include "base/strings/abseil_string_conversions.h"
  8. #include "base/strings/escape.h"
  9. #include "base/strings/strcat.h"
  10. #include "base/strings/string_number_conversions.h"
  11. #include "base/strings/string_piece.h"
  12. #include "base/strings/string_split.h"
  13. #include "base/strings/string_util.h"
  14. #include "base/time/time.h"
  15. #include "net/base/load_flags.h"
  16. #include "net/base/url_util.h"
  17. #include "net/http/http_request_headers.h"
  18. #include "net/http/http_request_info.h"
  19. #include "net/http/http_response_headers.h"
  20. #include "net/http/http_response_info.h"
  21. #include "net/http/http_util.h"
  22. namespace net {
  23. namespace {
  24. void AddSpdyHeader(const std::string& name,
  25. const std::string& value,
  26. spdy::Http2HeaderBlock* headers) {
  27. if (headers->find(name) == headers->end()) {
  28. (*headers)[name] = value;
  29. } else {
  30. (*headers)[name] = base::StrCat(
  31. {(*headers)[name].as_string(), base::StringPiece("\0", 1), value});
  32. }
  33. }
  34. } // namespace
  35. int SpdyHeadersToHttpResponse(const spdy::Http2HeaderBlock& headers,
  36. HttpResponseInfo* response) {
  37. // The ":status" header is required.
  38. spdy::Http2HeaderBlock::const_iterator it =
  39. headers.find(spdy::kHttp2StatusHeader);
  40. if (it == headers.end())
  41. return ERR_INCOMPLETE_HTTP2_HEADERS;
  42. const auto status = base::StringViewToStringPiece(it->second);
  43. std::string raw_headers =
  44. base::StrCat({"HTTP/1.1 ", status, base::StringPiece("\0", 1)});
  45. for (it = headers.begin(); it != headers.end(); ++it) {
  46. const auto name = base::StringViewToStringPiece(it->first);
  47. DCHECK_GT(name.size(), 0u);
  48. if (name[0] == ':') {
  49. // https://tools.ietf.org/html/rfc7540#section-8.1.2.4
  50. // Skip pseudo headers.
  51. continue;
  52. }
  53. // For each value, if the server sends a NUL-separated
  54. // list of values, we separate that back out into
  55. // individual headers for each value in the list.
  56. // e.g.
  57. // Set-Cookie "foo\0bar"
  58. // becomes
  59. // Set-Cookie: foo\0
  60. // Set-Cookie: bar\0
  61. const auto value = base::StringViewToStringPiece(it->second);
  62. size_t start = 0;
  63. size_t end = 0;
  64. do {
  65. end = value.find('\0', start);
  66. base::StringPiece tval;
  67. if (end != value.npos)
  68. tval = value.substr(start, (end - start));
  69. else
  70. tval = value.substr(start);
  71. base::StrAppend(&raw_headers,
  72. {name, ":", tval, base::StringPiece("\0", 1)});
  73. start = end + 1;
  74. } while (end != value.npos);
  75. }
  76. response->headers = base::MakeRefCounted<HttpResponseHeaders>(raw_headers);
  77. // When there are multiple location headers the response is a potential
  78. // response smuggling attack.
  79. if (HttpUtil::HeadersContainMultipleCopiesOfField(*response->headers,
  80. "location")) {
  81. return ERR_RESPONSE_HEADERS_MULTIPLE_LOCATION;
  82. }
  83. response->was_fetched_via_spdy = true;
  84. return OK;
  85. }
  86. void CreateSpdyHeadersFromHttpRequest(const HttpRequestInfo& info,
  87. const HttpRequestHeaders& request_headers,
  88. spdy::Http2HeaderBlock* headers) {
  89. (*headers)[spdy::kHttp2MethodHeader] = info.method;
  90. if (info.method == "CONNECT") {
  91. (*headers)[spdy::kHttp2AuthorityHeader] = GetHostAndPort(info.url);
  92. } else {
  93. (*headers)[spdy::kHttp2AuthorityHeader] = GetHostAndOptionalPort(info.url);
  94. (*headers)[spdy::kHttp2SchemeHeader] = info.url.scheme();
  95. (*headers)[spdy::kHttp2PathHeader] = info.url.PathForRequest();
  96. }
  97. HttpRequestHeaders::Iterator it(request_headers);
  98. while (it.GetNext()) {
  99. std::string name = base::ToLowerASCII(it.name());
  100. if (name.empty() || name[0] == ':' || name == "connection" ||
  101. name == "proxy-connection" || name == "transfer-encoding" ||
  102. name == "host") {
  103. continue;
  104. }
  105. AddSpdyHeader(name, it.value(), headers);
  106. }
  107. }
  108. void CreateSpdyHeadersFromHttpRequestForWebSocket(
  109. const GURL& url,
  110. const HttpRequestHeaders& request_headers,
  111. spdy::Http2HeaderBlock* headers) {
  112. (*headers)[spdy::kHttp2MethodHeader] = "CONNECT";
  113. (*headers)[spdy::kHttp2AuthorityHeader] = GetHostAndOptionalPort(url);
  114. (*headers)[spdy::kHttp2SchemeHeader] = "https";
  115. (*headers)[spdy::kHttp2PathHeader] = url.PathForRequest();
  116. (*headers)[spdy::kHttp2ProtocolHeader] = "websocket";
  117. HttpRequestHeaders::Iterator it(request_headers);
  118. while (it.GetNext()) {
  119. std::string name = base::ToLowerASCII(it.name());
  120. if (name.empty() || name[0] == ':' || name == "upgrade" ||
  121. name == "connection" || name == "proxy-connection" ||
  122. name == "transfer-encoding" || name == "host") {
  123. continue;
  124. }
  125. AddSpdyHeader(name, it.value(), headers);
  126. }
  127. }
  128. static_assert(HIGHEST - LOWEST < 4 && HIGHEST - MINIMUM_PRIORITY < 6,
  129. "request priority incompatible with spdy");
  130. spdy::SpdyPriority ConvertRequestPriorityToSpdyPriority(
  131. const RequestPriority priority) {
  132. DCHECK_GE(priority, MINIMUM_PRIORITY);
  133. DCHECK_LE(priority, MAXIMUM_PRIORITY);
  134. return static_cast<spdy::SpdyPriority>(MAXIMUM_PRIORITY - priority +
  135. spdy::kV3HighestPriority);
  136. }
  137. NET_EXPORT_PRIVATE RequestPriority
  138. ConvertSpdyPriorityToRequestPriority(spdy::SpdyPriority priority) {
  139. // Handle invalid values gracefully.
  140. return ((priority - spdy::kV3HighestPriority) >
  141. (MAXIMUM_PRIORITY - MINIMUM_PRIORITY))
  142. ? IDLE
  143. : static_cast<RequestPriority>(
  144. MAXIMUM_PRIORITY - (priority - spdy::kV3HighestPriority));
  145. }
  146. NET_EXPORT_PRIVATE void ConvertHeaderBlockToHttpRequestHeaders(
  147. const spdy::Http2HeaderBlock& spdy_headers,
  148. HttpRequestHeaders* http_headers) {
  149. for (const auto& it : spdy_headers) {
  150. base::StringPiece key = base::StringViewToStringPiece(it.first);
  151. if (key[0] == ':') {
  152. key.remove_prefix(1);
  153. }
  154. std::vector<base::StringPiece> values =
  155. base::SplitStringPiece(base::StringViewToStringPiece(it.second), "\0",
  156. base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
  157. for (const auto& value : values) {
  158. http_headers->SetHeader(key, value);
  159. }
  160. }
  161. }
  162. } // namespace net