Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c7e1f86979 | |||
| 3c0fda5b21 | |||
| 5891358639 | |||
| 84b869effb | |||
| 6ea62a9965 | |||
| 1d80737dce | |||
| a0f23c5d3c | |||
| 8b6c706737 | |||
| 402222fdd9 | |||
| aa156a439d | |||
| 8174a4a0f9 | |||
| 96059802a6 | |||
| bd6d0acc01 | |||
| a8c847876b |
@@ -0,0 +1,34 @@
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ruby:
|
||||
- ruby-2.0
|
||||
- ruby-2.1
|
||||
# - ruby-2.2
|
||||
- ruby-2.3
|
||||
- ruby-2.4
|
||||
- ruby-2.5
|
||||
- ruby-2.6
|
||||
- ruby-2.7
|
||||
- ruby-3.0
|
||||
- ruby-3.1
|
||||
- ruby-3.2
|
||||
- jruby-9.1
|
||||
- jruby-9.2
|
||||
- jruby-9.3
|
||||
- jruby-9.4
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby }}
|
||||
bundler-cache: true
|
||||
- run: ruby --version
|
||||
- run: bundle exec rspec
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
sudo: false
|
||||
dist: trusty
|
||||
language: ruby
|
||||
|
||||
rvm:
|
||||
- 1.9.3
|
||||
- 2.0.0
|
||||
- 2.1.10
|
||||
- 2.2.10
|
||||
- 2.3.8
|
||||
- 2.4.6
|
||||
- 2.5.5
|
||||
- 2.6.3
|
||||
- jruby-19mode
|
||||
- jruby-head
|
||||
|
||||
before_install:
|
||||
- '[[ "$(ruby --version)" == *"1.9.3"* ]] && gem update --system 2.4.8 || true'
|
||||
|
||||
script:
|
||||
- bundle exec rspec -c spec
|
||||
@@ -1,6 +1,11 @@
|
||||
### 0.1.5 / 2020-06-02
|
||||
|
||||
- Remove a ReDoS vulnerability in the header parser (CVE-2020-7663)
|
||||
|
||||
### 0.1.4 / 2019-06-10
|
||||
|
||||
- Fix a deprecation warning for using the `=~` operator on `true`
|
||||
- Change license from MIT to Apache 2.0
|
||||
|
||||
### 0.1.3 / 2017-11-11
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
Copyright 2014-2019 James Coglan
|
||||
Copyright 2014-2020 James Coglan
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# websocket-extensions [](http://travis-ci.org/faye/websocket-extensions-ruby)
|
||||
# websocket-extensions
|
||||
|
||||
A minimal framework that supports the implementation of WebSocket extensions in
|
||||
a way that's decoupled from the main protocol. This library aims to allow a
|
||||
@@ -227,8 +227,8 @@ then the `permessage-deflate` extension will receive the call:
|
||||
|
||||
```rb
|
||||
ext.create_server_session([
|
||||
{'server_no_context_takeover' => true, 'server_max_window_bits' => 8},
|
||||
{'server_max_window_bits' => 15}
|
||||
{ 'server_no_context_takeover' => true, 'server_max_window_bits' => 8 },
|
||||
{ 'server_max_window_bits' => 15 }
|
||||
])
|
||||
```
|
||||
|
||||
@@ -244,8 +244,8 @@ implement the following methods, as well as the *Session* API listed below.
|
||||
```rb
|
||||
client_session.generate_offer
|
||||
# e.g. -> [
|
||||
# {'server_no_context_takeover' => true, 'server_max_window_bits' => 8},
|
||||
# {'server_max_window_bits' => 15}
|
||||
# { 'server_no_context_takeover' => true, 'server_max_window_bits' => 8 },
|
||||
# { 'server_max_window_bits' => 15 }
|
||||
# ]
|
||||
```
|
||||
|
||||
@@ -270,7 +270,7 @@ must implement the following methods, as well as the *Session* API listed below.
|
||||
|
||||
```rb
|
||||
server_session.generate_response
|
||||
# e.g. -> {'server_max_window_bits' => 8}
|
||||
# e.g. -> { 'server_max_window_bits' => 8 }
|
||||
```
|
||||
|
||||
This returns the set of parameters the server session wants to send in its
|
||||
|
||||
@@ -38,7 +38,7 @@ module WebSocket
|
||||
end
|
||||
|
||||
if @by_name.has_key?(ext.name)
|
||||
raise TypeError, %Q{An extension with name "#{ext.name}" is already registered}
|
||||
raise TypeError, %Q{An extension with name "#{ ext.name }" is already registered}
|
||||
end
|
||||
|
||||
@by_name[ext.name] = ext
|
||||
@@ -78,18 +78,18 @@ module WebSocket
|
||||
|
||||
responses.each_offer do |name, params|
|
||||
unless record = @index[name]
|
||||
raise ExtensionError, %Q{Server sent am extension response for unknown extension "#{name}"}
|
||||
raise ExtensionError, %Q{Server sent am extension response for unknown extension "#{ name } }
|
||||
end
|
||||
|
||||
ext, session = *record
|
||||
|
||||
if reserved = reserved?(ext)
|
||||
raise ExtensionError, %Q{Server sent two extension responses that use the RSV#{reserved[0]} } +
|
||||
%Q{ bit: "#{reserved[1]}" and "#{ext.name}"}
|
||||
raise ExtensionError, %Q{Server sent two extension responses that use the RSV#{ reserved[0] }} +
|
||||
%Q{bit: "#{ reserved[1] }" and "#{ ext.name }"}
|
||||
end
|
||||
|
||||
unless session.activate(params) == true
|
||||
raise ExtensionError, %Q{Server send unacceptable extension parameters: #{Parser.serialize_params(name, params)}}
|
||||
raise ExtensionError, %Q{Server send unacceptable extension parameters: #{ Parser.serialize_params(name, params) }}
|
||||
end
|
||||
|
||||
reserve(ext)
|
||||
@@ -118,7 +118,7 @@ module WebSocket
|
||||
end
|
||||
|
||||
def valid_frame_rsv(frame)
|
||||
allowed = {:rsv1 => false, :rsv2 => false, :rsv3 => false}
|
||||
allowed = { :rsv1 => false, :rsv2 => false, :rsv3 => false }
|
||||
|
||||
if MESSAGE_OPCODES.include?(frame.opcode)
|
||||
@sessions.each do |ext, session|
|
||||
|
||||
@@ -6,10 +6,10 @@ module WebSocket
|
||||
class Parser
|
||||
TOKEN = /([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+)/
|
||||
NOTOKEN = /([^!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z])/
|
||||
QUOTED = /"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)"/
|
||||
PARAM = %r{#{TOKEN.source}(?:=(?:#{TOKEN.source}|#{QUOTED.source}))?}
|
||||
EXT = %r{#{TOKEN.source}(?: *; *#{PARAM.source})*}
|
||||
EXT_LIST = %r{^#{EXT.source}(?: *, *#{EXT.source})*$}
|
||||
QUOTED = /"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"\\])*)"/
|
||||
PARAM = %r{#{ TOKEN.source }(?:=(?:#{ TOKEN.source }|#{ QUOTED.source }))?}
|
||||
EXT = %r{#{ TOKEN.source }(?: *; *#{ PARAM.source })*}
|
||||
EXT_LIST = %r{^#{ EXT.source }(?: *, *#{ EXT.source })*$}
|
||||
NUMBER = /^-?(0|[1-9][0-9]*)(\.[0-9]+)?$/
|
||||
|
||||
ParseError = Class.new(ArgumentError)
|
||||
@@ -19,7 +19,7 @@ module WebSocket
|
||||
return offers if header == '' or header.nil?
|
||||
|
||||
unless header =~ EXT_LIST
|
||||
raise ParseError, "Invalid Sec-WebSocket-Extensions header: #{header}"
|
||||
raise ParseError, "Invalid Sec-WebSocket-Extensions header: #{ header }"
|
||||
end
|
||||
|
||||
scanner = StringScanner.new(header)
|
||||
|
||||
@@ -20,62 +20,68 @@ describe WebSocket::Extensions::Parser do
|
||||
|
||||
it "parses one offer with no params" do
|
||||
expect(parse 'a').to eq [
|
||||
{:name => "a", :params => {}}
|
||||
{ :name => "a", :params => {} }
|
||||
]
|
||||
end
|
||||
|
||||
it "parses two offers with no params" do
|
||||
expect(parse 'a, b').to eq [
|
||||
{:name => "a", :params => {}}, {:name => "b", :params => {}}
|
||||
{ :name => "a", :params => {} }, { :name => "b", :params => {} }
|
||||
]
|
||||
end
|
||||
|
||||
it "parses a duplicate offer name" do
|
||||
expect(parse 'a, a').to eq [
|
||||
{:name => "a", :params => {}},
|
||||
{:name => "a", :params => {}}
|
||||
{ :name => "a", :params => {} },
|
||||
{ :name => "a", :params => {} }
|
||||
]
|
||||
end
|
||||
|
||||
it "parses a flag" do
|
||||
expect(parse 'a; b').to eq [
|
||||
{:name => "a", :params => {"b" => true}}
|
||||
{ :name => "a", :params => { "b" => true } }
|
||||
]
|
||||
end
|
||||
|
||||
it "parses an unquoted param" do
|
||||
expect(parse 'a; b=1').to eq [
|
||||
{:name => "a", :params => {"b" => 1}}
|
||||
{ :name => "a", :params => { "b" => 1 } }
|
||||
]
|
||||
end
|
||||
|
||||
it "parses a quoted param" do
|
||||
expect(parse 'a; b="hi, \"there"').to eq [
|
||||
{:name => "a", :params => {"b" => 'hi, "there'}}
|
||||
{ :name => "a", :params => { "b" => 'hi, "there' } }
|
||||
]
|
||||
end
|
||||
|
||||
it "parses multiple params" do
|
||||
expect(parse 'a; b; c=1; d="hi"').to eq [
|
||||
{:name => "a", :params => {"b" => true, "c" => 1, "d" => "hi"}}
|
||||
{ :name => "a", :params => { "b" => true, "c" => 1, "d" => "hi" } }
|
||||
]
|
||||
end
|
||||
|
||||
it "parses duplicate params" do
|
||||
expect(parse 'a; b; c=1; b="hi"').to eq [
|
||||
{:name => "a", :params => {"b" => [true, "hi"], "c" => 1}}
|
||||
{ :name => "a", :params => { "b" => [true, "hi"], "c" => 1 } }
|
||||
]
|
||||
end
|
||||
|
||||
it "parses multiple complex offers" do
|
||||
expect(parse 'a; b=1, c, b; d, c; e="hi, there"; e, a; b').to eq [
|
||||
{:name => "a", :params => {"b" => 1}},
|
||||
{:name => "c", :params => {}},
|
||||
{:name => "b", :params => {"d" => true}},
|
||||
{:name => "c", :params => {"e" => ['hi, there', true]}},
|
||||
{:name => "a", :params => {"b" => true}}
|
||||
{ :name => "a", :params => { "b" => 1 } },
|
||||
{ :name => "c", :params => {} },
|
||||
{ :name => "b", :params => { "d" => true } },
|
||||
{ :name => "c", :params => { "e" => ['hi, there', true] } },
|
||||
{ :name => "a", :params => { "b" => true } }
|
||||
]
|
||||
end
|
||||
|
||||
it "rejects a string missing its closing quote" do
|
||||
expect {
|
||||
parse "foo; bar=\"fooa\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a\\a"
|
||||
}.to raise_error(WebSocket::Extensions::Parser::ParseError)
|
||||
end
|
||||
end
|
||||
|
||||
describe :serialize_params do
|
||||
|
||||
@@ -39,7 +39,7 @@ describe WebSocket::Extensions do
|
||||
|
||||
describe "client sessions" do
|
||||
before do
|
||||
@offer = {"mode" => "compress"}
|
||||
@offer = { "mode" => "compress" }
|
||||
allow(@ext).to receive(:create_client_session).and_return(@session)
|
||||
allow(@session).to receive(:generate_offer).and_return(@offer)
|
||||
@extensions.add(@ext)
|
||||
@@ -134,18 +134,18 @@ describe WebSocket::Extensions do
|
||||
end
|
||||
|
||||
it "activates one session with a boolean param" do
|
||||
expect(@session).to receive(:activate).with("gzip" => true).exactly(1).and_return(true)
|
||||
expect(@session).to receive(:activate).with({ "gzip" => true }).exactly(1).and_return(true)
|
||||
@extensions.activate("deflate; gzip")
|
||||
end
|
||||
|
||||
it "activates one session with a string param" do
|
||||
expect(@session).to receive(:activate).with("mode" => "compress").exactly(1).and_return(true)
|
||||
expect(@session).to receive(:activate).with({ "mode" => "compress" }).exactly(1).and_return(true)
|
||||
@extensions.activate("deflate; mode=compress")
|
||||
end
|
||||
|
||||
it "activate multiple sessions" do
|
||||
expect(@session).to receive(:activate).with("a" => true).exactly(1).and_return(true)
|
||||
expect(@nonconflict_session).to receive(:activate).with("b" => true).exactly(1).and_return(true)
|
||||
expect(@session).to receive(:activate).with({ "a" => true }).exactly(1).and_return(true)
|
||||
expect(@nonconflict_session).to receive(:activate).with({ "b" => true }).exactly(1).and_return(true)
|
||||
@extensions.activate("deflate; a, reverse; b")
|
||||
end
|
||||
|
||||
@@ -248,7 +248,7 @@ describe WebSocket::Extensions do
|
||||
|
||||
describe "server sessions" do
|
||||
before do
|
||||
@response = {"mode" => "compress"}
|
||||
@response = { "mode" => "compress" }
|
||||
allow(@ext).to receive(:create_server_session).and_return(@session)
|
||||
allow(@session).to receive(:generate_response).and_return(@response)
|
||||
|
||||
@@ -269,12 +269,12 @@ describe WebSocket::Extensions do
|
||||
|
||||
describe :generate_response do
|
||||
it "asks the extension for a server session with the offer" do
|
||||
expect(@ext).to receive(:create_server_session).with([{"flag" => true}]).exactly(1).and_return(@session)
|
||||
expect(@ext).to receive(:create_server_session).with([{ "flag" => true }]).exactly(1).and_return(@session)
|
||||
@extensions.generate_response("deflate; flag")
|
||||
end
|
||||
|
||||
it "asks the extension for a server session with multiple offers" do
|
||||
expect(@ext).to receive(:create_server_session).with([{"a" => true}, {"b" => true}]).exactly(1).and_return(@session)
|
||||
expect(@ext).to receive(:create_server_session).with([{ "a" => true }, { "b" => true }]).exactly(1).and_return(@session)
|
||||
@extensions.generate_response("deflate; a, deflate; b")
|
||||
end
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Gem::Specification.new do |s|
|
||||
s.name = 'websocket-extensions'
|
||||
s.version = '0.1.4'
|
||||
s.version = '0.1.5'
|
||||
s.summary = 'Generic extension manager for WebSocket connections'
|
||||
s.author = 'James Coglan'
|
||||
s.email = 'jcoglan@gmail.com'
|
||||
|
||||
Reference in New Issue
Block a user