| Index: net/server/web_socket_encoder.cc
|
| diff --git a/net/server/web_socket_encoder.cc b/net/server/web_socket_encoder.cc
|
| index 1a5431affd8415a23e6ee6f3a553064d1447a803..b1b93eed064d8537e05903917e0a1cfdc5cc4173 100644
|
| --- a/net/server/web_socket_encoder.cc
|
| +++ b/net/server/web_socket_encoder.cc
|
| @@ -4,10 +4,14 @@
|
|
|
| #include "net/server/web_socket_encoder.h"
|
|
|
| +#include <vector>
|
| +
|
| #include "base/logging.h"
|
| #include "base/strings/string_number_conversions.h"
|
| #include "base/strings/stringprintf.h"
|
| #include "net/base/io_buffer.h"
|
| +#include "net/websockets/websocket_deflate_parameters.h"
|
| +#include "net/websockets/websocket_extension.h"
|
| #include "net/websockets/websocket_extension_parser.h"
|
|
|
| namespace net {
|
| @@ -180,151 +184,111 @@ void EncodeFrameHybi17(const std::string& message,
|
| } // anonymous namespace
|
|
|
| // static
|
| -WebSocketEncoder* WebSocketEncoder::CreateServer(
|
| - const std::string& request_extensions,
|
| - std::string* response_extensions) {
|
| - bool deflate;
|
| - bool has_client_window_bits;
|
| - int client_window_bits;
|
| - int server_window_bits;
|
| - bool client_no_context_takeover;
|
| - bool server_no_context_takeover;
|
| - ParseExtensions(request_extensions, &deflate, &has_client_window_bits,
|
| - &client_window_bits, &server_window_bits,
|
| - &client_no_context_takeover, &server_no_context_takeover);
|
| -
|
| - if (deflate) {
|
| - *response_extensions = base::StringPrintf(
|
| - "permessage-deflate; server_max_window_bits=%d%s", server_window_bits,
|
| - server_no_context_takeover ? "; server_no_context_takeover" : "");
|
| - if (has_client_window_bits) {
|
| - base::StringAppendF(response_extensions, "; client_max_window_bits=%d",
|
| - client_window_bits);
|
| - } else {
|
| - DCHECK_EQ(client_window_bits, 15);
|
| - }
|
| - return new WebSocketEncoder(true /* is_server */, server_window_bits,
|
| - client_window_bits, server_no_context_takeover);
|
| - } else {
|
| - *response_extensions = std::string();
|
| - return new WebSocketEncoder(true /* is_server */);
|
| - }
|
| +scoped_ptr<WebSocketEncoder> WebSocketEncoder::CreateServer() {
|
| + return make_scoped_ptr(new WebSocketEncoder(FOR_SERVER, nullptr, nullptr));
|
| }
|
|
|
| // static
|
| -WebSocketEncoder* WebSocketEncoder::CreateClient(
|
| - const std::string& response_extensions) {
|
| - bool deflate;
|
| - bool has_client_window_bits;
|
| - int client_window_bits;
|
| - int server_window_bits;
|
| - bool client_no_context_takeover;
|
| - bool server_no_context_takeover;
|
| - ParseExtensions(response_extensions, &deflate, &has_client_window_bits,
|
| - &client_window_bits, &server_window_bits,
|
| - &client_no_context_takeover, &server_no_context_takeover);
|
| -
|
| - if (deflate) {
|
| - return new WebSocketEncoder(false /* is_server */, client_window_bits,
|
| - server_window_bits, client_no_context_takeover);
|
| - } else {
|
| - return new WebSocketEncoder(false /* is_server */);
|
| +scoped_ptr<WebSocketEncoder> WebSocketEncoder::CreateServer(
|
| + const std::string& extensions,
|
| + WebSocketDeflateParameters* deflate_parameters) {
|
| + WebSocketExtensionParser parser;
|
| + if (!parser.Parse(extensions)) {
|
| + // Failed to parse Sec-WebSocket-Extensions header. We MUST fail the
|
| + // connection.
|
| + return nullptr;
|
| }
|
| -}
|
| -
|
| -// static
|
| -void WebSocketEncoder::ParseExtensions(const std::string& header_value,
|
| - bool* deflate,
|
| - bool* has_client_window_bits,
|
| - int* client_window_bits,
|
| - int* server_window_bits,
|
| - bool* client_no_context_takeover,
|
| - bool* server_no_context_takeover) {
|
| - *deflate = false;
|
| - *has_client_window_bits = false;
|
| - *client_window_bits = 15;
|
| - *server_window_bits = 15;
|
| - *client_no_context_takeover = false;
|
| - *server_no_context_takeover = false;
|
| -
|
| - if (header_value.empty())
|
| - return;
|
|
|
| - WebSocketExtensionParser parser;
|
| - if (!parser.Parse(header_value))
|
| - return;
|
| - const std::vector<WebSocketExtension>& extensions = parser.extensions();
|
| - // TODO(tyoshino): Fail if this method is used for parsing a response and
|
| - // there are multiple permessage-deflate extensions or there are any unknown
|
| - // extensions.
|
| - for (const auto& extension : extensions) {
|
| - if (extension.name() != "permessage-deflate") {
|
| + for (const auto& extension : parser.extensions()) {
|
| + std::string failure_message;
|
| + WebSocketDeflateParameters offer;
|
| + if (!offer.Initialize(extension, &failure_message) ||
|
| + !offer.IsValidAsRequest(&failure_message)) {
|
| + // We decline unknown / malformed extensions.
|
| continue;
|
| }
|
|
|
| - const std::vector<WebSocketExtension::Parameter>& parameters =
|
| - extension.parameters();
|
| - for (const auto& param : parameters) {
|
| - const std::string& name = param.name();
|
| - // TODO(tyoshino): Fail the connection when an invalid value is given.
|
| - if (name == "client_max_window_bits") {
|
| - *has_client_window_bits = true;
|
| - if (param.HasValue()) {
|
| - int bits = 0;
|
| - if (base::StringToInt(param.value(), &bits) && bits >= 8 &&
|
| - bits <= 15) {
|
| - *client_window_bits = bits;
|
| - }
|
| - }
|
| - }
|
| - if (name == "server_max_window_bits" && param.HasValue()) {
|
| - int bits = 0;
|
| - if (base::StringToInt(param.value(), &bits) && bits >= 8 && bits <= 15)
|
| - *server_window_bits = bits;
|
| - }
|
| - if (name == "client_no_context_takeover")
|
| - *client_no_context_takeover = true;
|
| - if (name == "server_no_context_takeover")
|
| - *server_no_context_takeover = true;
|
| + WebSocketDeflateParameters response = offer;
|
| + if (offer.is_client_max_window_bits_specified() &&
|
| + !offer.has_client_max_window_bits_value()) {
|
| + // We need to choose one value for the response.
|
| + response.SetClientMaxWindowBits(15);
|
| }
|
| - *deflate = true;
|
| -
|
| - break;
|
| + DCHECK(response.IsValidAsResponse());
|
| + DCHECK(offer.IsCompatibleWith(response));
|
| + auto deflater = make_scoped_ptr(
|
| + new WebSocketDeflater(response.server_context_take_over_mode()));
|
| + auto inflater = make_scoped_ptr(
|
| + new WebSocketInflater(kInflaterChunkSize, kInflaterChunkSize));
|
| + if (!deflater->Initialize(response.PermissiveServerMaxWindowBits()) ||
|
| + !inflater->Initialize(response.PermissiveClientMaxWindowBits())) {
|
| + // For some reason we cannot accept the parameters.
|
| + continue;
|
| + }
|
| + *deflate_parameters = response;
|
| + return make_scoped_ptr(
|
| + new WebSocketEncoder(FOR_SERVER, deflater.Pass(), inflater.Pass()));
|
| }
|
| -}
|
|
|
| -WebSocketEncoder::WebSocketEncoder(bool is_server) : is_server_(is_server) {
|
| + // We cannot find an acceptable offer.
|
| + return make_scoped_ptr(new WebSocketEncoder(FOR_SERVER, nullptr, nullptr));
|
| }
|
|
|
| -WebSocketEncoder::WebSocketEncoder(bool is_server,
|
| - int deflate_bits,
|
| - int inflate_bits,
|
| - bool no_context_takeover)
|
| - : is_server_(is_server) {
|
| - deflater_.reset(new WebSocketDeflater(
|
| - no_context_takeover ? WebSocketDeflater::DO_NOT_TAKE_OVER_CONTEXT
|
| - : WebSocketDeflater::TAKE_OVER_CONTEXT));
|
| - inflater_.reset(
|
| - new WebSocketInflater(kInflaterChunkSize, kInflaterChunkSize));
|
| +// static
|
| +WebSocketEncoder* WebSocketEncoder::CreateClient(
|
| + const std::string& response_extensions) {
|
| + // TODO(yhirano): Add a way to return an error.
|
|
|
| - if (!deflater_->Initialize(deflate_bits) ||
|
| - !inflater_->Initialize(inflate_bits)) {
|
| - // Disable deflate support.
|
| - deflater_.reset();
|
| - inflater_.reset();
|
| + WebSocketExtensionParser parser;
|
| + if (!parser.Parse(response_extensions)) {
|
| + // Parse error. Note that there are two cases here.
|
| + // 1) There is no Sec-WebSocket-Extensions header.
|
| + // 2) There is a malformed Sec-WebSocketExtensions header.
|
| + // We should return a deflate-disabled encoder for the former case and
|
| + // fail the connection for the latter case.
|
| + return new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr);
|
| + }
|
| + if (parser.extensions().size() != 1) {
|
| + // Only permessage-deflate extension is supported.
|
| + // TODO (yhirano): Fail the connection.
|
| + return new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr);
|
| + }
|
| + const auto& extension = parser.extensions()[0];
|
| + WebSocketDeflateParameters params;
|
| + std::string failure_message;
|
| + if (!params.Initialize(extension, &failure_message) ||
|
| + !params.IsValidAsResponse(&failure_message)) {
|
| + // TODO (yhirano): Fail the connection.
|
| + return new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr);
|
| }
|
| -}
|
|
|
| -WebSocketEncoder::~WebSocketEncoder() {
|
| + auto deflater = make_scoped_ptr(
|
| + new WebSocketDeflater(params.client_context_take_over_mode()));
|
| + auto inflater = make_scoped_ptr(
|
| + new WebSocketInflater(kInflaterChunkSize, kInflaterChunkSize));
|
| + if (!deflater->Initialize(params.PermissiveClientMaxWindowBits()) ||
|
| + !inflater->Initialize(params.PermissiveServerMaxWindowBits())) {
|
| + // TODO (yhirano): Fail the connection.
|
| + return new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr);
|
| + }
|
| +
|
| + return new WebSocketEncoder(FOR_CLIENT, deflater.Pass(), inflater.Pass());
|
| }
|
|
|
| +WebSocketEncoder::WebSocketEncoder(Type type,
|
| + scoped_ptr<WebSocketDeflater> deflater,
|
| + scoped_ptr<WebSocketInflater> inflater)
|
| + : type_(type), deflater_(deflater.Pass()), inflater_(inflater.Pass()) {}
|
| +
|
| +WebSocketEncoder::~WebSocketEncoder() {}
|
| +
|
| WebSocket::ParseResult WebSocketEncoder::DecodeFrame(
|
| const base::StringPiece& frame,
|
| int* bytes_consumed,
|
| std::string* output) {
|
| bool compressed;
|
| - WebSocket::ParseResult result =
|
| - DecodeFrameHybi17(frame, is_server_, bytes_consumed, output, &compressed);
|
| + WebSocket::ParseResult result = DecodeFrameHybi17(
|
| + frame, type_ == FOR_SERVER, bytes_consumed, output, &compressed);
|
| if (result == WebSocket::FRAME_OK && compressed) {
|
| if (!Inflate(output))
|
| result = WebSocket::FRAME_ERROR;
|
|
|