| Index: net/websockets/websocket_handshake_handler.cc
|
| diff --git a/net/websockets/websocket_handshake_handler.cc b/net/websockets/websocket_handshake_handler.cc
|
| index 63247109b689c2c8cf6df6c60bdbc4ee1e6dea2c..6bcc2304cc057a7a579936cd4dbd530eb33de140 100644
|
| --- a/net/websockets/websocket_handshake_handler.cc
|
| +++ b/net/websockets/websocket_handshake_handler.cc
|
| @@ -4,346 +4,12 @@
|
|
|
| #include "net/websockets/websocket_handshake_handler.h"
|
|
|
| -#include <limits>
|
| -
|
| #include "base/base64.h"
|
| +#include "base/logging.h"
|
| #include "base/sha1.h"
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "base/strings/string_piece.h"
|
| -#include "base/strings/string_tokenizer.h"
|
| -#include "base/strings/string_util.h"
|
| -#include "base/strings/stringprintf.h"
|
| -#include "net/http/http_request_headers.h"
|
| -#include "net/http/http_response_headers.h"
|
| -#include "net/http/http_util.h"
|
| #include "net/websockets/websocket_handshake_constants.h"
|
| -#include "url/gurl.h"
|
|
|
| namespace net {
|
| -namespace {
|
| -
|
| -const int kVersionHeaderValueForRFC6455 = 13;
|
| -
|
| -// Splits |handshake_message| into Status-Line or Request-Line (including CRLF)
|
| -// and headers (excluding 2nd CRLF of double CRLFs at the end of a handshake
|
| -// response).
|
| -void ParseHandshakeHeader(
|
| - const char* handshake_message, int len,
|
| - std::string* request_line,
|
| - std::string* headers) {
|
| - size_t i = base::StringPiece(handshake_message, len).find_first_of("\r\n");
|
| - if (i == base::StringPiece::npos) {
|
| - *request_line = std::string(handshake_message, len);
|
| - *headers = "";
|
| - return;
|
| - }
|
| - // |request_line| includes \r\n.
|
| - *request_line = std::string(handshake_message, i + 2);
|
| -
|
| - int header_len = len - (i + 2) - 2;
|
| - if (header_len > 0) {
|
| - // |handshake_message| includes trailing \r\n\r\n.
|
| - // |headers| doesn't include 2nd \r\n.
|
| - *headers = std::string(handshake_message + i + 2, header_len);
|
| - } else {
|
| - *headers = "";
|
| - }
|
| -}
|
| -
|
| -void FetchHeaders(const std::string& headers,
|
| - const char* const headers_to_get[],
|
| - size_t headers_to_get_len,
|
| - std::vector<std::string>* values) {
|
| - net::HttpUtil::HeadersIterator iter(headers.begin(), headers.end(), "\r\n");
|
| - while (iter.GetNext()) {
|
| - for (size_t i = 0; i < headers_to_get_len; i++) {
|
| - if (LowerCaseEqualsASCII(iter.name_begin(), iter.name_end(),
|
| - headers_to_get[i])) {
|
| - values->push_back(iter.values());
|
| - }
|
| - }
|
| - }
|
| -}
|
| -
|
| -bool GetHeaderName(std::string::const_iterator line_begin,
|
| - std::string::const_iterator line_end,
|
| - std::string::const_iterator* name_begin,
|
| - std::string::const_iterator* name_end) {
|
| - std::string::const_iterator colon = std::find(line_begin, line_end, ':');
|
| - if (colon == line_end) {
|
| - return false;
|
| - }
|
| - *name_begin = line_begin;
|
| - *name_end = colon;
|
| - if (*name_begin == *name_end || net::HttpUtil::IsLWS(**name_begin))
|
| - return false;
|
| - net::HttpUtil::TrimLWS(name_begin, name_end);
|
| - return true;
|
| -}
|
| -
|
| -// Similar to HttpUtil::StripHeaders, but it preserves malformed headers, that
|
| -// is, lines that are not formatted as "<name>: <value>\r\n".
|
| -std::string FilterHeaders(
|
| - const std::string& headers,
|
| - const char* const headers_to_remove[],
|
| - size_t headers_to_remove_len) {
|
| - std::string filtered_headers;
|
| -
|
| - base::StringTokenizer lines(headers.begin(), headers.end(), "\r\n");
|
| - while (lines.GetNext()) {
|
| - std::string::const_iterator line_begin = lines.token_begin();
|
| - std::string::const_iterator line_end = lines.token_end();
|
| - std::string::const_iterator name_begin;
|
| - std::string::const_iterator name_end;
|
| - bool should_remove = false;
|
| - if (GetHeaderName(line_begin, line_end, &name_begin, &name_end)) {
|
| - for (size_t i = 0; i < headers_to_remove_len; ++i) {
|
| - if (LowerCaseEqualsASCII(name_begin, name_end, headers_to_remove[i])) {
|
| - should_remove = true;
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - if (!should_remove) {
|
| - filtered_headers.append(line_begin, line_end);
|
| - filtered_headers.append("\r\n");
|
| - }
|
| - }
|
| - return filtered_headers;
|
| -}
|
| -
|
| -bool CheckVersionInRequest(const std::string& request_headers) {
|
| - std::vector<std::string> values;
|
| - const char* const headers_to_get[1] = {
|
| - websockets::kSecWebSocketVersionLowercase};
|
| - FetchHeaders(request_headers, headers_to_get, 1, &values);
|
| - DCHECK_LE(values.size(), 1U);
|
| - if (values.empty())
|
| - return false;
|
| -
|
| - int version;
|
| - bool conversion_success = base::StringToInt(values[0], &version);
|
| - if (!conversion_success)
|
| - return false;
|
| -
|
| - return version == kVersionHeaderValueForRFC6455;
|
| -}
|
| -
|
| -// Append a header to a string. Equivalent to
|
| -// response_message += header + ": " + value + "\r\n"
|
| -// but avoids unnecessary allocations and copies.
|
| -void AppendHeader(const base::StringPiece& header,
|
| - const base::StringPiece& value,
|
| - std::string* response_message) {
|
| - static const char kColonSpace[] = ": ";
|
| - const size_t kColonSpaceSize = sizeof(kColonSpace) - 1;
|
| - static const char kCrNl[] = "\r\n";
|
| - const size_t kCrNlSize = sizeof(kCrNl) - 1;
|
| -
|
| - size_t extra_size =
|
| - header.size() + kColonSpaceSize + value.size() + kCrNlSize;
|
| - response_message->reserve(response_message->size() + extra_size);
|
| - response_message->append(header.begin(), header.end());
|
| - response_message->append(kColonSpace, kColonSpace + kColonSpaceSize);
|
| - response_message->append(value.begin(), value.end());
|
| - response_message->append(kCrNl, kCrNl + kCrNlSize);
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -WebSocketHandshakeRequestHandler::WebSocketHandshakeRequestHandler()
|
| - : original_length_(0),
|
| - raw_length_(0) {}
|
| -
|
| -bool WebSocketHandshakeRequestHandler::ParseRequest(
|
| - const char* data, int length) {
|
| - DCHECK_GT(length, 0);
|
| - std::string input(data, length);
|
| - int input_header_length =
|
| - HttpUtil::LocateEndOfHeaders(input.data(), input.size(), 0);
|
| - if (input_header_length <= 0)
|
| - return false;
|
| -
|
| - ParseHandshakeHeader(input.data(),
|
| - input_header_length,
|
| - &request_line_,
|
| - &headers_);
|
| -
|
| - if (!CheckVersionInRequest(headers_)) {
|
| - NOTREACHED();
|
| - return false;
|
| - }
|
| -
|
| - original_length_ = input_header_length;
|
| - return true;
|
| -}
|
| -
|
| -size_t WebSocketHandshakeRequestHandler::original_length() const {
|
| - return original_length_;
|
| -}
|
| -
|
| -void WebSocketHandshakeRequestHandler::AppendHeaderIfMissing(
|
| - const std::string& name, const std::string& value) {
|
| - DCHECK(!headers_.empty());
|
| - HttpUtil::AppendHeaderIfMissing(name.c_str(), value, &headers_);
|
| -}
|
| -
|
| -void WebSocketHandshakeRequestHandler::RemoveHeaders(
|
| - const char* const headers_to_remove[],
|
| - size_t headers_to_remove_len) {
|
| - DCHECK(!headers_.empty());
|
| - headers_ = FilterHeaders(
|
| - headers_, headers_to_remove, headers_to_remove_len);
|
| -}
|
| -
|
| -HttpRequestInfo WebSocketHandshakeRequestHandler::GetRequestInfo(
|
| - const GURL& url, std::string* challenge) {
|
| - HttpRequestInfo request_info;
|
| - request_info.url = url;
|
| - size_t method_end = base::StringPiece(request_line_).find_first_of(" ");
|
| - if (method_end != base::StringPiece::npos)
|
| - request_info.method = std::string(request_line_.data(), method_end);
|
| -
|
| - request_info.extra_headers.Clear();
|
| - request_info.extra_headers.AddHeadersFromString(headers_);
|
| -
|
| - request_info.extra_headers.RemoveHeader(websockets::kUpgrade);
|
| - request_info.extra_headers.RemoveHeader(HttpRequestHeaders::kConnection);
|
| -
|
| - std::string key;
|
| - bool header_present = request_info.extra_headers.GetHeader(
|
| - websockets::kSecWebSocketKey, &key);
|
| - DCHECK(header_present);
|
| - request_info.extra_headers.RemoveHeader(websockets::kSecWebSocketKey);
|
| - *challenge = key;
|
| - return request_info;
|
| -}
|
| -
|
| -bool WebSocketHandshakeRequestHandler::GetRequestHeaderBlock(
|
| - const GURL& url,
|
| - SpdyHeaderBlock* headers,
|
| - std::string* challenge,
|
| - int spdy_protocol_version) {
|
| - // Construct opening handshake request headers as a SPDY header block.
|
| - // For details, see WebSocket Layering over SPDY/3 Draft 8.
|
| - if (spdy_protocol_version <= 2) {
|
| - (*headers)["path"] = url.path();
|
| - (*headers)["version"] = "WebSocket/13";
|
| - (*headers)["scheme"] = url.scheme();
|
| - } else {
|
| - (*headers)[":path"] = url.path();
|
| - (*headers)[":version"] = "WebSocket/13";
|
| - (*headers)[":scheme"] = url.scheme();
|
| - }
|
| -
|
| - HttpUtil::HeadersIterator iter(headers_.begin(), headers_.end(), "\r\n");
|
| - while (iter.GetNext()) {
|
| - if (LowerCaseEqualsASCII(iter.name_begin(),
|
| - iter.name_end(),
|
| - websockets::kUpgradeLowercase) ||
|
| - LowerCaseEqualsASCII(
|
| - iter.name_begin(), iter.name_end(), "connection") ||
|
| - LowerCaseEqualsASCII(iter.name_begin(),
|
| - iter.name_end(),
|
| - websockets::kSecWebSocketVersionLowercase)) {
|
| - // These headers must be ignored.
|
| - continue;
|
| - } else if (LowerCaseEqualsASCII(iter.name_begin(),
|
| - iter.name_end(),
|
| - websockets::kSecWebSocketKeyLowercase)) {
|
| - *challenge = iter.values();
|
| - // Sec-WebSocket-Key is not sent to the server.
|
| - continue;
|
| - } else if (LowerCaseEqualsASCII(
|
| - iter.name_begin(), iter.name_end(), "host") ||
|
| - LowerCaseEqualsASCII(
|
| - iter.name_begin(), iter.name_end(), "origin") ||
|
| - LowerCaseEqualsASCII(
|
| - iter.name_begin(),
|
| - iter.name_end(),
|
| - websockets::kSecWebSocketProtocolLowercase) ||
|
| - LowerCaseEqualsASCII(
|
| - iter.name_begin(),
|
| - iter.name_end(),
|
| - websockets::kSecWebSocketExtensionsLowercase)) {
|
| - // TODO(toyoshim): Some WebSocket extensions may not be compatible with
|
| - // SPDY. We should omit them from a Sec-WebSocket-Extension header.
|
| - std::string name;
|
| - if (spdy_protocol_version <= 2)
|
| - name = base::StringToLowerASCII(iter.name());
|
| - else
|
| - name = ":" + base::StringToLowerASCII(iter.name());
|
| - (*headers)[name] = iter.values();
|
| - continue;
|
| - }
|
| - // Others should be sent out to |headers|.
|
| - std::string name = base::StringToLowerASCII(iter.name());
|
| - SpdyHeaderBlock::iterator found = headers->find(name);
|
| - if (found == headers->end()) {
|
| - (*headers)[name] = iter.values();
|
| - } else {
|
| - // For now, websocket doesn't use multiple headers, but follows to http.
|
| - found->second.append(1, '\0'); // +=() doesn't append 0's
|
| - found->second.append(iter.values());
|
| - }
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -std::string WebSocketHandshakeRequestHandler::GetRawRequest() {
|
| - DCHECK(!request_line_.empty());
|
| - DCHECK(!headers_.empty());
|
| -
|
| - std::string raw_request = request_line_ + headers_ + "\r\n";
|
| - raw_length_ = raw_request.size();
|
| - return raw_request;
|
| -}
|
| -
|
| -size_t WebSocketHandshakeRequestHandler::raw_length() const {
|
| - DCHECK_GT(raw_length_, 0);
|
| - return raw_length_;
|
| -}
|
| -
|
| -WebSocketHandshakeResponseHandler::WebSocketHandshakeResponseHandler()
|
| - : original_header_length_(0) {}
|
| -
|
| -WebSocketHandshakeResponseHandler::~WebSocketHandshakeResponseHandler() {}
|
| -
|
| -size_t WebSocketHandshakeResponseHandler::ParseRawResponse(
|
| - const char* data, int length) {
|
| - DCHECK_GT(length, 0);
|
| - if (HasResponse()) {
|
| - DCHECK(!status_line_.empty());
|
| - // headers_ might be empty for wrong response from server.
|
| -
|
| - return 0;
|
| - }
|
| -
|
| - size_t old_original_length = original_.size();
|
| -
|
| - original_.append(data, length);
|
| - // TODO(ukai): fail fast when response gives wrong status code.
|
| - original_header_length_ = HttpUtil::LocateEndOfHeaders(
|
| - original_.data(), original_.size(), 0);
|
| - if (!HasResponse())
|
| - return length;
|
| -
|
| - ParseHandshakeHeader(original_.data(),
|
| - original_header_length_,
|
| - &status_line_,
|
| - &headers_);
|
| - int header_size = status_line_.size() + headers_.size();
|
| - DCHECK_GE(original_header_length_, header_size);
|
| - header_separator_ = std::string(original_.data() + header_size,
|
| - original_header_length_ - header_size);
|
| - return original_header_length_ - old_original_length;
|
| -}
|
| -
|
| -bool WebSocketHandshakeResponseHandler::HasResponse() const {
|
| - return original_header_length_ > 0 &&
|
| - static_cast<size_t>(original_header_length_) <= original_.size();
|
| -}
|
|
|
| void ComputeSecWebSocketAccept(const std::string& key,
|
| std::string* accept) {
|
| @@ -354,145 +20,4 @@ void ComputeSecWebSocketAccept(const std::string& key,
|
| base::Base64Encode(hash, accept);
|
| }
|
|
|
| -bool WebSocketHandshakeResponseHandler::ParseResponseInfo(
|
| - const HttpResponseInfo& response_info,
|
| - const std::string& challenge) {
|
| - if (!response_info.headers.get())
|
| - return false;
|
| -
|
| - // TODO(ricea): Eliminate all the reallocations and string copies.
|
| - std::string response_message;
|
| - response_message = response_info.headers->GetStatusLine();
|
| - response_message += "\r\n";
|
| -
|
| - AppendHeader(websockets::kUpgrade,
|
| - websockets::kWebSocketLowercase,
|
| - &response_message);
|
| -
|
| - AppendHeader(
|
| - HttpRequestHeaders::kConnection, websockets::kUpgrade, &response_message);
|
| -
|
| - std::string websocket_accept;
|
| - ComputeSecWebSocketAccept(challenge, &websocket_accept);
|
| - AppendHeader(
|
| - websockets::kSecWebSocketAccept, websocket_accept, &response_message);
|
| -
|
| - void* iter = NULL;
|
| - std::string name;
|
| - std::string value;
|
| - while (response_info.headers->EnumerateHeaderLines(&iter, &name, &value)) {
|
| - AppendHeader(name, value, &response_message);
|
| - }
|
| - response_message += "\r\n";
|
| -
|
| - return ParseRawResponse(response_message.data(),
|
| - response_message.size()) == response_message.size();
|
| -}
|
| -
|
| -bool WebSocketHandshakeResponseHandler::ParseResponseHeaderBlock(
|
| - const SpdyHeaderBlock& headers,
|
| - const std::string& challenge,
|
| - int spdy_protocol_version) {
|
| - SpdyHeaderBlock::const_iterator status;
|
| - if (spdy_protocol_version <= 2)
|
| - status = headers.find("status");
|
| - else
|
| - status = headers.find(":status");
|
| - if (status == headers.end())
|
| - return false;
|
| -
|
| - std::string hash =
|
| - base::SHA1HashString(challenge + websockets::kWebSocketGuid);
|
| - std::string websocket_accept;
|
| - base::Base64Encode(hash, &websocket_accept);
|
| -
|
| - std::string response_message = base::StringPrintf(
|
| - "%s %s\r\n", websockets::kHttpProtocolVersion, status->second.c_str());
|
| -
|
| - AppendHeader(
|
| - websockets::kUpgrade, websockets::kWebSocketLowercase, &response_message);
|
| - AppendHeader(
|
| - HttpRequestHeaders::kConnection, websockets::kUpgrade, &response_message);
|
| - AppendHeader(
|
| - websockets::kSecWebSocketAccept, websocket_accept, &response_message);
|
| -
|
| - for (SpdyHeaderBlock::const_iterator iter = headers.begin();
|
| - iter != headers.end();
|
| - ++iter) {
|
| - // For each value, if the server sends a NUL-separated list of values,
|
| - // we separate that back out into individual headers for each value
|
| - // in the list.
|
| - if ((spdy_protocol_version <= 2 &&
|
| - LowerCaseEqualsASCII(iter->first, "status")) ||
|
| - (spdy_protocol_version >= 3 &&
|
| - LowerCaseEqualsASCII(iter->first, ":status"))) {
|
| - // The status value is already handled as the first line of
|
| - // |response_message|. Just skip here.
|
| - continue;
|
| - }
|
| - const std::string& value = iter->second;
|
| - size_t start = 0;
|
| - size_t end = 0;
|
| - do {
|
| - end = value.find('\0', start);
|
| - std::string tval;
|
| - if (end != std::string::npos)
|
| - tval = value.substr(start, (end - start));
|
| - else
|
| - tval = value.substr(start);
|
| - if (spdy_protocol_version >= 3 &&
|
| - (LowerCaseEqualsASCII(iter->first,
|
| - websockets::kSecWebSocketProtocolSpdy3) ||
|
| - LowerCaseEqualsASCII(iter->first,
|
| - websockets::kSecWebSocketExtensionsSpdy3)))
|
| - AppendHeader(iter->first.substr(1), tval, &response_message);
|
| - else
|
| - AppendHeader(iter->first, tval, &response_message);
|
| - start = end + 1;
|
| - } while (end != std::string::npos);
|
| - }
|
| - response_message += "\r\n";
|
| -
|
| - return ParseRawResponse(response_message.data(),
|
| - response_message.size()) == response_message.size();
|
| -}
|
| -
|
| -void WebSocketHandshakeResponseHandler::GetHeaders(
|
| - const char* const headers_to_get[],
|
| - size_t headers_to_get_len,
|
| - std::vector<std::string>* values) {
|
| - DCHECK(HasResponse());
|
| - DCHECK(!status_line_.empty());
|
| - // headers_ might be empty for wrong response from server.
|
| - if (headers_.empty())
|
| - return;
|
| -
|
| - FetchHeaders(headers_, headers_to_get, headers_to_get_len, values);
|
| -}
|
| -
|
| -void WebSocketHandshakeResponseHandler::RemoveHeaders(
|
| - const char* const headers_to_remove[],
|
| - size_t headers_to_remove_len) {
|
| - DCHECK(HasResponse());
|
| - DCHECK(!status_line_.empty());
|
| - // headers_ might be empty for wrong response from server.
|
| - if (headers_.empty())
|
| - return;
|
| -
|
| - headers_ = FilterHeaders(headers_, headers_to_remove, headers_to_remove_len);
|
| -}
|
| -
|
| -std::string WebSocketHandshakeResponseHandler::GetRawResponse() const {
|
| - DCHECK(HasResponse());
|
| - return original_.substr(0, original_header_length_);
|
| -}
|
| -
|
| -std::string WebSocketHandshakeResponseHandler::GetResponse() {
|
| - DCHECK(HasResponse());
|
| - DCHECK(!status_line_.empty());
|
| - // headers_ might be empty for wrong response from server.
|
| -
|
| - return status_line_ + headers_ + header_separator_;
|
| -}
|
| -
|
| } // namespace net
|
|
|