h1p 0.6 → 1.0

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: 90f7937fd6bcefdd1627c208bf73054fb3fb03a855666c7247f04aac4398d280
4
- data.tar.gz: ab6a866b843beaf92137b991048db9af006f4cb5e510e211dc6ba6652bbc84a2
3
+ metadata.gz: 2d2493b6fc11d7deb1231f9a2d0bf1c6dc59b00a01065da7086e8c65326f74df
4
+ data.tar.gz: e30bf2c5a3213a4744a66a412e2db34a2b26b05baa8073409a18c7e0bd314de3
5
5
  SHA512:
6
- metadata.gz: 101e6e13aeed0cf2250dbe962c0c9c9edc5bf69e6ff2a59ee0d02018992c9b76dcaa4be3ddb44bbcb2272e355278a0b717c51050a2786cc95c00fbdb23bc40fa
7
- data.tar.gz: 2d3130c067d51f173873d6181b76999dd897c241dcafc5fa3f69ec0837404b2fd7e292cadc2fbf9e27003e3dfd933dc394799abf03f12187d6c80e4f2dab83d6
6
+ metadata.gz: 2adc85f461d3a41e7e67f9c199be486b85fd46407e60455329ac065a5779c60d62328a049091bb9d4b7d0be2f1610db38a8744c35f7071b06991ec837235a981
7
+ data.tar.gz: 926acbd38590f74666e8d774b6f54d3b610cbf0570495727049b2c2ba628a80888d30e2e0bd83ac26b3897c9a0875be42ce336a439a9e07a2dfb8160aebc8441
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 1.0 2023-06-07
2
+
3
+ - Add support for array as header value (#2)
4
+
5
+ ## 0.6.1 2023-05-28
6
+
7
+ - Fix sending response with frozen headers hash
8
+
1
9
  ## 0.6 2023-01-05
2
10
 
3
11
  - Add documentation
data/Gemfile.lock CHANGED
@@ -1,14 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- h1p (0.6)
4
+ h1p (1.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- minitest (5.17.0)
9
+ minitest (5.18.0)
10
10
  rake (13.0.6)
11
- rake-compiler (1.2.1)
11
+ rake-compiler (1.2.3)
12
12
  rake
13
13
 
14
14
  PLATFORMS
@@ -16,9 +16,9 @@ PLATFORMS
16
16
 
17
17
  DEPENDENCIES
18
18
  h1p!
19
- minitest (~> 5.17.0)
19
+ minitest (~> 5.18.0)
20
20
  rake (~> 13.0.6)
21
- rake-compiler (= 1.2.1)
21
+ rake-compiler (= 1.2.3)
22
22
 
23
23
  BUNDLED WITH
24
24
  2.2.26
data/README.md CHANGED
@@ -230,15 +230,22 @@ arbitrary IO instances. To write a response with or without a body, use
230
230
 
231
231
  ```ruby
232
232
  H1P.send_response(socket, { 'Some-Header' => 'header value'}, 'foobar')
233
- #=> "HTTP/1.1 200 OK\r\nSome-Header: header value\r\n\r\nfoobar"
233
+ # HTTP/1.1 200 OK
234
+ # Some-Header: header value
235
+ #
236
+ # foobar
234
237
 
235
238
  # The :protocol pseudo header sets the protocol in the status line:
236
239
  H1P.send_response(socket, { ':protocol' => 'HTTP/0.9' })
237
- #=> "HTTP/0.9 200 OK\r\n\r\n"
240
+ # HTTP/0.9 200 OK
241
+ #
242
+ #
238
243
 
239
244
  # The :status pseudo header sets the response status:
240
245
  H1P.send_response(socket, { ':status' => '418 I\'m a teapot' })
241
- #=> "HTTP/1.1 418 I'm a teapot\r\n\r\n"
246
+ # HTTP/1.1 418 I'm a teapot
247
+ #
248
+ #
242
249
  ```
243
250
 
244
251
  To send responses using chunked transfer encoding use
@@ -246,7 +253,13 @@ To send responses using chunked transfer encoding use
246
253
 
247
254
  ```ruby
248
255
  H1P.send_chunked_response(socket, {}, "foobar")
249
- #=> "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n0\r\n\r\n"
256
+ # HTTP/1.1 200 OK
257
+ # Transfer-Encoding: chunked
258
+ # 6
259
+ # foobar
260
+ # 0
261
+ #
262
+ #
250
263
  ```
251
264
 
252
265
  You can also call `H1P.send_chunked_response` with a block that provides the
@@ -263,10 +276,14 @@ To send individual chunks use `H1P.send_body_chunk`:
263
276
 
264
277
  ```ruby
265
278
  H1P.send_body_chunk(socket, 'foo')
266
- #=> "3\r\nfoo\r\n"
279
+ # 3
280
+ # foo
281
+ #
267
282
 
268
283
  H1P.send_body_chunk(socket, nil)
269
- #=> "0\r\n\r\n"
284
+ # 0
285
+ #
286
+ #
270
287
  ```
271
288
 
272
289
  ## Parser Design
data/ext/h1p/h1p.c CHANGED
@@ -22,6 +22,7 @@ ID ID_call;
22
22
  ID ID_downcase;
23
23
  ID ID_eof_p;
24
24
  ID ID_eq;
25
+ ID ID_join;
25
26
  ID ID_read_method;
26
27
  ID ID_read;
27
28
  ID ID_readpartial;
@@ -56,6 +57,7 @@ VALUE STR_transfer_encoding_capitalized;
56
57
 
57
58
  VALUE STR_CRLF;
58
59
  VALUE STR_EMPTY_CHUNK;
60
+ VALUE STR_COMMA_SPACE;
59
61
 
60
62
  VALUE SYM_backend_read;
61
63
  VALUE SYM_backend_recv;
@@ -1176,12 +1178,24 @@ void send_response_write_status_line(send_response_ctx *ctx, VALUE protocol, VAL
1176
1178
  ctx->buffer_len += partlen + 2;
1177
1179
  }
1178
1180
 
1181
+ inline static VALUE format_comma_separated_header_values(VALUE array) {
1182
+ return rb_funcall(array, ID_join, 1, STR_COMMA_SPACE);
1183
+ }
1184
+
1179
1185
  int send_response_write_header(VALUE key, VALUE val, VALUE arg) {
1180
1186
  if (TYPE(key) != T_STRING) key = rb_funcall(key, ID_to_s, 0);
1181
1187
  char *keyptr = RSTRING_PTR(key);
1182
1188
  if (RSTRING_LEN(key) < 1 || keyptr[0] == ':') return 0;
1183
1189
 
1184
- if (TYPE(val) != T_STRING) val = rb_funcall(val, ID_to_s, 0);
1190
+ switch (TYPE(val)) {
1191
+ case T_STRING:
1192
+ break;
1193
+ case T_ARRAY:
1194
+ val = format_comma_separated_header_values(val);
1195
+ break;
1196
+ default:
1197
+ val = rb_funcall(val, ID_to_s, 0);
1198
+ }
1185
1199
  unsigned int keylen = RSTRING_LEN(key);
1186
1200
  char *valptr = RSTRING_PTR(val);
1187
1201
  unsigned int vallen = RSTRING_LEN(val);
@@ -1236,10 +1250,11 @@ VALUE H1P_send_response(int argc,VALUE *argv, VALUE self) {
1236
1250
 
1237
1251
  bodyptr = RSTRING_PTR(body);
1238
1252
  bodylen = RSTRING_LEN(body);
1239
- rb_hash_aset(headers, STR_content_length_capitalized, INT2FIX(bodylen));
1253
+ // rb_hash_aset(headers, STR_content_length_capitalized, INT2FIX(bodylen));
1240
1254
  }
1241
1255
 
1242
1256
  rb_hash_foreach(headers, send_response_write_header, (VALUE)&ctx);
1257
+ send_response_write_header(STR_content_length_capitalized, INT2FIX(bodylen), (VALUE)&ctx);
1243
1258
 
1244
1259
  char *endptr = ctx.buffer_ptr + ctx.buffer_len;
1245
1260
  endptr[0] = '\r';
@@ -1309,8 +1324,8 @@ VALUE H1P_send_chunked_response(VALUE self, VALUE io, VALUE headers) {
1309
1324
  if (status == Qnil) status = STR_pseudo_status_default;
1310
1325
  send_response_write_status_line(&ctx, protocol, status);
1311
1326
 
1312
- rb_hash_aset(headers, STR_transfer_encoding_capitalized, STR_chunked);
1313
1327
  rb_hash_foreach(headers, send_response_write_header, (VALUE)&ctx);
1328
+ send_response_write_header(STR_transfer_encoding_capitalized, STR_chunked, (VALUE)&ctx);
1314
1329
 
1315
1330
  ctx.buffer_ptr[ctx.buffer_len] = '\r';
1316
1331
  ctx.buffer_ptr[ctx.buffer_len + 1] = '\n';
@@ -1376,6 +1391,7 @@ void Init_H1P(void) {
1376
1391
  ID_downcase = rb_intern("downcase");
1377
1392
  ID_eof_p = rb_intern("eof?");
1378
1393
  ID_eq = rb_intern("==");
1394
+ ID_join = rb_intern("join");
1379
1395
  ID_read_method = rb_intern("__read_method__");
1380
1396
  ID_read = rb_intern("read");
1381
1397
  ID_readpartial = rb_intern("readpartial");
@@ -1404,8 +1420,9 @@ void Init_H1P(void) {
1404
1420
  GLOBAL_STR(STR_transfer_encoding, "transfer-encoding");
1405
1421
  GLOBAL_STR(STR_transfer_encoding_capitalized, "Transfer-Encoding");
1406
1422
 
1407
- GLOBAL_STR(STR_CRLF, "\r\n");
1408
- GLOBAL_STR(STR_EMPTY_CHUNK, "0\r\n\r\n");
1423
+ GLOBAL_STR(STR_CRLF, "\r\n");
1424
+ GLOBAL_STR(STR_EMPTY_CHUNK, "0\r\n\r\n");
1425
+ GLOBAL_STR(STR_COMMA_SPACE, ", ");
1409
1426
 
1410
1427
  SYM_backend_read = ID2SYM(ID_backend_read);
1411
1428
  SYM_backend_recv = ID2SYM(ID_backend_recv);
data/h1p.gemspec CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.require_paths = ["lib"]
19
19
  s.required_ruby_version = '>= 2.7'
20
20
 
21
- s.add_development_dependency 'rake-compiler', '1.2.1'
21
+ s.add_development_dependency 'rake-compiler', '1.2.3'
22
22
  s.add_development_dependency 'rake', '~>13.0.6'
23
- s.add_development_dependency 'minitest', '~>5.17.0'
23
+ s.add_development_dependency 'minitest', '~>5.18.0'
24
24
  end
data/lib/h1p/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module H1P
4
- VERSION = '0.6'
4
+ VERSION = '1.0'
5
5
  end
data/test/test_h1p.rb CHANGED
@@ -9,30 +9,38 @@ class SendResponseTest < MiniTest::Test
9
9
  H1P.send_response(o, { ':status' => '418 I\'m a teapot' })
10
10
  o.close
11
11
  response = i.read
12
- assert_equal "HTTP/1.1 418 I'm a teapot\r\n\r\n", response
12
+ assert_equal "HTTP/1.1 418 I'm a teapot\r\nContent-Length: 0\r\n\r\n", response
13
13
 
14
14
  i, o = IO.pipe
15
15
  count = H1P.send_response(o, { ':protocol' => 'HTTP/1.0' })
16
16
  o.close
17
17
  response = i.read
18
- assert_equal "HTTP/1.0 200 OK\r\n\r\n", response
19
- assert_equal "HTTP/1.0 200 OK\r\n\r\n".bytesize, count
18
+ assert_equal "HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n", response
19
+ assert_equal "HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n".bytesize, count
20
20
  end
21
21
 
22
22
  def test_send_response_string_headers
23
23
  i, o = IO.pipe
24
- H1P.send_response(o, { 'Foo' => 'Bar', 'Content-Length' => '123' })
24
+ H1P.send_response(o, { 'Foo' => 'Bar', 'X-Blah' => '123' })
25
25
  o.close
26
26
  response = i.read
27
- assert_equal "HTTP/1.1 200 OK\r\nFoo: Bar\r\nContent-Length: 123\r\n\r\n", response
27
+ assert_equal "HTTP/1.1 200 OK\r\nFoo: Bar\r\nX-Blah: 123\r\nContent-Length: 0\r\n\r\n", response
28
28
  end
29
29
 
30
30
  def test_send_response_non_string_headers
31
31
  i, o = IO.pipe
32
- H1P.send_response(o, { :Foo => 'Bar', 'Content-Length' => 123 })
32
+ H1P.send_response(o, { :Foo => 'Bar', 'X-Blah' => 123 })
33
33
  o.close
34
34
  response = i.read
35
- assert_equal "HTTP/1.1 200 OK\r\nFoo: Bar\r\nContent-Length: 123\r\n\r\n", response
35
+ assert_equal "HTTP/1.1 200 OK\r\nFoo: Bar\r\nX-Blah: 123\r\nContent-Length: 0\r\n\r\n", response
36
+ end
37
+
38
+ def test_send_response_multiple_header_values
39
+ i, o = IO.pipe
40
+ H1P.send_response(o, { :Foo => ['Bar', 'Baz'], 'X-Blah' => 123 })
41
+ o.close
42
+ response = i.read
43
+ assert_equal "HTTP/1.1 200 OK\r\nFoo: Bar, Baz\r\nX-Blah: 123\r\nContent-Length: 0\r\n\r\n", response
36
44
  end
37
45
 
38
46
  def test_send_response_with_body
@@ -43,6 +51,15 @@ class SendResponseTest < MiniTest::Test
43
51
  assert_equal "HTTP/1.1 200 OK\r\nContent-Length: 6\r\n\r\nfoobar", response
44
52
  end
45
53
 
54
+ def test_send_response_with_frozen_headers_hash
55
+ i, o = IO.pipe
56
+ h = {Foo: 'bar'}.freeze
57
+ H1P.send_response(o, h, 'foo')
58
+ o.close
59
+ response = i.read
60
+ assert_equal "HTTP/1.1 200 OK\r\nFoo: bar\r\nContent-Length: 3\r\n\r\nfoo", response
61
+ end
62
+
46
63
  def test_send_response_with_big_body
47
64
  i, o = IO.pipe
48
65
  body = "abcdefg" * 10000
@@ -111,4 +128,21 @@ class SendChunkedResponseTest < MiniTest::Test
111
128
  assert_equal "HTTP/1.1 200 OK\r\nFoo: bar\r\nTransfer-Encoding: chunked\r\n\r\n3\r\nfoo\r\n3\r\nbar\r\n3\r\nbaz\r\n0\r\n\r\n", response
112
129
  assert_equal len, response.bytesize
113
130
  end
131
+
132
+ def test_send_chunked_response_with_frozen_headers_hash
133
+ isrc, osrc = IO.pipe
134
+ osrc << 'foobarbaz'
135
+ osrc.close
136
+
137
+ i, o = IO.pipe
138
+ h = { 'Foo' => 'bar' }.freeze
139
+ len = H1P.send_chunked_response(o, h) do
140
+ isrc.read(3)
141
+ end
142
+ o.close
143
+
144
+ response = i.read
145
+ assert_equal "HTTP/1.1 200 OK\r\nFoo: bar\r\nTransfer-Encoding: chunked\r\n\r\n3\r\nfoo\r\n3\r\nbar\r\n3\r\nbaz\r\n0\r\n\r\n", response
146
+ assert_equal len, response.bytesize
147
+ end
114
148
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: h1p
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.6'
4
+ version: '1.0'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-05 00:00:00.000000000 Z
11
+ date: 2023-06-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 1.2.1
19
+ version: 1.2.3
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 1.2.1
26
+ version: 1.2.3
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 5.17.0
47
+ version: 5.18.0
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 5.17.0
54
+ version: 5.18.0
55
55
  description:
56
56
  email: sharon@noteflakes.com
57
57
  executables: []