qeweney 0.14 → 0.18

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: 3f515f9637f0d993c9dfbea4d425695f76915871ad0c62e090cbb31a4a57b4a7
4
- data.tar.gz: 80a6bf6da6a0d2b85771deff0cb24ad9bcf389e4af37042a6fd62c215617a657
3
+ metadata.gz: 70524001d652ee7b23a7a08b8c09aae2a7bf484148fa693b1c6b23a9c868ba51
4
+ data.tar.gz: 17c5254f9b07b242cc673219f2738bd6f4f0176bb4f076273b10f2aed9a21530
5
5
  SHA512:
6
- metadata.gz: d08170e772a3fad93d49a06bc8ee6c656e5057e5368d3dfba6687d1f838dff50e7cfb8895315480f3f85ea519d8ee0ae20a3a480ee26293bd86bfec2c4912918
7
- data.tar.gz: a1918424e95a70891eb4f8fb1b163cdf62167bb3847c16192be9f8dffd95c77513cca4f0e6d4a759275b14bc7891d770585b3625b4601177195ade7a731f6814
6
+ metadata.gz: be74c2f1c96b8a4b4cd6b7e081f71b2c2019c2ab9c37b8680bdead5f4de1eddddca1522658ee56869837851bb6752fc147b3ffb42d0a49c4d7bd1f98822a0c98
7
+ data.tar.gz: 6f252321dc1f79d69e635d47ee34efefd1274a704122bfc736aa589618836d6e29e0cbb495669d1a96321569e75559c0a441ff0ebf91c71c4f38b2543383d27a
@@ -0,0 +1 @@
1
+ github: ciconia
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## 0.18 2022-02-16
2
+
3
+ - Implement `Request#rewrite!` (#2)
4
+
5
+ ## 0.17 2022-02-10
6
+
7
+ - Fix default return value in `TestAdapter#status`
8
+
9
+ ## 0.16 2022-01-22
10
+
11
+ - Use frozen empty hash as default headers in response methods
12
+ - Add `text` key in registered MIME types
13
+
14
+ ## 0.15 2022-01-08
15
+
16
+ - Add `TestAdapter` class for testing requests and responses
17
+ - Add `RoutingMethods#reject` method
18
+
1
19
  ## 0.14 2021-08-10
2
20
 
3
21
  - Fix Request#on with paths containing slashes
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- qeweney (0.14)
4
+ qeweney (0.18)
5
5
  escape_utils (~> 1.2.1)
6
6
 
7
7
  GEM
@@ -21,7 +21,7 @@ GEM
21
21
  ruby-progressbar (1.11.0)
22
22
 
23
23
  PLATFORMS
24
- ruby
24
+ x86_64-linux
25
25
 
26
26
  DEPENDENCIES
27
27
  benchmark-ips (~> 2.8.3)
@@ -31,4 +31,4 @@ DEPENDENCIES
31
31
  rake (~> 12.3.3)
32
32
 
33
33
  BUNDLED WITH
34
- 2.1.4
34
+ 2.3.3
@@ -104,7 +104,7 @@ OptimizedRubyApp = ->(r) do
104
104
  else
105
105
  return r.respond('Hello')
106
106
  end
107
- elsif method == 'post'
107
+ elsif method == 'post'
108
108
  # puts 'Someone said Hello'
109
109
  return r.redirect('/')
110
110
  end
@@ -8,6 +8,7 @@ module Qeweney
8
8
  'css' => 'text/css',
9
9
  'js' => 'application/javascript',
10
10
  'txt' => 'text/plain',
11
+ 'text' => 'text/plain',
11
12
 
12
13
  'gif' => 'image/gif',
13
14
  'jpg' => 'image/jpeg',
data/lib/qeweney/rack.rb CHANGED
@@ -74,15 +74,15 @@ module Qeweney
74
74
  def initialize(request)
75
75
  @request = request
76
76
  end
77
-
77
+
78
78
  def gets; end
79
-
79
+
80
80
  def read(length = nil, outbuf = nil); end
81
-
81
+
82
82
  def each(&block)
83
83
  @request.each_chunk(&block)
84
84
  end
85
-
85
+
86
86
  def rewind; end
87
87
  end
88
88
 
@@ -110,7 +110,7 @@ module Qeweney
110
110
  'rack.multipart.buffer_size' => nil,
111
111
  'rack.multipar.tempfile_factory' => nil
112
112
  }
113
-
113
+
114
114
  HTTP_HEADER_RE = /^HTTP_(.+)$/.freeze
115
115
 
116
116
  def self.rack_env_value_from_request(request, key)
@@ -15,13 +15,12 @@ module Qeweney
15
15
  extend RequestInfoClassMethods
16
16
 
17
17
  attr_reader :headers, :adapter
18
- attr_accessor :__next__
19
-
18
+
20
19
  def initialize(headers, adapter)
21
20
  @headers = headers
22
21
  @adapter = adapter
23
22
  end
24
-
23
+
25
24
  def buffer_body_chunk(chunk)
26
25
  @buffered_body_chunks ||= []
27
26
  @buffered_body_chunks << chunk
@@ -38,7 +37,7 @@ module Qeweney
38
37
 
39
38
  @adapter.get_body_chunk(self, buffered_only)
40
39
  end
41
-
40
+
42
41
  def each_chunk
43
42
  if @buffered_body_chunks
44
43
  while (chunk = @buffered_body_chunks.shift)
@@ -68,29 +67,31 @@ module Qeweney
68
67
  def complete?
69
68
  @adapter.complete?(self)
70
69
  end
71
-
72
- def respond(body, headers = {})
70
+
71
+ EMPTY_HEADERS = {}.freeze
72
+
73
+ def respond(body, headers = EMPTY_HEADERS)
73
74
  @adapter.respond(self, body, headers)
74
75
  @headers_sent = true
75
76
  end
76
-
77
- def send_headers(headers = {}, empty_response = false)
77
+
78
+ def send_headers(headers = EMPTY_HEADERS, empty_response = false)
78
79
  return if @headers_sent
79
-
80
+
80
81
  @headers_sent = true
81
82
  @adapter.send_headers(self, headers, empty_response: empty_response)
82
83
  end
83
-
84
+
84
85
  def send_chunk(body, done: false)
85
86
  send_headers({}) unless @headers_sent
86
-
87
+
87
88
  @adapter.send_chunk(self, body, done: done)
88
89
  end
89
90
  alias_method :<<, :send_chunk
90
-
91
+
91
92
  def finish
92
93
  send_headers({}) unless @headers_sent
93
-
94
+
94
95
  @adapter.finish(self)
95
96
  end
96
97
 
@@ -25,43 +25,58 @@ module Qeweney
25
25
  def protocol
26
26
  @protocol ||= @adapter.protocol
27
27
  end
28
-
28
+
29
29
  def method
30
30
  @method ||= @headers[':method'].downcase
31
31
  end
32
-
32
+
33
33
  def scheme
34
34
  @scheme ||= @headers[':scheme']
35
35
  end
36
-
36
+
37
+ # Rewrites the request path by replacing the given src with the given
38
+ # replacement.
39
+ #
40
+ # @param src [String, Regexp] src pattern
41
+ # @param replacement [String] replacement
42
+ # @return [Qeweney::Request] self
43
+ def rewrite!(src, replacement)
44
+ @headers[':path'] = @headers[':path']
45
+ .gsub(src, replacement)
46
+ .gsub('//', '/')
47
+ @path = nil
48
+ @uri = nil
49
+ @full_uri = nil
50
+ self
51
+ end
52
+
37
53
  def uri
38
54
  @uri ||= URI.parse(@headers[':path'] || '')
39
55
  end
40
-
56
+
41
57
  def full_uri
42
58
  @full_uri = "#{scheme}://#{host}#{uri}"
43
59
  end
44
-
60
+
45
61
  def path
46
62
  @path ||= uri.path
47
63
  end
48
-
64
+
49
65
  def query_string
50
66
  @query_string ||= uri.query
51
67
  end
52
-
68
+
53
69
  def query
54
70
  return @query if @query
55
-
71
+
56
72
  @query = (q = uri.query) ? parse_query(q) : {}
57
73
  end
58
-
74
+
59
75
  QUERY_KV_REGEXP = /([^=]+)(?:=(.*))?/
60
-
76
+
61
77
  def parse_query(query)
62
78
  query.split('&').each_with_object({}) do |kv, h|
63
79
  k, v = kv.match(QUERY_KV_REGEXP)[1..2]
64
- # k, v = kv.split('=')
65
80
  h[k.to_sym] = v ? URI.decode_www_form_component(v) : true
66
81
  end
67
82
  end
@@ -89,13 +104,13 @@ module Qeweney
89
104
 
90
105
  COOKIE_RE = /^([^=]+)=(.*)$/.freeze
91
106
  SEMICOLON = ';'
92
-
107
+
93
108
  def parse_cookies(cookies)
94
109
  return {} unless cookies
95
110
 
96
111
  cookies.split(SEMICOLON).each_with_object({}) do |c, h|
97
112
  raise BadRequestError, 'Invalid cookie format' unless c.strip =~ COOKIE_RE
98
-
113
+
99
114
  key, value = Regexp.last_match[1..2]
100
115
  h[key] = EscapeUtils.unescape_uri(value)
101
116
  end
@@ -151,7 +166,7 @@ module Qeweney
151
166
  break if header.empty?
152
167
 
153
168
  next unless header =~ /^([^\:]+)\:\s?(.+)$/
154
-
169
+
155
170
  headers[Regexp.last_match(1).downcase] = Regexp.last_match(2)
156
171
  end
157
172
  # remove trailing \r\n
@@ -169,13 +184,13 @@ module Qeweney
169
184
  body.force_encoding(Encoding::UTF_8) unless body.encoding == Encoding::UTF_8
170
185
  body.split('&').each_with_object({}) do |i, m|
171
186
  raise 'Invalid parameter format' unless i =~ PARAMETER_RE
172
-
187
+
173
188
  k = Regexp.last_match(1)
174
189
  raise 'Invalid parameter size' if k.size > MAX_PARAMETER_NAME_SIZE
175
-
190
+
176
191
  v = Regexp.last_match(2)
177
192
  raise 'Invalid parameter size' if v.size > MAX_PARAMETER_VALUE_SIZE
178
-
193
+
179
194
  m[EscapeUtils.unescape_uri(k)] = EscapeUtils.unescape_uri(v)
180
195
  end
181
196
  end
@@ -14,7 +14,7 @@ module Qeweney
14
14
  def file_stat_to_etag(stat)
15
15
  "#{stat.mtime.to_i.to_s(36)}#{stat.size.to_s(36)}"
16
16
  end
17
-
17
+
18
18
  def file_stat_to_last_modified(stat)
19
19
  stat.mtime.httpdate
20
20
  end
@@ -114,7 +114,7 @@ module Qeweney
114
114
  if (modified_since = headers['if-modified-since'])
115
115
  return true if modified_since == last_modified
116
116
  end
117
-
117
+
118
118
  false
119
119
  end
120
120
 
@@ -7,10 +7,11 @@ module Qeweney
7
7
 
8
8
  module RoutingMethods
9
9
  def route(&block)
10
- (@path_parts ||= path.split('/'))[@path_parts_idx ||= 1]
10
+ @path_parts ||= path.split('/')
11
+ @path_parts_idx ||= 1
11
12
  res = catch(:stop) { yield self }
12
13
  return if res == :found
13
-
14
+
14
15
  respond(nil, ':status' => 404)
15
16
  end
16
17
 
@@ -39,11 +40,11 @@ module Qeweney
39
40
 
40
41
  def on(route, &block)
41
42
  return route_found(&block) unless route
42
-
43
+
43
44
  route_parts = route.split('/')
44
45
  route_length = route_parts.size
45
46
  return unless @path_parts[@path_parts_idx, route_length] == route_parts
46
-
47
+
47
48
  enter_route(route_length)
48
49
  route_found(&block)
49
50
  leave_route(route_length)
@@ -72,22 +73,22 @@ module Qeweney
72
73
 
73
74
  route_found(&block)
74
75
  end
75
-
76
+
76
77
  def on_get(route = nil, &block)
77
78
  return unless method == 'get'
78
-
79
+
79
80
  on(route, &block)
80
81
  end
81
-
82
+
82
83
  def on_post(route = nil, &block)
83
84
  return unless method == 'post'
84
-
85
+
85
86
  on(route, &block)
86
87
  end
87
88
 
88
89
  def on_options(route = nil, &block)
89
90
  return unless method == 'options'
90
-
91
+
91
92
  on(route, &block)
92
93
  end
93
94
 
@@ -124,6 +125,11 @@ module Qeweney
124
125
  on_upgrade('websocket', &block)
125
126
  end
126
127
 
128
+ def reject(body, status)
129
+ respond(body, ':status' => status)
130
+ throw :stop, :found
131
+ end
132
+
127
133
  def stop_routing
128
134
  throw :stop, :found
129
135
  end
@@ -5,7 +5,7 @@ module Qeweney
5
5
  module Status
6
6
  # translated from https://golang.org/pkg/net/http/#pkg-constants
7
7
  # https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
8
-
8
+
9
9
  CONTINUE = 100 # RFC 7231, 6.2.1
10
10
  SWITCHING_PROTOCOLS = 101 # RFC 7231, 6.2.2
11
11
  PROCESSING = 102 # RFC 2518, 10.1
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Qeweney
4
+ class TestAdapter
5
+ attr_reader :body, :headers
6
+
7
+ def get_body_chunk
8
+ nil
9
+ end
10
+
11
+ def respond(req, body, headers)
12
+ @body = body
13
+ @headers = headers
14
+ end
15
+
16
+ def status
17
+ headers[':status'] || Qeweney::Status::OK
18
+ end
19
+
20
+ def self.mock(headers = {})
21
+ headers[':method'] ||= ''
22
+ headers[':path'] ||= ''
23
+ Request.new(headers, new)
24
+ end
25
+ end
26
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Qeweney
4
- VERSION = '0.14'
4
+ VERSION = '0.18'
5
5
  end
data/lib/qeweney.rb CHANGED
@@ -5,4 +5,3 @@ end
5
5
 
6
6
  require_relative 'qeweney/request.rb'
7
7
  require_relative 'qeweney/status.rb'
8
-
data/test/helper.rb CHANGED
@@ -8,7 +8,6 @@ require 'fileutils'
8
8
  require_relative './coverage' if ENV['COVERAGE']
9
9
 
10
10
  require 'minitest/autorun'
11
- require 'minitest/reporters'
12
11
 
13
12
  module Qeweney
14
13
  class MockAdapter
@@ -35,7 +34,3 @@ module Qeweney
35
34
  end
36
35
  end
37
36
  end
38
-
39
- Minitest::Reporters.use! [
40
- Minitest::Reporters::SpecReporter.new
41
- ]
@@ -65,4 +65,26 @@ class RequestInfoTest < MiniTest::Test
65
65
  'signin_ref' => '/'
66
66
  }, r.cookies)
67
67
  end
68
+
69
+ def test_rewrite!
70
+ r = Qeweney.mock(
71
+ ':scheme' => 'https',
72
+ 'host' => 'foo.bar',
73
+ ':path' => '/hey/ho?a=b&c=d'
74
+ )
75
+
76
+ assert_equal '/hey/ho', r.path
77
+ assert_equal URI.parse('/hey/ho?a=b&c=d'), r.uri
78
+ assert_equal 'https://foo.bar/hey/ho?a=b&c=d', r.full_uri
79
+
80
+ r.rewrite!('/hhh', '/')
81
+ assert_equal '/hey/ho', r.path
82
+ assert_equal URI.parse('/hey/ho?a=b&c=d'), r.uri
83
+ assert_equal 'https://foo.bar/hey/ho?a=b&c=d', r.full_uri
84
+
85
+ r.rewrite!('/hey', '/')
86
+ assert_equal '/ho', r.path
87
+ assert_equal URI.parse('/ho?a=b&c=d'), r.uri
88
+ assert_equal 'https://foo.bar/ho?a=b&c=d', r.full_uri
89
+ end
68
90
  end
@@ -73,7 +73,7 @@ class StaticFileResponeTest < MiniTest::Test
73
73
 
74
74
  deflate = Zlib::Deflate.new
75
75
  deflated_content = deflate.deflate(@content, Zlib::FINISH)
76
-
76
+
77
77
  assert_equal [
78
78
  [:respond, r, deflated_content, {
79
79
  'etag' => @etag,
@@ -94,7 +94,7 @@ class StaticFileResponeTest < MiniTest::Test
94
94
  z.flush
95
95
  z.close
96
96
  gzipped_content = buf.string
97
-
97
+
98
98
  assert_equal [
99
99
  [:respond, r, gzipped_content, {
100
100
  'etag' => @etag,
@@ -127,7 +127,7 @@ class UpgradeTest < MiniTest::Test
127
127
  }]
128
128
  ], r.response_calls
129
129
 
130
-
130
+
131
131
  r = Qeweney.mock
132
132
  r.upgrade('df', { 'foo' => 'bar' })
133
133
 
@@ -148,7 +148,7 @@ class UpgradeTest < MiniTest::Test
148
148
  'sec-websocket-version' => '23',
149
149
  'sec-websocket-key' => 'abcdefghij'
150
150
  )
151
-
151
+
152
152
  assert_equal 'websocket', r.upgrade_protocol
153
153
 
154
154
  r.upgrade_to_websocket('foo' => 'baz')
data/test/test_routing.rb CHANGED
@@ -81,7 +81,7 @@ class RoutingTest < MiniTest::Test
81
81
  default_relative_path = r.route_relative_path
82
82
  r.on_root { r.respond(File.join('ROOT', r.route_relative_path)) }
83
83
  r.on('foo') { r.respond(File.join('FOO', r.route_relative_path)) }
84
- r.on('bar') {
84
+ r.on('bar') {
85
85
  r.on('baz') { r.respond(File.join('BAR/BAZ', r.route_relative_path)) }
86
86
  r.default { r.respond(File.join('BAR', r.route_relative_path)) }
87
87
  }
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+ require 'qeweney/test_adapter'
5
+
6
+ class TestAdapterTest < MiniTest::Test
7
+ def test_test_adapter
8
+ adapter = Qeweney::TestAdapter.new
9
+ req = Qeweney::Request.new({ ':path' => '/foo' }, adapter)
10
+ req.respond('bar', 'Content-Type' => 'baz')
11
+
12
+ assert_equal 'bar', adapter.body
13
+ assert_equal({'Content-Type' => 'baz'}, adapter.headers)
14
+ end
15
+ 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.14'
4
+ version: '0.18'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-10 00:00:00.000000000 Z
11
+ date: 2022-02-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: escape_utils
@@ -80,13 +80,14 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: 2.8.3
83
- description:
83
+ description:
84
84
  email: sharon@noteflakes.com
85
85
  executables: []
86
86
  extensions: []
87
87
  extra_rdoc_files:
88
88
  - README.md
89
89
  files:
90
+ - ".github/FUNDING.yml"
90
91
  - ".github/workflows/test.yml"
91
92
  - ".gitignore"
92
93
  - CHANGELOG.md
@@ -105,6 +106,7 @@ files:
105
106
  - lib/qeweney/response.rb
106
107
  - lib/qeweney/routing.rb
107
108
  - lib/qeweney/status.rb
109
+ - lib/qeweney/test_adapter.rb
108
110
  - lib/qeweney/version.rb
109
111
  - qeweney.gemspec
110
112
  - test/helper.rb
@@ -112,12 +114,13 @@ files:
112
114
  - test/test_request_info.rb
113
115
  - test/test_response.rb
114
116
  - test/test_routing.rb
117
+ - test/test_test_adapter.rb
115
118
  homepage: http://github.com/digital-fabric/qeweney
116
119
  licenses:
117
120
  - MIT
118
121
  metadata:
119
122
  source_code_uri: https://github.com/digital-fabric/qeweney
120
- post_install_message:
123
+ post_install_message:
121
124
  rdoc_options:
122
125
  - "--title"
123
126
  - Qeweney
@@ -136,8 +139,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
136
139
  - !ruby/object:Gem::Version
137
140
  version: '0'
138
141
  requirements: []
139
- rubygems_version: 3.1.6
140
- signing_key:
142
+ rubygems_version: 3.3.3
143
+ signing_key:
141
144
  specification_version: 4
142
145
  summary: Qeweney - cross library HTTP request / response API
143
146
  test_files: []