protocol-http 0.20.1 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5f14e6a0192840efd07d2ba8e8e3a8bd9312eb5fa8b976f1e12baaf14ee49696
4
- data.tar.gz: 2548bc880b20a5e162cad7fdcbbece9a0cdfae429861b8a5f1bbd68cf904cb9b
3
+ metadata.gz: 01dc23ef2870a6572cc986fd3dac2de5902ec3283fb5a380d9f24b1193516d68
4
+ data.tar.gz: ca6bf9694e01884a6d2c2e9ab5e85c54d64c4f150881cf57fb2ba1ed80fa0b9c
5
5
  SHA512:
6
- metadata.gz: b1e6692c96818aed1ed11c02b976169598ba9df9afc1f73109745168f62d85a75b6bf9abe530cac1daa39a3a40b5a815e06280581d8912b6c32f7e488f35fac0
7
- data.tar.gz: 747e961c97c027258a46fbbd70f9845eead55d0c3dc6d108dae0083b99693bf5384f3868c9d50e5270d06337341879d265dc362a7907a16d22ecca73149e8223
6
+ metadata.gz: d71145aa0b55861140b181c2351501cd9eec840b747abd4d5d48841a4e8d2df0bb5d2e5fa0c0c83be98a51775e92160cd52690806c5b1443808f7d6386cdc568
7
+ data.tar.gz: f2f603776458e27ed0b2148fce7c9f9ac419417c001e2ea42270248df651c34f524b6646926847a8aa93139509b8bd72d86c875a3b341e9c16eb0a124ecfd6a5
@@ -0,0 +1,69 @@
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.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
3
+ # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the "Software"), to deal
@@ -25,8 +25,8 @@ require_relative 'wrapper'
25
25
  module Protocol
26
26
  module HTTP
27
27
  module Body
28
- # Invokes a callback once the body has finished reading.
29
- class Streamable < Wrapper
28
+ # Invokes a callback once the body has completed, either successfully or due to an error.
29
+ class Completable < Wrapper
30
30
  def self.wrap(message, &block)
31
31
  if body = message&.body and !body.empty?
32
32
  message.body = self.new(message.body, block)
@@ -60,6 +60,21 @@ module Protocol
60
60
  nil
61
61
  end
62
62
 
63
+ # Should the internal mechanism prefer to use {call}?
64
+ # @returns [Boolean]
65
+ def stream?
66
+ false
67
+ end
68
+
69
+ # Write the body to the given stream.
70
+ def call(stream)
71
+ while chunk = self.read
72
+ stream.write(chunk)
73
+ end
74
+ ensure
75
+ stream.close($!)
76
+ end
77
+
63
78
  # Read all remaining chunks into a buffered body and close the underlying input.
64
79
  def finish
65
80
  # Internally, this invokes `self.each` which then invokes `self.close`.
@@ -71,6 +71,14 @@ module Protocol
71
71
  def inspect
72
72
  @body.inspect
73
73
  end
74
+
75
+ def stream?
76
+ @body.stream?
77
+ end
78
+
79
+ def call(stream)
80
+ @body.call(stream)
81
+ end
74
82
  end
75
83
  end
76
84
  end
@@ -22,6 +22,6 @@
22
22
 
23
23
  module Protocol
24
24
  module HTTP
25
- VERSION = "0.20.1"
25
+ VERSION = "0.21.0"
26
26
  end
27
27
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: protocol-http
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.20.1
4
+ version: 0.21.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-19 00:00:00.000000000 Z
11
+ date: 2020-11-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: covered
@@ -64,11 +64,13 @@ files:
64
64
  - ".gitignore"
65
65
  - ".rspec"
66
66
  - README.md
67
+ - STREAMING.md
67
68
  - bake.rb
68
69
  - gems.rb
69
70
  - lib/protocol/http.rb
70
71
  - lib/protocol/http/accept_encoding.rb
71
72
  - lib/protocol/http/body/buffered.rb
73
+ - lib/protocol/http/body/completable.rb
72
74
  - lib/protocol/http/body/deflate.rb
73
75
  - lib/protocol/http/body/digestable.rb
74
76
  - lib/protocol/http/body/file.rb
@@ -78,7 +80,6 @@ files:
78
80
  - lib/protocol/http/body/reader.rb
79
81
  - lib/protocol/http/body/rewindable.rb
80
82
  - lib/protocol/http/body/stream.rb
81
- - lib/protocol/http/body/streamable.rb
82
83
  - lib/protocol/http/body/wrapper.rb
83
84
  - lib/protocol/http/content_encoding.rb
84
85
  - lib/protocol/http/cookie.rb
@@ -121,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
122
  - !ruby/object:Gem::Version
122
123
  version: '0'
123
124
  requirements: []
124
- rubygems_version: 3.0.3
125
+ rubygems_version: 3.1.2
125
126
  signing_key:
126
127
  specification_version: 4
127
128
  summary: Provides abstractions to handle HTTP protocols.