webmachine 2.0.1 → 2.0.2

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: 50f6f80075000018960c21cd0e313ee59278caba937809ce8f0d23e9f8b03e5c
4
- data.tar.gz: 60bdc1f1592aa26ddee4dc2cfa6bd0f44aae05a031a6b16194a8efce4ea9ed72
3
+ metadata.gz: 34385e0209af616a39bbd85807eb5a1989e7c4119757de7de598404aca768e7a
4
+ data.tar.gz: f850905e898bd592b2c5ef61a15e67c005a16ee9dadae1fc6dc09371dd043473
5
5
  SHA512:
6
- metadata.gz: 31b0c2810bc7c2030bfc5eae7f20d0e9d3a057d76b75fdaddda0cbf97a64306735597c6a435653adce799088a525ec01d373acf210fb98af55af78ae89fc09cc
7
- data.tar.gz: '097062c4470c980ada7bab9b0a26af4620b4689661e81168be662967cd37ebea5d3aab2d730138ce221947d6e5a410532cfbcd3ce72a0fcf51bf47eeb0fc03ee'
6
+ metadata.gz: da0492a20458fea3a771532b6c144bd7e0b65bd517d9d951d7669f8772fb239fe9028717cf8778ed51de44cd600da7c14799db20035bf8f33673a15aa1618c55
7
+ data.tar.gz: 0febf4b1cc9425928352377bfc191bae02a27696e6c18cac1f0f1f5d865fefe0143fc9e92be3d1c36f17e54a4f4e1434b7b917f605bfb916607894aed524f5c9
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  ### HEAD
2
2
 
3
+ ### 2.0.2
4
+
5
+ * Support Rack version 3
6
+ * Support Ruby 3.4 and 4.0
7
+ * Apply RuboCop fixes
8
+ * Clean up gemspec configuration
9
+ * Organise development and test dependencies in the Gemfile
10
+
3
11
  ### 2.0.1 Feb 27, 2024
4
12
 
5
13
  * Don't include the `doc/`, `pkg/`, or `vendor/` directory in the gem package
data/examples/logging.rb CHANGED
@@ -34,7 +34,7 @@ App = Webmachine::Application.new do |app|
34
34
 
35
35
  app.configure do |config|
36
36
  config.adapter = :WEBrick
37
- config.adapter_options = {AccessLog: [], Logger: Logger.new('/dev/null')}
37
+ config.adapter_options = {AccessLog: [], Logger: Logger.new(File::NULL)}
38
38
  end
39
39
  end
40
40
 
@@ -41,7 +41,7 @@ module Webmachine
41
41
  DEFAULT_OPTIONS = {}
42
42
 
43
43
  REQUEST_URI = 'REQUEST_URI'.freeze
44
- VERSION_STRING = "#{Webmachine::SERVER_STRING} Rack/#{::Rack.version}".freeze
44
+ VERSION_STRING = "#{Webmachine::SERVER_STRING} Rack/#{::Rack.release}".freeze
45
45
  NEWLINE = "\n".freeze
46
46
 
47
47
  # Start the Rack adapter
@@ -52,7 +52,12 @@ module Webmachine
52
52
  Host: application.configuration.ip
53
53
  }).merge(application.configuration.adapter_options)
54
54
 
55
- @server = ::Rack::Server.new(options)
55
+ if rack_v3?
56
+ require 'rackup'
57
+ @server = ::Rackup::Server.new(options)
58
+ else
59
+ @server = ::Rack::Server.new(options)
60
+ end
56
61
  @server.start
57
62
  end
58
63
 
@@ -70,7 +75,7 @@ module Webmachine
70
75
  response.headers[SERVER] = VERSION_STRING
71
76
 
72
77
  rack_status = response.code
73
- rack_headers = response.headers.flattened(NEWLINE)
78
+ rack_headers = build_rack_response_headers(response.headers)
74
79
  rack_body = case response.body
75
80
  when String # Strings are enumerable in ruby 1.8
76
81
  [response.body]
@@ -78,10 +83,25 @@ module Webmachine
78
83
  if (io_body = IO.try_convert(response.body))
79
84
  io_body
80
85
  elsif response.body.respond_to?(:call)
81
- Webmachine::ChunkedBody.new(Array(response.body.call))
86
+ if rack_v3?
87
+ # In Rack 3 the server (e.g. WEBrick via Rackup) buffers the body
88
+ # and applies chunked encoding itself when Transfer-Encoding is set,
89
+ # so we must not pre-encode with ChunkedBody.
90
+ [response.body.call]
91
+ else
92
+ # Rack 2's WEBrick handler sends the body as-is; ChunkedBody is
93
+ # required to produce valid chunked-encoded wire data.
94
+ Webmachine::ChunkedBody.new(Array(response.body.call))
95
+ end
82
96
  elsif response.body.respond_to?(:each)
83
- # This might be an IOEncoder with a Content-Length, which shouldn't be chunked.
84
- if response.headers[TRANSFER_ENCODING] == 'chunked'
97
+ if rack_v3?
98
+ # Return the plain enumerable. Rackup buffers it into a String then
99
+ # WEBrick chunks that String when Transfer-Encoding: chunked is set.
100
+ response.body
101
+ elsif response.headers[TRANSFER_ENCODING] == 'chunked'
102
+ # Rack 2: only pre-encode bodies that are already marked chunked;
103
+ # IOEncoder bodies carry their own Content-Length and must not be
104
+ # wrapped.
85
105
  Webmachine::ChunkedBody.new(response.body)
86
106
  else
87
107
  response.body
@@ -97,6 +117,45 @@ module Webmachine
97
117
 
98
118
  protected
99
119
 
120
+ # Build a Rack-compatible response headers hash from Webmachine's response
121
+ # headers.
122
+ #
123
+ # Header names are always lowercased: this is required by Rack 3 and is
124
+ # harmless for Rack 2 (all Rack 2 handlers match header names
125
+ # case-insensitively).
126
+ #
127
+ # The +set-cookie+ value is formatted differently per Rack version:
128
+ #
129
+ # * Rack 3 / Rackup: the value must be an Array. Rackup's WEBrick handler
130
+ # deletes the lowercase +set-cookie+ key and calls
131
+ # +res.cookies.concat(Array(value))+, emitting one Set-Cookie line per
132
+ # cookie. Joining with +\n+ instead would produce a header value
133
+ # containing a newline, which WEBrick 1.9+ rejects as
134
+ # +WEBrick::HTTPResponse::InvalidHeader+.
135
+ #
136
+ # * Rack 2 / Rack::Handler::WEBrick: the handler splits on +\n+ before
137
+ # adding cookies (+vs.split("\n")+), so the value must be a newline-joined
138
+ # String. Passing an Array causes a +NoMethodError+ because +Array+ does
139
+ # not define +#split+.
140
+ def build_rack_response_headers(response_headers)
141
+ response_headers.each_with_object({}) do |(key, value), h|
142
+ rack_key = key.downcase
143
+ h[rack_key] = if rack_key == 'set-cookie'
144
+ if rack_v3?
145
+ # Array lets Rackup emit one Set-Cookie header per cookie.
146
+ Array(value)
147
+ else
148
+ # Rack 2's handler splits on \n; give it a newline-joined String.
149
+ Array(value).join(NEWLINE)
150
+ end
151
+ elsif value.is_a?(Array)
152
+ value.join(NEWLINE)
153
+ else
154
+ value
155
+ end
156
+ end
157
+ end
158
+
100
159
  def routing_tokens(rack_req)
101
160
  nil # no-op for default, un-mapped rack adapter
102
161
  end
@@ -107,6 +166,11 @@ module Webmachine
107
166
 
108
167
  private
109
168
 
169
+ # Returns true when running under Rack 3.x.
170
+ def rack_v3?
171
+ ::Rack.release.start_with?('3.')
172
+ end
173
+
110
174
  def build_webmachine_request(rack_req, headers)
111
175
  RackRequest.new(rack_req.request_method,
112
176
  rack_req.url,
@@ -128,6 +192,9 @@ module Webmachine
128
192
 
129
193
  class RackResponse
130
194
  ONE_FIVE = '1.5'.freeze
195
+ # Header names are normalised to lowercase by build_rack_response_headers,
196
+ # so use the lowercase form everywhere inside RackResponse too.
197
+ LOWERCASE_CONTENT_TYPE = 'content-type'.freeze
131
198
 
132
199
  def initialize(body, status, headers)
133
200
  @body = body
@@ -136,8 +203,8 @@ module Webmachine
136
203
  end
137
204
 
138
205
  def finish
139
- @headers[CONTENT_TYPE] ||= TEXT_HTML if rack_release_enforcing_content_type
140
- @headers.delete(CONTENT_TYPE) if response_without_body
206
+ @headers[LOWERCASE_CONTENT_TYPE] ||= TEXT_HTML if rack_release_enforcing_content_type
207
+ @headers.delete(LOWERCASE_CONTENT_TYPE) if response_without_body
141
208
  [@status, @headers, @body]
142
209
  end
143
210
 
@@ -177,8 +244,12 @@ module Webmachine
177
244
  if @value
178
245
  @value.join
179
246
  else
180
- @request.body.rewind
181
- @request.body.read
247
+ # Rack 3 removed the requirement for rack.input to implement #rewind
248
+ # (Rack::Lint::InputWrapper in Rack 3 does not define it), so guard
249
+ # the call to avoid a NoMethodError on every PUT/POST request.
250
+ body = @request.body
251
+ body.rewind if body.respond_to?(:rewind)
252
+ body.read
182
253
  end
183
254
  end
184
255
 
@@ -29,7 +29,7 @@ module Webmachine
29
29
  class Server < ::WEBrick::HTTPServer
30
30
  def initialize(options)
31
31
  @application = options[:application]
32
- super(options)
32
+ super
33
33
  end
34
34
 
35
35
  # Handles a request
@@ -5,6 +5,7 @@ module Webmachine
5
5
  # This class by itself represents a "strong" entity tag.
6
6
  class ETag
7
7
  include QuotedString
8
+
8
9
  # The pattern for a weak entity tag
9
10
  WEAK_ETAG = /^W\/#{QUOTED_STRING}$/.freeze
10
11
 
@@ -32,7 +32,7 @@ module Webmachine
32
32
  # @param [Object]
33
33
  # @return [Webmachine::Headers]
34
34
  def self.[](*args)
35
- super(super(*args).map { |k, v| [k.to_s.downcase, v] })
35
+ super(super.map { |k, v| [k.to_s.downcase, v] })
36
36
  end
37
37
 
38
38
  # Fetch a header
@@ -6,6 +6,7 @@ module Webmachine
6
6
  # Encapsulates a MIME media type, with logic for matching types.
7
7
  class MediaType
8
8
  extend Translation
9
+
9
10
  # Matches valid media types
10
11
  MEDIA_TYPE_REGEX = /^\s*([^;\s]+)\s*((?:;\s*\S+\s*)*)\s*$/.freeze
11
12
 
@@ -112,11 +113,8 @@ module Webmachine
112
113
  # ignoring params and taking into account wildcards
113
114
  def type_matches?(other)
114
115
  other = self.class.parse(other)
115
- if [Dispatcher::Route::MATCH_ALL_STR, MATCHES_ALL, type].include?(other.type)
116
- true
117
- else
118
- other.major == major && other.minor == Dispatcher::Route::MATCH_ALL_STR
119
- end
116
+ [Dispatcher::Route::MATCH_ALL_STR, MATCHES_ALL, type].include?(other.type) ||
117
+ (other.major == major && other.minor == Dispatcher::Route::MATCH_ALL_STR)
120
118
  end
121
119
  end # class MediaType
122
120
  end # module Webmachine
@@ -7,6 +7,7 @@ module Webmachine
7
7
  # @api private
8
8
  class IOEncoder < Encoder
9
9
  include Enumerable
10
+
10
11
  CHUNK_SIZE = 8192
11
12
  # Iterates over the IO, encoding and yielding individual chunks
12
13
  # of the response entity.
@@ -1,16 +1,15 @@
1
- require 'pstore'
2
-
3
1
  module Webmachine
4
2
  module Trace
5
- # Implements a trace storage using PStore from Ruby's standard
6
- # library. To use this trace store, specify the :pstore engine
7
- # and a path where it can store traces:
3
+ # Implements a trace storage using the pstore gem. To use this trace store,
4
+ # add `pstore` to your Gemfile and specify the :pstore engine and a path
5
+ # where it can store traces:
8
6
  # @example
9
7
  # Webmachine::Trace.trace_store = :pstore, "/tmp/webmachine.trace"
10
8
  class PStoreTraceStore
11
9
  # @api private
12
10
  # @param [String] path where to store traces in a PStore
13
11
  def initialize(path)
12
+ require 'pstore' # JIT load of the pstore gem. Avoid requiring the dependency when this class is not used.
14
13
  @pstore = PStore.new(path)
15
14
  end
16
15
 
@@ -1,6 +1,6 @@
1
1
  module Webmachine
2
2
  # Library version
3
- VERSION = '2.0.1'.freeze
3
+ VERSION = '2.0.2'.freeze
4
4
 
5
5
  # String for use in "Server" HTTP response header, which includes
6
6
  # the {VERSION}.
metadata CHANGED
@@ -1,43 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: webmachine
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Cribbs
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-27 00:00:00.000000000 Z
11
+ date: 2026-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: i18n
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: 0.4.0
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: 0.4.0
27
- - !ruby/object:Gem::Dependency
28
- name: multi_json
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
13
  - !ruby/object:Gem::Dependency
42
14
  name: as-notifications
43
15
  requirement: !ruby/object:Gem::Requirement
@@ -73,33 +45,33 @@ dependencies:
73
45
  - !ruby/object:Gem::Version
74
46
  version: '0'
75
47
  - !ruby/object:Gem::Dependency
76
- name: webrick
48
+ name: i18n
77
49
  requirement: !ruby/object:Gem::Requirement
78
50
  requirements:
79
- - - "~>"
51
+ - - ">="
80
52
  - !ruby/object:Gem::Version
81
- version: 1.7.0
82
- type: :development
53
+ version: 0.4.0
54
+ type: :runtime
83
55
  prerelease: false
84
56
  version_requirements: !ruby/object:Gem::Requirement
85
57
  requirements:
86
- - - "~>"
58
+ - - ">="
87
59
  - !ruby/object:Gem::Version
88
- version: 1.7.0
60
+ version: 0.4.0
89
61
  - !ruby/object:Gem::Dependency
90
- name: standard
62
+ name: multi_json
91
63
  requirement: !ruby/object:Gem::Requirement
92
64
  requirements:
93
- - - "~>"
65
+ - - ">="
94
66
  - !ruby/object:Gem::Version
95
- version: '1.21'
96
- type: :development
67
+ version: '0'
68
+ type: :runtime
97
69
  prerelease: false
98
70
  version_requirements: !ruby/object:Gem::Requirement
99
71
  requirements:
100
- - - "~>"
72
+ - - ">="
101
73
  - !ruby/object:Gem::Version
102
- version: '1.21'
74
+ version: '0'
103
75
  description: " webmachine is a toolkit for building HTTP applications in a declarative
104
76
  fashion, that avoids the confusion of going through a CGI-style interface like Rack.
105
77
  It is strongly influenced by the original Erlang project of the same name and shares
@@ -191,13 +163,14 @@ homepage: https://github.com/webmachine/webmachine-ruby
191
163
  licenses:
192
164
  - Apache-2.0
193
165
  metadata:
166
+ allowed_push_host: https://rubygems.org
194
167
  bug_tracker_uri: https://github.com/webmachine/webmachine-ruby/issues
195
168
  changelog_uri: https://github.com/webmachine/webmachine-ruby/blob/HEAD/CHANGELOG.md
196
- documentation_uri: https://www.rubydoc.info/gems/webmachine/2.0.1
169
+ documentation_uri: https://www.rubydoc.info/gems/webmachine/2.0.2
197
170
  homepage_uri: https://github.com/webmachine/webmachine-ruby
198
171
  source_code_uri: https://github.com/webmachine/webmachine-ruby
199
172
  wiki_uri: https://github.com/webmachine/webmachine-ruby/wiki
200
- post_install_message:
173
+ post_install_message:
201
174
  rdoc_options: []
202
175
  require_paths:
203
176
  - lib
@@ -212,8 +185,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
212
185
  - !ruby/object:Gem::Version
213
186
  version: '0'
214
187
  requirements: []
215
- rubygems_version: 3.4.10
216
- signing_key:
188
+ rubygems_version: 3.0.3.1
189
+ signing_key:
217
190
  specification_version: 4
218
191
  summary: webmachine is a toolkit for building HTTP applications,
219
192
  test_files: []