qeweney 0.7.5 → 0.8

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: c4a3ab1651dedfc78d9d35aa048db2d77e3eb8873d2cbd81d2944746b07ecee8
4
- data.tar.gz: f75a0a000b4c1f690885deb9b163baf9ead6d285ab2c58366821c26e995342d4
3
+ metadata.gz: ccff703b19924cf5dc8797b02a5338f12d990eacfa4fbcebf35123dae7f8840e
4
+ data.tar.gz: 385e5518ff1715d6e57b95f01ee6edfbcc45b49dfab3105d51014cd4a68a92cd
5
5
  SHA512:
6
- metadata.gz: e6aa575224a992f928fd42bec42979798e6656841eb676ca43996776ede7e24b303982a69376f735be0a5ec2ce02ced79b573a10353cd5b5d7097e168f4ce625
7
- data.tar.gz: 3870128bb83a47be1fee088bd128e8701098b92f301cc515e863da462001ea65af51018ab3e1ae0e9c2dfd7250fad310690917d47fa2afa8b3fd88c5b2cbb381
6
+ metadata.gz: 234aac50cd2f39f754d31fac0e401d06a8117f771de69d1840fcfb43f31b748df521a658eae3ff3989eb4683d76d2999fb2b510c80cef578218cf4d4b90f1ed1
7
+ data.tar.gz: b2c2b1f2a3109f5438862877aa0283c54a0818154f310fde257fdec01e92356972e88cbf3508050843d4434a6735f45ec79e9a559f9a8e3e1819c714957f3161
data/CHANGELOG.md CHANGED
@@ -1,6 +1,10 @@
1
+ ## 0.8 2021-03-10
2
+
3
+ - Pass request as first argument to all adapter methods
4
+
1
5
  ## 0.7.5 2021-03-08
2
6
 
3
- - Set content-type header in
7
+ - Set content-type header in `#serve_file`
4
8
 
5
9
  ## 0.7.4 2021-03-07
6
10
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- qeweney (0.7.5)
4
+ qeweney (0.8)
5
5
  escape_utils (~> 1.2.1)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -1,11 +1,71 @@
1
1
  # Qeweney
2
2
 
3
- ## Cross-library feature rich HTTP request / response API
3
+ ## Cross-library HTTP request / response API for servers
4
4
 
5
- Qeweney provides a uniform API for dealing with HTTP requests and responses.
5
+ Qeweney provides a uniform API for dealing with HTTP requests and responses on
6
+ the server side. Qeweney defines a uniform adapter interface that allows
7
+ handling incoming HTTP requests and sending HTTP responses over any protocol or
8
+ transport, be it HTTP/1, HTTP/2 or a Rack interface.
9
+
10
+ Qeweney is primarily designed to work with
11
+ [Tipi](https://github.com/digital-fabric/tipi), but can also be used directly
12
+ inside Rack apps, or to drive Rack apps.
6
13
 
7
14
  ## Features
8
15
 
9
- - Works with different web server APIs, notably Tipi, Digital Fabric, and Rack
10
- - Transport-agnostic
11
- - High-performance routing API inspired by Roda
16
+ - Works with different web server APIs, notably Tipi, Digital Fabric, and Rack.
17
+ - Transport-agnostic.
18
+ - High-performance routing API inspired by Roda.
19
+ - Rich API for extracting data from HTTP requests: form parsing, cookies, file
20
+ uploads, etc.
21
+ - Rich API for constructing HTTP responses: streaming responses, HTTP upgrades,
22
+ static file serving, delate and gzip encoding, caching etc.
23
+ - Suitable for both blocking and non-blocking concurrency models.
24
+ - Allows working with request before request body is read and parsed.
25
+
26
+ ## Overview
27
+
28
+ In Qeweney, the main class developers will interact with is `Qeweney::Request`,
29
+ which encapsulates an HTTP request (from the server's point of view), and offers
30
+ an API for extracting request information and responding to that request.
31
+
32
+ A request is always associated with an _adapter_, an object that implements the
33
+ Qeweney adapter interface, and that allows reading request bodies (for uploads
34
+ and form submissions) and sending responses.
35
+
36
+ ## The Qeweney Adapter Interface
37
+
38
+ ```ruby
39
+ class AdapterInterface
40
+ # Reads a chunk from the request body
41
+ # @req [Qeweney::Request] request for which the chunk is to be read
42
+ def get_body_chunk(req)
43
+ end
44
+
45
+ # Send a non-streaming response
46
+ # @req [Qeweney::Request] request for which the response is sent
47
+ # @body [String, nil] response body
48
+ # @headers [Hash] response headers
49
+ def respond(req, body, headers)
50
+ end
51
+
52
+ # Send only headers
53
+ # @req [Qeweney::Request] request for which the response is sent
54
+ # @headers [Hash] response headers
55
+ # @empty_response [boolean] whether response is empty
56
+ def send_headers(req, headers, empty_response: nil)
57
+ end
58
+
59
+ # Send a body chunk (this implies chunked transfer encoding)
60
+ # @req [Qeweney::Request] request for which the response is sent
61
+ # @body [String, nil] chunk
62
+ # @done [boolean] whether response is finished
63
+ def send_chunk(req, body, done: false)
64
+ end
65
+
66
+ # Finishes response
67
+ # @req [Qeweney::Request] request for which the response is sent
68
+ def finish(req)
69
+ end
70
+ end
71
+ ```
data/lib/qeweney/rack.rb CHANGED
@@ -32,20 +32,20 @@ module Qeweney
32
32
  headers
33
33
  end
34
34
 
35
- def respond(body, headers)
35
+ def respond(req, body, headers)
36
36
  @response_body << body
37
37
  @response_headers = headers
38
38
  end
39
39
 
40
- def send_headers(headers, empty_response: nil)
40
+ def send_headers(req, headers, empty_response: nil)
41
41
  @response_headers = headers
42
42
  end
43
43
 
44
- def send_chunk(body, done: false)
44
+ def send_chunk(req, body, done: false)
45
45
  @response_body << body
46
46
  end
47
47
 
48
- def finish
48
+ def finish(req)
49
49
  end
50
50
 
51
51
  def rack_response
@@ -34,7 +34,7 @@ module Qeweney
34
34
  return chunk
35
35
  end
36
36
 
37
- @message_complete ? nil : @adapter.get_body_chunk
37
+ @message_complete ? nil : @adapter.get_body_chunk(self)
38
38
  end
39
39
 
40
40
  def each_chunk
@@ -44,7 +44,7 @@ module Qeweney
44
44
  end
45
45
  @buffered_body_chunks = nil
46
46
  end
47
- while !@message_complete && (chunk = @adapter.get_body_chunk)
47
+ while !@message_complete && (chunk = @adapter.get_body_chunk(self))
48
48
  yield chunk
49
49
  end
50
50
  end
@@ -59,7 +59,7 @@ module Qeweney
59
59
  end
60
60
 
61
61
  def consume
62
- @adapter.consume_request
62
+ @adapter.consume_request(self)
63
63
  end
64
64
 
65
65
  def keep_alive?
@@ -68,7 +68,7 @@ module Qeweney
68
68
 
69
69
  def read
70
70
  buf = @buffered_body_chunks ? @buffered_body_chunks.join : nil
71
- while (chunk = @adapter.get_body_chunk)
71
+ while (chunk = @adapter.get_body_chunk(self))
72
72
  (buf ||= +'') << chunk
73
73
  end
74
74
  @buffered_body_chunks = nil
@@ -77,7 +77,7 @@ module Qeweney
77
77
  alias_method :body, :read
78
78
 
79
79
  def respond(body, headers = {})
80
- @adapter.respond(body, headers)
80
+ @adapter.respond(self, body, headers)
81
81
  @headers_sent = true
82
82
  end
83
83
 
@@ -85,24 +85,32 @@ module Qeweney
85
85
  return if @headers_sent
86
86
 
87
87
  @headers_sent = true
88
- @adapter.send_headers(headers, empty_response: empty_response)
88
+ @adapter.send_headers(self, headers, empty_response: empty_response)
89
89
  end
90
90
 
91
91
  def send_chunk(body, done: false)
92
92
  send_headers({}) unless @headers_sent
93
93
 
94
- @adapter.send_chunk(body, done: done)
94
+ @adapter.send_chunk(self, body, done: done)
95
95
  end
96
96
  alias_method :<<, :send_chunk
97
97
 
98
98
  def finish
99
99
  send_headers({}) unless @headers_sent
100
100
 
101
- @adapter.finish
101
+ @adapter.finish(self)
102
102
  end
103
103
 
104
104
  def headers_sent?
105
105
  @headers_sent
106
106
  end
107
+
108
+ def rx_incr(count)
109
+ headers[':rx'] ? headers[';rx'] += count : headers[':rx'] = count
110
+ end
111
+
112
+ def tx_incr(count)
113
+ headers[':tx'] ? headers[':tx'] += count : headers[':tx'] = count
114
+ end
107
115
  end
108
116
  end
@@ -58,6 +58,7 @@ module Qeweney
58
58
  def split_query_string(query)
59
59
  query.split('&').each_with_object({}) do |kv, h|
60
60
  k, v = kv.split('=')
61
+ p split_query_string: { k: k, v: v }
61
62
  h[k.to_sym] = URI.decode_www_form_component(v)
62
63
  end
63
64
  end
@@ -159,6 +159,7 @@ module Qeweney
159
159
 
160
160
  # TODO: send separate chunks for multi-part body
161
161
  # TODO: add support for streaming body
162
+ # TODO: add support for websocket
162
163
  end
163
164
  end
164
165
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Qeweney
4
- VERSION = '0.7.5'
4
+ VERSION = '0.8'
5
5
  end
@@ -8,7 +8,7 @@ class RedirectTest < MiniTest::Test
8
8
  r.redirect('/foo')
9
9
 
10
10
  assert_equal [
11
- [:respond, nil, {":status"=>302, "Location"=>"/foo"}]
11
+ [:respond, r, nil, {":status"=>302, "Location"=>"/foo"}]
12
12
  ], r.response_calls
13
13
  end
14
14
 
@@ -17,7 +17,7 @@ class RedirectTest < MiniTest::Test
17
17
  r.redirect('/bar', Qeweney::Status::MOVED_PERMANENTLY)
18
18
 
19
19
  assert_equal [
20
- [:respond, nil, {":status"=>301, "Location"=>"/bar"}]
20
+ [:respond, r, nil, {":status"=>301, "Location"=>"/bar"}]
21
21
  ], r.response_calls
22
22
  end
23
23
  end
@@ -37,7 +37,7 @@ class StaticFileResponeTest < MiniTest::Test
37
37
  r.serve_file('helper.rb', base_path: __dir__)
38
38
 
39
39
  assert_equal [
40
- [:respond, @content, { 'etag' => @etag, 'last-modified' => @last_modified }]
40
+ [:respond, r, @content, { 'etag' => @etag, 'last-modified' => @last_modified }]
41
41
  ], r.response_calls
42
42
  end
43
43
 
@@ -45,25 +45,25 @@ class StaticFileResponeTest < MiniTest::Test
45
45
  r = Qeweney.mock('if-none-match' => @etag)
46
46
  r.serve_file('helper.rb', base_path: __dir__)
47
47
  assert_equal [
48
- [:respond, nil, { 'etag' => @etag, ':status' => Qeweney::Status::NOT_MODIFIED }]
48
+ [:respond, r, nil, { 'etag' => @etag, ':status' => Qeweney::Status::NOT_MODIFIED }]
49
49
  ], r.response_calls
50
50
 
51
51
  r = Qeweney.mock('if-modified-since' => @last_modified)
52
52
  r.serve_file('helper.rb', base_path: __dir__)
53
53
  assert_equal [
54
- [:respond, nil, { 'etag' => @etag, ':status' => Qeweney::Status::NOT_MODIFIED }]
54
+ [:respond, r, nil, { 'etag' => @etag, ':status' => Qeweney::Status::NOT_MODIFIED }]
55
55
  ], r.response_calls
56
56
 
57
57
  r = Qeweney.mock('if-none-match' => 'foobar')
58
58
  r.serve_file('helper.rb', base_path: __dir__)
59
59
  assert_equal [
60
- [:respond, @content, { 'etag' => @etag, 'last-modified' => @last_modified }]
60
+ [:respond, r, @content, { 'etag' => @etag, 'last-modified' => @last_modified }]
61
61
  ], r.response_calls
62
62
 
63
63
  r = Qeweney.mock('if-modified-since' => Time.now.httpdate)
64
64
  r.serve_file('helper.rb', base_path: __dir__)
65
65
  assert_equal [
66
- [:respond, @content, { 'etag' => @etag, 'last-modified' => @last_modified }]
66
+ [:respond, r, @content, { 'etag' => @etag, 'last-modified' => @last_modified }]
67
67
  ], r.response_calls
68
68
  end
69
69
 
@@ -75,7 +75,7 @@ class StaticFileResponeTest < MiniTest::Test
75
75
  deflated_content = deflate.deflate(@content, Zlib::FINISH)
76
76
 
77
77
  assert_equal [
78
- [:respond, deflated_content, {
78
+ [:respond, r, deflated_content, {
79
79
  'etag' => @etag,
80
80
  'last-modified' => @last_modified,
81
81
  'vary' => 'Accept-Encoding',
@@ -96,7 +96,7 @@ class StaticFileResponeTest < MiniTest::Test
96
96
  gzipped_content = buf.string
97
97
 
98
98
  assert_equal [
99
- [:respond, gzipped_content, {
99
+ [:respond, r, gzipped_content, {
100
100
  'etag' => @etag,
101
101
  'last-modified' => @last_modified,
102
102
  'vary' => 'Accept-Encoding',
@@ -109,7 +109,7 @@ class StaticFileResponeTest < MiniTest::Test
109
109
  r = Qeweney.mock
110
110
  r.serve_file('foo.rb', base_path: __dir__)
111
111
  assert_equal [
112
- [:respond, nil, { ':status' => Qeweney::Status::NOT_FOUND }]
112
+ [:respond, r, nil, { ':status' => Qeweney::Status::NOT_FOUND }]
113
113
  ], r.response_calls
114
114
  end
115
115
  end
@@ -120,7 +120,7 @@ class UpgradeTest < MiniTest::Test
120
120
  r.upgrade('df')
121
121
 
122
122
  assert_equal [
123
- [:respond, nil, {
123
+ [:respond, r, nil, {
124
124
  ':status' => 101,
125
125
  'Upgrade' => 'df',
126
126
  'Connection' => 'upgrade'
@@ -132,7 +132,7 @@ class UpgradeTest < MiniTest::Test
132
132
  r.upgrade('df', { 'foo' => 'bar' })
133
133
 
134
134
  assert_equal [
135
- [:respond, nil, {
135
+ [:respond, r, nil, {
136
136
  ':status' => 101,
137
137
  'Upgrade' => 'df',
138
138
  'Connection' => 'upgrade',
@@ -155,7 +155,7 @@ class UpgradeTest < MiniTest::Test
155
155
  accept = Digest::SHA1.base64digest('abcdefghij258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
156
156
 
157
157
  assert_equal [
158
- [:respond, nil, {
158
+ [:respond, r, nil, {
159
159
  ':status' => 101,
160
160
  'Upgrade' => 'websocket',
161
161
  'Connection' => 'upgrade',
@@ -179,7 +179,7 @@ class ServeRackTest < MiniTest::Test
179
179
  })
180
180
 
181
181
  assert_equal [
182
- [:respond, "get /foo/bar", {':status' => 404, 'Foo' => 'Bar' }]
182
+ [:respond, r, 'get /foo/bar', {':status' => 404, 'Foo' => 'Bar' }]
183
183
  ], r.response_calls
184
184
  end
185
185
  end
data/test/test_routing.rb CHANGED
@@ -19,27 +19,27 @@ class RoutingTest < MiniTest::Test
19
19
  def test_app1
20
20
  r = Qeweney.mock(':path' => '/foo')
21
21
  App1.(r)
22
- assert_equal [[:respond, nil, { ':status' => 404 }]], r.response_calls
22
+ assert_equal [[:respond, r, nil, { ':status' => 404 }]], r.response_calls
23
23
 
24
24
  r = Qeweney.mock(':path' => '/')
25
25
  App1.(r)
26
- assert_equal [[:respond, nil, { ':status' => 302, 'Location' => '/hello' }]], r.response_calls
26
+ assert_equal [[:respond, r, nil, { ':status' => 302, 'Location' => '/hello' }]], r.response_calls
27
27
 
28
28
  r = Qeweney.mock(':path' => '/hello', ':method' => 'foo')
29
29
  App1.(r)
30
- assert_equal [[:respond, nil, { ':status' => 404 }]], r.response_calls
30
+ assert_equal [[:respond, r, nil, { ':status' => 404 }]], r.response_calls
31
31
 
32
32
  r = Qeweney.mock(':path' => '/hello', ':method' => 'get')
33
33
  App1.(r)
34
- assert_equal [[:respond, 'Hello', {}]], r.response_calls
34
+ assert_equal [[:respond, r, 'Hello', {}]], r.response_calls
35
35
 
36
36
  r = Qeweney.mock(':path' => '/hello', ':method' => 'post')
37
37
  App1.(r)
38
- assert_equal [[:respond, nil, { ':status' => 302, 'Location' => '/' }]], r.response_calls
38
+ assert_equal [[:respond, r, nil, { ':status' => 302, 'Location' => '/' }]], r.response_calls
39
39
 
40
40
  r = Qeweney.mock(':path' => '/hello/world', ':method' => 'get')
41
41
  App1.(r)
42
- assert_equal [[:respond, 'Hello world', {}]], r.response_calls
42
+ assert_equal [[:respond, r, 'Hello world', {}]], r.response_calls
43
43
  end
44
44
 
45
45
  def test_on_root
@@ -58,19 +58,19 @@ class RoutingTest < MiniTest::Test
58
58
 
59
59
  r = Qeweney.mock(':path' => '/')
60
60
  app.(r)
61
- assert_equal [[:respond, 'root', {}]], r.response_calls
61
+ assert_equal [[:respond, r, 'root', {}]], r.response_calls
62
62
 
63
63
  r = Qeweney.mock(':path' => '/foo')
64
64
  app.(r)
65
- assert_equal [[:respond, 'foo root', {}]], r.response_calls
65
+ assert_equal [[:respond, r, 'foo root', {}]], r.response_calls
66
66
 
67
67
  r = Qeweney.mock(':path' => '/foo/bar')
68
68
  app.(r)
69
- assert_equal [[:respond, 'bar root', {}]], r.response_calls
69
+ assert_equal [[:respond, r, 'bar root', {}]], r.response_calls
70
70
 
71
71
  r = Qeweney.mock(':path' => '/foo/bar/baz')
72
72
  app.(r)
73
- assert_equal [[:respond, 'baz root', {}]], r.response_calls
73
+ assert_equal [[:respond, r, 'baz root', {}]], r.response_calls
74
74
  end
75
75
 
76
76
  def test_relative_path
@@ -90,27 +90,27 @@ class RoutingTest < MiniTest::Test
90
90
  r = Qeweney.mock(':path' => '/')
91
91
  app.(r)
92
92
  assert_equal '/', default_relative_path
93
- assert_equal [[:respond, 'ROOT/', {}]], r.response_calls
93
+ assert_equal [[:respond, r, 'ROOT/', {}]], r.response_calls
94
94
 
95
95
 
96
96
  r = Qeweney.mock(':path' => '/foo/bar/baz')
97
97
  app.(r)
98
98
  assert_equal '/foo/bar/baz', default_relative_path
99
- assert_equal [[:respond, 'FOO/bar/baz', {}]], r.response_calls
99
+ assert_equal [[:respond, r, 'FOO/bar/baz', {}]], r.response_calls
100
100
 
101
101
  r = Qeweney.mock(':path' => '/bar/a/b/c')
102
102
  app.(r)
103
103
  assert_equal '/bar/a/b/c', default_relative_path
104
- assert_equal [[:respond, 'BAR/a/b/c', {}]], r.response_calls
104
+ assert_equal [[:respond, r, 'BAR/a/b/c', {}]], r.response_calls
105
105
 
106
106
  r = Qeweney.mock(':path' => '/bar/baz/b/c')
107
107
  app.(r)
108
108
  assert_equal '/bar/baz/b/c', default_relative_path
109
- assert_equal [[:respond, 'BAR/BAZ/b/c', {}]], r.response_calls
109
+ assert_equal [[:respond, r, 'BAR/BAZ/b/c', {}]], r.response_calls
110
110
 
111
111
  r = Qeweney.mock(':path' => '/baz/d/e/f')
112
112
  app.(r)
113
113
  assert_equal '/baz/d/e/f', default_relative_path
114
- assert_equal [[:respond, '/d/e/f', {}]], r.response_calls
114
+ assert_equal [[:respond, r, '/d/e/f', {}]], r.response_calls
115
115
  end
116
116
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qeweney
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.5
4
+ version: '0.8'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-08 00:00:00.000000000 Z
11
+ date: 2021-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: escape_utils