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 |