// 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