mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 22:36:09 +03:00
152 lines
4.9 KiB
C++
152 lines
4.9 KiB
C++
|
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
// found in the LICENSE file.
|
||
|
|
||
|
#include "net/server/web_socket.h"
|
||
|
|
||
|
#include <vector>
|
||
|
|
||
|
#include "base/base64.h"
|
||
|
#include "base/logging.h"
|
||
|
#include "base/sha1.h"
|
||
|
#include "base/strings/string_number_conversions.h"
|
||
|
#include "base/strings/stringprintf.h"
|
||
|
#include "base/sys_byteorder.h"
|
||
|
#include "net/server/http_connection.h"
|
||
|
#include "net/server/http_server.h"
|
||
|
#include "net/server/http_server_request_info.h"
|
||
|
#include "net/server/http_server_response_info.h"
|
||
|
#include "net/server/web_socket_encoder.h"
|
||
|
#include "net/websockets/websocket_deflate_parameters.h"
|
||
|
#include "net/websockets/websocket_extension.h"
|
||
|
#include "net/websockets/websocket_handshake_constants.h"
|
||
|
|
||
|
namespace net {
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
std::string ExtensionsHeaderString(
|
||
|
const std::vector<WebSocketExtension>& extensions) {
|
||
|
if (extensions.empty())
|
||
|
return std::string();
|
||
|
|
||
|
std::string result = "Sec-WebSocket-Extensions: " + extensions[0].ToString();
|
||
|
for (size_t i = 1; i < extensions.size(); ++i)
|
||
|
result += ", " + extensions[i].ToString();
|
||
|
return result + "\r\n";
|
||
|
}
|
||
|
|
||
|
std::string ValidResponseString(
|
||
|
const std::string& accept_hash,
|
||
|
const std::vector<WebSocketExtension> extensions) {
|
||
|
return base::StringPrintf(
|
||
|
"HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
|
||
|
"Upgrade: WebSocket\r\n"
|
||
|
"Connection: Upgrade\r\n"
|
||
|
"Sec-WebSocket-Accept: %s\r\n"
|
||
|
"%s"
|
||
|
"\r\n",
|
||
|
accept_hash.c_str(), ExtensionsHeaderString(extensions).c_str());
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
WebSocket::WebSocket(HttpServer* server, HttpConnection* connection)
|
||
|
: server_(server), connection_(connection), closed_(false) {}
|
||
|
|
||
|
WebSocket::~WebSocket() = default;
|
||
|
|
||
|
void WebSocket::Accept(const HttpServerRequestInfo& request,
|
||
|
const NetworkTrafficAnnotationTag traffic_annotation) {
|
||
|
std::string version = request.GetHeaderValue("sec-websocket-version");
|
||
|
if (version != "8" && version != "13") {
|
||
|
SendErrorResponse("Invalid request format. The version is not valid.",
|
||
|
traffic_annotation);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
std::string key = request.GetHeaderValue("sec-websocket-key");
|
||
|
if (key.empty()) {
|
||
|
SendErrorResponse(
|
||
|
"Invalid request format. Sec-WebSocket-Key is empty or isn't "
|
||
|
"specified.",
|
||
|
traffic_annotation);
|
||
|
return;
|
||
|
}
|
||
|
std::string encoded_hash;
|
||
|
base::Base64Encode(base::SHA1HashString(key + websockets::kWebSocketGuid),
|
||
|
&encoded_hash);
|
||
|
|
||
|
std::vector<WebSocketExtension> response_extensions;
|
||
|
auto i = request.headers.find("sec-websocket-extensions");
|
||
|
if (i == request.headers.end()) {
|
||
|
encoder_ = WebSocketEncoder::CreateServer();
|
||
|
} else {
|
||
|
WebSocketDeflateParameters params;
|
||
|
encoder_ = WebSocketEncoder::CreateServer(i->second, ¶ms);
|
||
|
if (!encoder_) {
|
||
|
Fail();
|
||
|
return;
|
||
|
}
|
||
|
if (encoder_->deflate_enabled()) {
|
||
|
DCHECK(params.IsValidAsResponse());
|
||
|
response_extensions.push_back(params.AsExtension());
|
||
|
}
|
||
|
}
|
||
|
server_->SendRaw(connection_->id(),
|
||
|
ValidResponseString(encoded_hash, response_extensions),
|
||
|
traffic_annotation);
|
||
|
}
|
||
|
|
||
|
WebSocket::ParseResult WebSocket::Read(std::string* message) {
|
||
|
if (closed_)
|
||
|
return FRAME_CLOSE;
|
||
|
|
||
|
if (!encoder_) {
|
||
|
// RFC6455, section 4.1 says "Once the client's opening handshake has been
|
||
|
// sent, the client MUST wait for a response from the server before sending
|
||
|
// any further data". If |encoder_| is null here, ::Accept either has not
|
||
|
// been called at all, or has rejected a request rather than producing
|
||
|
// a server handshake. Either way, the client clearly couldn't have gotten
|
||
|
// a proper server handshake, so error out, especially since this method
|
||
|
// can't proceed without an |encoder_|.
|
||
|
return FRAME_ERROR;
|
||
|
}
|
||
|
|
||
|
HttpConnection::ReadIOBuffer* read_buf = connection_->read_buf();
|
||
|
base::StringPiece frame(read_buf->StartOfBuffer(), read_buf->GetSize());
|
||
|
int bytes_consumed = 0;
|
||
|
ParseResult result = encoder_->DecodeFrame(frame, &bytes_consumed, message);
|
||
|
if (result == FRAME_OK)
|
||
|
read_buf->DidConsume(bytes_consumed);
|
||
|
if (result == FRAME_CLOSE)
|
||
|
closed_ = true;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
void WebSocket::Send(const std::string& message,
|
||
|
const NetworkTrafficAnnotationTag traffic_annotation) {
|
||
|
if (closed_)
|
||
|
return;
|
||
|
std::string encoded;
|
||
|
encoder_->EncodeFrame(message, 0, &encoded);
|
||
|
server_->SendRaw(connection_->id(), encoded, traffic_annotation);
|
||
|
}
|
||
|
|
||
|
void WebSocket::Fail() {
|
||
|
closed_ = true;
|
||
|
// TODO(yhirano): The server SHOULD log the problem.
|
||
|
server_->Close(connection_->id());
|
||
|
}
|
||
|
|
||
|
void WebSocket::SendErrorResponse(
|
||
|
const std::string& message,
|
||
|
const NetworkTrafficAnnotationTag traffic_annotation) {
|
||
|
if (closed_)
|
||
|
return;
|
||
|
closed_ = true;
|
||
|
server_->Send500(connection_->id(), message, traffic_annotation);
|
||
|
}
|
||
|
|
||
|
} // namespace net
|