protocol-http 0.21.0 → 0.22.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/protocol/http/cookie.rb +14 -3
- data/lib/protocol/http/header/cookie.rb +1 -0
- data/lib/protocol/http/headers.rb +13 -13
- data/lib/protocol/http/middleware/builder.rb +10 -6
- data/lib/protocol/http/version.rb +1 -1
- metadata +5 -15
- data/.editorconfig +0 -5
- data/.github/workflows/development.yml +0 -44
- data/.gitignore +0 -14
- data/.rspec +0 -3
- data/README.md +0 -90
- data/STREAMING.md +0 -69
- data/bake.rb +0 -37
- data/gems.rb +0 -16
- data/protocol-http.gemspec +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5ce664c282254446c2a0630171f738e1f2649f79610357c6516278bb1daa8b8
|
4
|
+
data.tar.gz: 6e279c9da31e55d441e2bdc95bd1802ac89688e03d80a6f7a6bfe1d6b3b505ee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: febde12794a4c38af66b3578af6ffeb5ffdbf8bf606b9c7c413e6b4f4e2cd65452100fbe8ec33794be2f5cb044c4db1bb151763ff7a3f30bda7b9e766ee3738d
|
7
|
+
data.tar.gz: 96ce0f77a34495f5ee10c4b00099db58be7fbaceaef65249ed5e3abaa931cc7f7bb6df048a901923fc7113624fb7bd21becae6263e28ee3000cc617fc662b3ae
|
data/lib/protocol/http/cookie.rb
CHANGED
@@ -32,6 +32,10 @@ module Protocol
|
|
32
32
|
@directives = directives
|
33
33
|
end
|
34
34
|
|
35
|
+
attr :name
|
36
|
+
attr :value
|
37
|
+
attr :directives
|
38
|
+
|
35
39
|
def encoded_name
|
36
40
|
URL.escape(@name)
|
37
41
|
end
|
@@ -61,11 +65,11 @@ module Protocol
|
|
61
65
|
return buffer
|
62
66
|
end
|
63
67
|
|
64
|
-
def self.parse
|
65
|
-
head, *
|
68
|
+
def self.parse(string)
|
69
|
+
head, *directives = string.split(/\s*;\s*/)
|
66
70
|
|
67
71
|
key, value = head.split('=')
|
68
|
-
directives
|
72
|
+
directives = self.parse_directives(directives)
|
69
73
|
|
70
74
|
self.new(
|
71
75
|
URI.decode(key),
|
@@ -73,6 +77,13 @@ module Protocol
|
|
73
77
|
directives,
|
74
78
|
)
|
75
79
|
end
|
80
|
+
|
81
|
+
def self.parse_directives(strings)
|
82
|
+
strings.collect do |string|
|
83
|
+
key, value = string.split('=', 2)
|
84
|
+
[key, value || true]
|
85
|
+
end.to_h
|
86
|
+
end
|
76
87
|
end
|
77
88
|
end
|
78
89
|
end
|
@@ -37,7 +37,7 @@ module Protocol
|
|
37
37
|
Split = Header::Split
|
38
38
|
Multiple = Header::Multiple
|
39
39
|
|
40
|
-
|
40
|
+
TRAILER = 'trailer'
|
41
41
|
|
42
42
|
# Construct an instance from a headers Array or Hash. No-op if already an instance of `Headers`. If the underlying array is frozen, it will be duped.
|
43
43
|
# @return [Headers] an instance of headers.
|
@@ -67,7 +67,7 @@ module Protocol
|
|
67
67
|
@fields = fields
|
68
68
|
@indexed = indexed
|
69
69
|
|
70
|
-
# Marks where
|
70
|
+
# Marks where trailer start in the @fields array.
|
71
71
|
@tail = nil
|
72
72
|
end
|
73
73
|
|
@@ -84,10 +84,10 @@ module Protocol
|
|
84
84
|
@tail = nil
|
85
85
|
end
|
86
86
|
|
87
|
-
# Flatten
|
87
|
+
# Flatten trailer into the headers.
|
88
88
|
def flatten!
|
89
89
|
if @tail
|
90
|
-
self.delete(
|
90
|
+
self.delete(TRAILER)
|
91
91
|
@tail = nil
|
92
92
|
end
|
93
93
|
|
@@ -101,27 +101,27 @@ module Protocol
|
|
101
101
|
# An array of `[key, value]` pairs.
|
102
102
|
attr :fields
|
103
103
|
|
104
|
-
# @return the
|
105
|
-
def
|
104
|
+
# @return the trailer if there are any.
|
105
|
+
def trailer?
|
106
106
|
@tail != nil
|
107
107
|
end
|
108
108
|
|
109
|
-
# Record the current headers, and prepare to receive
|
110
|
-
def
|
111
|
-
return nil unless self.include?(
|
109
|
+
# Record the current headers, and prepare to receive trailer.
|
110
|
+
def trailer!(&block)
|
111
|
+
return nil unless self.include?(TRAILER)
|
112
112
|
|
113
113
|
@tail ||= @fields.size
|
114
114
|
|
115
|
-
return to_enum(:
|
115
|
+
return to_enum(:trailer!) unless block_given?
|
116
116
|
|
117
117
|
if @tail
|
118
118
|
@fields.drop(@tail).each(&block)
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
|
-
# Enumerate all
|
123
|
-
def
|
124
|
-
return to_enum(:
|
122
|
+
# Enumerate all headers in the trailer, if there are any.
|
123
|
+
def trailer(&block)
|
124
|
+
return to_enum(:trailer) unless block_given?
|
125
125
|
|
126
126
|
if @tail
|
127
127
|
@fields.drop(@tail).each(&block)
|
@@ -35,17 +35,17 @@ module Protocol
|
|
35
35
|
end
|
36
36
|
|
37
37
|
class Builder
|
38
|
-
def initialize(default_app = NotFound
|
38
|
+
def initialize(default_app = NotFound)
|
39
39
|
@use = []
|
40
40
|
@app = default_app
|
41
|
-
|
42
|
-
instance_eval(&block) if block_given?
|
43
41
|
end
|
44
42
|
|
45
|
-
def use(middleware, *
|
46
|
-
@use << proc {|app| middleware.new(app, *
|
43
|
+
def use(middleware, *arguments, &block)
|
44
|
+
@use << proc {|app| middleware.new(app, *arguments, &block)}
|
47
45
|
end
|
48
46
|
|
47
|
+
ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
|
48
|
+
|
49
49
|
def run(app)
|
50
50
|
@app = app
|
51
51
|
end
|
@@ -56,7 +56,11 @@ module Protocol
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def self.build(&block)
|
59
|
-
Builder.new
|
59
|
+
builder = Builder.new
|
60
|
+
|
61
|
+
builder.instance_eval(&block)
|
62
|
+
|
63
|
+
return builder.to_app
|
60
64
|
end
|
61
65
|
end
|
62
66
|
end
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: protocol-http
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.22.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-06-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: covered
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
@@ -54,19 +54,10 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
description:
|
56
56
|
email:
|
57
|
-
- samuel.williams@oriontransfer.co.nz
|
58
57
|
executables: []
|
59
58
|
extensions: []
|
60
59
|
extra_rdoc_files: []
|
61
60
|
files:
|
62
|
-
- ".editorconfig"
|
63
|
-
- ".github/workflows/development.yml"
|
64
|
-
- ".gitignore"
|
65
|
-
- ".rspec"
|
66
|
-
- README.md
|
67
|
-
- STREAMING.md
|
68
|
-
- bake.rb
|
69
|
-
- gems.rb
|
70
61
|
- lib/protocol/http.rb
|
71
62
|
- lib/protocol/http/accept_encoding.rb
|
72
63
|
- lib/protocol/http/body/buffered.rb
|
@@ -102,7 +93,6 @@ files:
|
|
102
93
|
- lib/protocol/http/response.rb
|
103
94
|
- lib/protocol/http/url.rb
|
104
95
|
- lib/protocol/http/version.rb
|
105
|
-
- protocol-http.gemspec
|
106
96
|
homepage: https://github.com/socketry/protocol-http
|
107
97
|
licenses:
|
108
98
|
- MIT
|
@@ -122,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
112
|
- !ruby/object:Gem::Version
|
123
113
|
version: '0'
|
124
114
|
requirements: []
|
125
|
-
rubygems_version: 3.1.
|
115
|
+
rubygems_version: 3.1.6
|
126
116
|
signing_key:
|
127
117
|
specification_version: 4
|
128
118
|
summary: Provides abstractions to handle HTTP protocols.
|
data/.editorconfig
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
name: Development
|
2
|
-
|
3
|
-
on: [push, pull_request]
|
4
|
-
|
5
|
-
jobs:
|
6
|
-
test:
|
7
|
-
runs-on: ${{matrix.os}}-latest
|
8
|
-
continue-on-error: ${{matrix.experimental}}
|
9
|
-
|
10
|
-
strategy:
|
11
|
-
matrix:
|
12
|
-
os:
|
13
|
-
- ubuntu
|
14
|
-
- macos
|
15
|
-
|
16
|
-
ruby:
|
17
|
-
- 2.5
|
18
|
-
- 2.6
|
19
|
-
- 2.7
|
20
|
-
|
21
|
-
experimental: [false]
|
22
|
-
env: [""]
|
23
|
-
|
24
|
-
include:
|
25
|
-
- os: ubuntu
|
26
|
-
ruby: truffleruby
|
27
|
-
experimental: true
|
28
|
-
- os: ubuntu
|
29
|
-
ruby: jruby
|
30
|
-
experimental: true
|
31
|
-
- os: ubuntu
|
32
|
-
ruby: head
|
33
|
-
experimental: true
|
34
|
-
|
35
|
-
steps:
|
36
|
-
- uses: actions/checkout@v2
|
37
|
-
- uses: ioquatix/setup-ruby@master
|
38
|
-
with:
|
39
|
-
ruby-version: ${{matrix.ruby}}
|
40
|
-
bundler-cache: true
|
41
|
-
|
42
|
-
- name: Run tests
|
43
|
-
timeout-minutes: 5
|
44
|
-
run: ${{matrix.env}} bundle exec rspec
|
data/.gitignore
DELETED
data/.rspec
DELETED
data/README.md
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
# Protocol::HTTP
|
2
|
-
|
3
|
-
Provides abstractions for working with the HTTP protocol.
|
4
|
-
|
5
|
-
[![Development Status](https://github.com/socketry/protocol-http/workflows/Development/badge.svg)](https://github.com/socketry/protocol-http/actions?workflow=Development)
|
6
|
-
|
7
|
-
## Installation
|
8
|
-
|
9
|
-
Add this line to your application's Gemfile:
|
10
|
-
|
11
|
-
``` ruby
|
12
|
-
gem 'protocol-http'
|
13
|
-
```
|
14
|
-
|
15
|
-
And then execute:
|
16
|
-
|
17
|
-
$ bundle
|
18
|
-
|
19
|
-
Or install it yourself as:
|
20
|
-
|
21
|
-
$ gem install protocol-http
|
22
|
-
|
23
|
-
## Usage
|
24
|
-
|
25
|
-
### Headers
|
26
|
-
|
27
|
-
``` ruby
|
28
|
-
require 'protocol/http/headers'
|
29
|
-
|
30
|
-
headers = Protocol::HTTP::Headers.new
|
31
|
-
|
32
|
-
headers['Content-Type'] = "image/jpeg"
|
33
|
-
|
34
|
-
headers['content-type']
|
35
|
-
# => "image/jpeg"
|
36
|
-
```
|
37
|
-
|
38
|
-
### Reference
|
39
|
-
|
40
|
-
``` ruby
|
41
|
-
require 'protocol/http/reference'
|
42
|
-
|
43
|
-
reference = Protocol::HTTP::Reference.new("/search", q: 'kittens')
|
44
|
-
|
45
|
-
reference.to_s
|
46
|
-
# => "/search?q=kittens"
|
47
|
-
```
|
48
|
-
|
49
|
-
### URL
|
50
|
-
|
51
|
-
``` ruby
|
52
|
-
require 'protocol/http/url'
|
53
|
-
|
54
|
-
reference = Protocol::HTTP::Reference.parse("/search?q=kittens")
|
55
|
-
|
56
|
-
parameters = Protocol::HTTP::URL.decode(reference.query_string)
|
57
|
-
# => {"q"=>"kittens"}
|
58
|
-
```
|
59
|
-
|
60
|
-
## Contributing
|
61
|
-
|
62
|
-
1. Fork it
|
63
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
64
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
65
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
66
|
-
5. Create new Pull Request
|
67
|
-
|
68
|
-
## License
|
69
|
-
|
70
|
-
Released under the MIT license.
|
71
|
-
|
72
|
-
Copyright, 2019, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
|
73
|
-
|
74
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
75
|
-
of this software and associated documentation files (the "Software"), to deal
|
76
|
-
in the Software without restriction, including without limitation the rights
|
77
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
78
|
-
copies of the Software, and to permit persons to whom the Software is
|
79
|
-
furnished to do so, subject to the following conditions:
|
80
|
-
|
81
|
-
The above copyright notice and this permission notice shall be included in
|
82
|
-
all copies or substantial portions of the Software.
|
83
|
-
|
84
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
85
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
86
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
87
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
88
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
89
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
90
|
-
THE SOFTWARE.
|
data/STREAMING.md
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
# Streaming
|
2
|
-
|
3
|
-
Streaming is a complex topic given that most server's are not scalable in this respect. A variety of solutions exist to work around limited server implementations, but they impose significant burden on the implementation and are generally incompatible with implementations of HTTP which are not request per connection. We propose to simplify the current Rack implementation to support streaming in a consistent way, but also allow for backwards compatibility with existing servers.
|
4
|
-
|
5
|
-
## Current Implementation
|
6
|
-
|
7
|
-
Rack currently supports streaming using `rack.hijack`. There are two ways to hijack the underlying socket for a given request: a partial hijack and a full hijack. Not all servers support both modes of operation and neither method is fully compatible with HTTP/2.
|
8
|
-
|
9
|
-
### Partial Hijack
|
10
|
-
|
11
|
-
A partial hijack allows the web server to respond with the response status, headers, and then yields the stream back to the application code. It does this by using a special `rack.hijack` header:
|
12
|
-
|
13
|
-
``` ruby
|
14
|
-
def call(env)
|
15
|
-
body = proc do |stream|
|
16
|
-
stream.write("Hello World")
|
17
|
-
stream.close
|
18
|
-
end
|
19
|
-
|
20
|
-
if env['rack.hijack?']
|
21
|
-
return [200, {'rack.hijack' => body}, nil]
|
22
|
-
end
|
23
|
-
end
|
24
|
-
```
|
25
|
-
|
26
|
-
- It is not clear whether the body needs to handle the formatting of the response, i.e. chunked encoding, compression, transfer encoding, etc.
|
27
|
-
- It mixes `rack.` specific headers with normal HTTP headers which is computationally expensive for the server to detect.
|
28
|
-
- It's not clear what the returned body should be.
|
29
|
-
|
30
|
-
### Full Hijack
|
31
|
-
|
32
|
-
A full hijack allows the web server to completely take control of the underlying socket. This occurs at the point the `rack.hijack` is invoked.
|
33
|
-
|
34
|
-
``` ruby
|
35
|
-
def call(env)
|
36
|
-
if hijack = env['rack.hijack']
|
37
|
-
socket = hijack.call
|
38
|
-
|
39
|
-
Thread.new do
|
40
|
-
socket.write("Hello World")
|
41
|
-
socket.close
|
42
|
-
end
|
43
|
-
|
44
|
-
return nil # ???
|
45
|
-
end
|
46
|
-
end
|
47
|
-
```
|
48
|
-
|
49
|
-
- The socket might not actually be a raw socket, if the server is using TLS.
|
50
|
-
- Extreme care is required in order to handle the protocol correctly.
|
51
|
-
- It's not clear if such a design can work with anything other than HTTP/1.
|
52
|
-
- The user code **must** return from the `call(env)` method in order for the server continue handling requests.
|
53
|
-
- It's not clear what the response should be.
|
54
|
-
|
55
|
-
## Typical Scenarios
|
56
|
-
|
57
|
-
There are several typical scenarios where `rack.hijack` has been used.
|
58
|
-
|
59
|
-
### ActionCable
|
60
|
-
|
61
|
-
ActionCable uses full hijack in order to negotiate and communicate using WebSocket protocol. Such a design requires the ActionCable server to run in the same process as the web server.
|
62
|
-
|
63
|
-
### MessageBus
|
64
|
-
|
65
|
-
MessageBus uses full hijack in order to keep the socket alive and feed messages occasionally.
|
66
|
-
|
67
|
-
## Supporting HTTP/2
|
68
|
-
|
69
|
-
It is possible to support partial hijack in HTTP/2. The hijacked stream is multiplexed the same as any other connection. However, such a design cannot be thread safe without significant design trade-offs.
|
data/bake.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
def external
|
4
|
-
require 'bundler'
|
5
|
-
|
6
|
-
Bundler.with_unbundled_env do
|
7
|
-
clone_and_test("protocol-http1")
|
8
|
-
clone_and_test("protocol-http2")
|
9
|
-
clone_and_test("async-websocket")
|
10
|
-
clone_and_test("async-http")
|
11
|
-
clone_and_test("async-rest")
|
12
|
-
clone_and_test("falcon")
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
|
-
def clone_and_test(name)
|
19
|
-
path = "external/#{name}"
|
20
|
-
|
21
|
-
unless File.exist?(path)
|
22
|
-
system("git", "clone", "https://git@github.com/socketry/#{name}", path)
|
23
|
-
end
|
24
|
-
|
25
|
-
gemfile = [
|
26
|
-
File.join(path, "gems.rb"),
|
27
|
-
File.join(path, "Gemfile"),
|
28
|
-
].find{|path| File.exist?(path)}
|
29
|
-
|
30
|
-
system("git", "checkout", "-f", File.basename(gemfile), chdir: path)
|
31
|
-
|
32
|
-
File.open(gemfile, "a") do |file|
|
33
|
-
file.puts('', 'gem "protocol-http", path: "../../"')
|
34
|
-
end
|
35
|
-
|
36
|
-
system("bundle install && bundle exec rspec", chdir: path)
|
37
|
-
end
|
data/gems.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
source "https://rubygems.org"
|
4
|
-
|
5
|
-
# Specify your gem's dependencies in protocol-http.gemspec
|
6
|
-
gemspec
|
7
|
-
|
8
|
-
group :maintenance, optional: true do
|
9
|
-
gem "bake-modernize"
|
10
|
-
gem "bake-bundler"
|
11
|
-
end
|
12
|
-
|
13
|
-
group :test do
|
14
|
-
gem 'async-io'
|
15
|
-
gem 'async-rspec'
|
16
|
-
end
|
data/protocol-http.gemspec
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
|
2
|
-
require_relative "lib/protocol/http/version"
|
3
|
-
|
4
|
-
Gem::Specification.new do |spec|
|
5
|
-
spec.name = "protocol-http"
|
6
|
-
spec.version = Protocol::HTTP::VERSION
|
7
|
-
spec.authors = ["Samuel Williams"]
|
8
|
-
spec.email = ["samuel.williams@oriontransfer.co.nz"]
|
9
|
-
|
10
|
-
spec.summary = "Provides abstractions to handle HTTP protocols."
|
11
|
-
spec.homepage = "https://github.com/socketry/protocol-http"
|
12
|
-
spec.license = "MIT"
|
13
|
-
|
14
|
-
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
15
|
-
f.match(%r{^(test|spec|features)/})
|
16
|
-
end
|
17
|
-
|
18
|
-
spec.required_ruby_version = '>= 2.5'
|
19
|
-
|
20
|
-
spec.require_paths = ["lib"]
|
21
|
-
|
22
|
-
spec.add_development_dependency "covered"
|
23
|
-
spec.add_development_dependency "bundler"
|
24
|
-
spec.add_development_dependency "rspec"
|
25
|
-
end
|