h1p 0.6 → 1.0

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: 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: []