qeweney 0.7.5 → 0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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