Blame | Last modification | View Log | RSS feed
<?phpnamespace Ratchet\RFC6455\Handshake;use Psr\Http\Message\MessageInterface;use Psr\Http\Message\RequestInterface;use Psr\Http\Message\ResponseInterface;final class PermessageDeflateOptions{const MAX_WINDOW_BITS = 15;/* this is a private instead of const for 5.4 compatibility */private static $VALID_BITS = ['8', '9', '10', '11', '12', '13', '14', '15'];private $deflateEnabled = false;private $server_no_context_takeover;private $client_no_context_takeover;private $server_max_window_bits;private $client_max_window_bits;private function __construct() { }public static function createEnabled() {$new = new static();$new->deflateEnabled = true;$new->client_max_window_bits = self::MAX_WINDOW_BITS;$new->client_no_context_takeover = false;$new->server_max_window_bits = self::MAX_WINDOW_BITS;$new->server_no_context_takeover = false;return $new;}public static function createDisabled() {return new static();}public function withClientNoContextTakeover() {$new = clone $this;$new->client_no_context_takeover = true;}public function withoutClientNoContextTakeover() {$new = clone $this;$new->client_no_context_takeover = false;}public function withServerNoContextTakeover() {$new = clone $this;$new->server_no_context_takeover = true;}public function withoutServerNoContextTakeover() {$new = clone $this;$new->server_no_context_takeover = false;}public function withServerMaxWindowBits($bits = self::MAX_WINDOW_BITS) {if (!in_array($bits, self::$VALID_BITS)) {throw new \Exception('server_max_window_bits must have a value between 8 and 15.');}$new = clone $this;$new->server_max_window_bits = $bits;}public function withClientMaxWindowBits($bits = self::MAX_WINDOW_BITS) {if (!in_array($bits, self::$VALID_BITS)) {throw new \Exception('client_max_window_bits must have a value between 8 and 15.');}$new = clone $this;$new->client_max_window_bits = $bits;}/*** https://tools.ietf.org/html/rfc6455#section-9.1* https://tools.ietf.org/html/rfc7692#section-7** @param MessageInterface $requestOrResponse* @return PermessageDeflateOptions[]* @throws \Exception*/public static function fromRequestOrResponse(MessageInterface $requestOrResponse) {$optionSets = [];$extHeader = preg_replace('/\s+/', '', join(', ', $requestOrResponse->getHeader('Sec-Websocket-Extensions')));$configurationRequests = explode(',', $extHeader);foreach ($configurationRequests as $configurationRequest) {$parts = explode(';', $configurationRequest);if (count($parts) == 0) {continue;}if ($parts[0] !== 'permessage-deflate') {continue;}array_shift($parts);$options = new static();$options->deflateEnabled = true;foreach ($parts as $part) {$kv = explode('=', $part);$key = $kv[0];$value = count($kv) > 1 ? $kv[1] : null;switch ($key) {case "server_no_context_takeover":case "client_no_context_takeover":if ($value !== null) {throw new InvalidPermessageDeflateOptionsException($key . ' must not have a value.');}$value = true;break;case "server_max_window_bits":if (!in_array($value, self::$VALID_BITS)) {throw new InvalidPermessageDeflateOptionsException($key . ' must have a value between 8 and 15.');}break;case "client_max_window_bits":if ($value === null) {$value = '15';}if (!in_array($value, self::$VALID_BITS)) {throw new InvalidPermessageDeflateOptionsException($key . ' must have no value or a value between 8 and 15.');}break;default:throw new InvalidPermessageDeflateOptionsException('Option "' . $key . '"is not valid for permessage deflate');}if ($options->$key !== null) {throw new InvalidPermessageDeflateOptionsException($key . ' specified more than once. Connection must be declined.');}$options->$key = $value;}if ($options->getClientMaxWindowBits() === null) {$options->client_max_window_bits = 15;}if ($options->getServerMaxWindowBits() === null) {$options->server_max_window_bits = 15;}$optionSets[] = $options;}// always put a disabled on the end$optionSets[] = new static();return $optionSets;}/*** @return mixed*/public function getServerNoContextTakeover(){return $this->server_no_context_takeover;}/*** @return mixed*/public function getClientNoContextTakeover(){return $this->client_no_context_takeover;}/*** @return mixed*/public function getServerMaxWindowBits(){return $this->server_max_window_bits;}/*** @return mixed*/public function getClientMaxWindowBits(){return $this->client_max_window_bits;}/*** @return bool*/public function isEnabled(){return $this->deflateEnabled;}/*** @param ResponseInterface $response* @return ResponseInterface*/public function addHeaderToResponse(ResponseInterface $response){if (!$this->deflateEnabled) {return $response;}$header = 'permessage-deflate';if ($this->client_max_window_bits != 15) {$header .= '; client_max_window_bits='. $this->client_max_window_bits;}if ($this->client_no_context_takeover) {$header .= '; client_no_context_takeover';}if ($this->server_max_window_bits != 15) {$header .= '; server_max_window_bits=' . $this->server_max_window_bits;}if ($this->server_no_context_takeover) {$header .= '; server_no_context_takeover';}return $response->withAddedHeader('Sec-Websocket-Extensions', $header);}public function addHeaderToRequest(RequestInterface $request) {if (!$this->deflateEnabled) {return $request;}$header = 'permessage-deflate';if ($this->server_no_context_takeover) {$header .= '; server_no_context_takeover';}if ($this->client_no_context_takeover) {$header .= '; client_no_context_takeover';}if ($this->server_max_window_bits != 15) {$header .= '; server_max_window_bits=' . $this->server_max_window_bits;}$header .= '; client_max_window_bits';if ($this->client_max_window_bits != 15) {$header .= '='. $this->client_max_window_bits;}return $request->withAddedHeader('Sec-Websocket-Extensions', $header);}public static function permessageDeflateSupported($version = PHP_VERSION) {if (!function_exists('deflate_init')) {return false;}if (version_compare($version, '7.1.3', '>')) {return true;}if (version_compare($version, '7.0.18', '>=')&& version_compare($version, '7.1.0', '<')) {return true;}return false;}}