| // Copyright 2015 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/websockets/websocket_deflate_parameters.h" |
| |
| #include "base/strings/string_number_conversions.h" |
| |
| namespace net { |
| |
| namespace { |
| |
| const WebSocketDeflater::ContextTakeOverMode kTakeOverContext = |
| WebSocketDeflater::TAKE_OVER_CONTEXT; |
| const WebSocketDeflater::ContextTakeOverMode kDoNotTakeOverContext = |
| WebSocketDeflater::DO_NOT_TAKE_OVER_CONTEXT; |
| |
| const char kServerNoContextTakeOver[] = "server_no_context_takeover"; |
| const char kClientNoContextTakeOver[] = "client_no_context_takeover"; |
| const char kServerMaxWindowBits[] = "server_max_window_bits"; |
| const char kClientMaxWindowBits[] = "client_max_window_bits"; |
| const char kExtensionName[] = "permessage-deflate"; |
| |
| bool GetWindowBits(const std::string& value, int* window_bits) { |
| return !value.empty() && value[0] != '0' && |
| value.find_first_not_of("0123456789") == std::string::npos && |
| base::StringToInt(value, window_bits); |
| } |
| |
| bool DuplicateError(const std::string& name, std::string* failure_message) { |
| *failure_message = |
| "Received duplicate permessage-deflate extension parameter " + name; |
| return false; |
| } |
| |
| bool InvalidError(const std::string& name, std::string* failure_message) { |
| *failure_message = "Received invalid " + name + " parameter"; |
| return false; |
| } |
| |
| } // namespace |
| |
| WebSocketExtension WebSocketDeflateParameters::AsExtension() const { |
| WebSocketExtension e(kExtensionName); |
| |
| if (server_context_take_over_mode_ == kDoNotTakeOverContext) |
| e.Add(WebSocketExtension::Parameter(kServerNoContextTakeOver)); |
| if (client_context_take_over_mode_ == kDoNotTakeOverContext) |
| e.Add(WebSocketExtension::Parameter(kClientNoContextTakeOver)); |
| if (is_server_max_window_bits_specified()) { |
| DCHECK(server_max_window_bits_.has_value); |
| e.Add(WebSocketExtension::Parameter( |
| kServerMaxWindowBits, base::IntToString(server_max_window_bits()))); |
| } |
| if (is_client_max_window_bits_specified()) { |
| if (has_client_max_window_bits_value()) { |
| e.Add(WebSocketExtension::Parameter( |
| kClientMaxWindowBits, base::IntToString(client_max_window_bits()))); |
| } else { |
| e.Add(WebSocketExtension::Parameter(kClientMaxWindowBits)); |
| } |
| } |
| |
| return e; |
| } |
| |
| bool WebSocketDeflateParameters::IsValidAsRequest(std::string*) const { |
| if (server_max_window_bits_.is_specified) { |
| DCHECK(server_max_window_bits_.has_value); |
| DCHECK(IsValidWindowBits(server_max_window_bits_.bits)); |
| } |
| if (client_max_window_bits_.is_specified && |
| client_max_window_bits_.has_value) { |
| DCHECK(IsValidWindowBits(client_max_window_bits_.bits)); |
| } |
| return true; |
| } |
| |
| bool WebSocketDeflateParameters::IsValidAsResponse( |
| std::string* failure_message) const { |
| if (server_max_window_bits_.is_specified) { |
| DCHECK(server_max_window_bits_.has_value); |
| DCHECK(IsValidWindowBits(server_max_window_bits_.bits)); |
| } |
| if (client_max_window_bits_.is_specified) { |
| if (!client_max_window_bits_.has_value) { |
| *failure_message = "client_max_window_bits must have value"; |
| return false; |
| } |
| DCHECK(IsValidWindowBits(client_max_window_bits_.bits)); |
| } |
| |
| return true; |
| } |
| |
| bool WebSocketDeflateParameters::Initialize(const WebSocketExtension& extension, |
| std::string* failure_message) { |
| *this = WebSocketDeflateParameters(); |
| |
| if (extension.name() != kExtensionName) { |
| *failure_message = "extension name doesn't match"; |
| return false; |
| } |
| for (const auto& p : extension.parameters()) { |
| if (p.name() == kServerNoContextTakeOver) { |
| if (server_context_take_over_mode() == kDoNotTakeOverContext) |
| return DuplicateError(p.name(), failure_message); |
| if (p.HasValue()) |
| return InvalidError(p.name(), failure_message); |
| SetServerNoContextTakeOver(); |
| } else if (p.name() == kClientNoContextTakeOver) { |
| if (client_context_take_over_mode() == kDoNotTakeOverContext) |
| return DuplicateError(p.name(), failure_message); |
| if (p.HasValue()) |
| return InvalidError(p.name(), failure_message); |
| SetClientNoContextTakeOver(); |
| } else if (p.name() == kServerMaxWindowBits) { |
| if (server_max_window_bits_.is_specified) |
| return DuplicateError(p.name(), failure_message); |
| int bits; |
| if (!GetWindowBits(p.value(), &bits) || !IsValidWindowBits(bits)) |
| return InvalidError(p.name(), failure_message); |
| SetServerMaxWindowBits(bits); |
| } else if (p.name() == kClientMaxWindowBits) { |
| if (client_max_window_bits_.is_specified) |
| return DuplicateError(p.name(), failure_message); |
| if (p.value().empty()) { |
| SetClientMaxWindowBits(); |
| } else { |
| int bits; |
| if (!GetWindowBits(p.value(), &bits) || !IsValidWindowBits(bits)) |
| return InvalidError(p.name(), failure_message); |
| SetClientMaxWindowBits(bits); |
| } |
| } else { |
| *failure_message = |
| "Received an unexpected permessage-deflate extension parameter"; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool WebSocketDeflateParameters::IsCompatibleWith( |
| const WebSocketDeflateParameters& response) const { |
| const auto& request = *this; |
| DCHECK(request.IsValidAsRequest()); |
| DCHECK(response.IsValidAsResponse()); |
| |
| // server_no_context_take_over |
| if (request.server_context_take_over_mode() == kDoNotTakeOverContext && |
| response.server_context_take_over_mode() == kTakeOverContext) { |
| return false; |
| } |
| |
| // No compatibility check is needed for client_no_context_take_over |
| |
| // server_max_window_bits |
| if (request.server_max_window_bits_.is_specified) { |
| DCHECK(request.server_max_window_bits_.has_value); |
| if (!response.server_max_window_bits_.is_specified) |
| return false; |
| DCHECK(response.server_max_window_bits_.has_value); |
| if (request.server_max_window_bits_.bits < |
| response.server_max_window_bits_.bits) { |
| return false; |
| } |
| } |
| |
| // client_max_window_bits |
| if (!request.client_max_window_bits_.is_specified && |
| response.client_max_window_bits_.is_specified) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace net |