h1p 0.6.1 → 1.1

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: 96197142deeb30d27fe9b3fc9de21a07ed1de8f0a1d3db9b8d967310056bdd12
4
- data.tar.gz: aa71893e95f7a7e8ba8de8123987445262edd6dbcad7efba12337091a2421d14
3
+ metadata.gz: 33974d5940b6a9ad9282df7d946486a1ee265ed67e2d5004bbe3e3572c4233db
4
+ data.tar.gz: a0b175ce9eb77d83457376902ab71cf597eedc41009c6094564e8de20e8532f5
5
5
  SHA512:
6
- metadata.gz: 9380e218a2433f61a88676fb807ecb742391dff2c6d7e4937d24ff1385839ed897eaf81eb3d8d1f916bcf53f30cd2fd9c31f0c78fcd6c0e35d3753dc1d3a1252
7
- data.tar.gz: 2ce69a5e542390018f779e9ba0a73263b362b3a6d69838494da73d18cb6f84a3262ed98fd086bb098ffa253f264db4d3e1d40e5ae8f9757ca1928c36ce9f02ce
6
+ metadata.gz: b8826e6c0039d1721ba0d662fb542a19ed9349bdc0ec6aadc976b172266284955c0d6784da9b1d1b9d8e5ef76181cc8b1299eb2164ab1b56817899e66ae1ec53
7
+ data.tar.gz: 6d9343d65ffc31dadaa7fd9a01e5221d183a830d7d79fa82d79381832160ad1c342ffa6f1a4892f8aad78f7f1adde161517b79f571fdb5efc098cc4e55c99781
@@ -8,7 +8,7 @@ jobs:
8
8
  fail-fast: false
9
9
  matrix:
10
10
  os: [ubuntu-latest]
11
- ruby: [2.7, 3.0, 3.1, 3.2]
11
+ ruby: ['3.0', '3.1', '3.2', 'head']
12
12
 
13
13
  name: >-
14
14
  ${{matrix.os}}, ${{matrix.ruby}}
@@ -24,8 +24,6 @@ jobs:
24
24
  run: |
25
25
  gem install bundler
26
26
  bundle install
27
- - name: Show Linux kernel version
28
- run: uname -r
29
27
  - name: Compile C-extension
30
28
  run: bundle exec rake compile
31
29
  - name: Run tests
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 1.1 2023-07-01
2
+
3
+ - Rescue `ArgumentError` in `#parse_headers` (#4)
4
+
5
+ ## 1.0 2023-06-07
6
+
7
+ - Add support for array as header value (#2)
8
+
1
9
  ## 0.6.1 2023-05-28
2
10
 
3
11
  - Fix sending response with frozen headers hash
data/Gemfile.lock CHANGED
@@ -1,14 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- h1p (0.6.1)
4
+ h1p (1.1)
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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2021 Digital Fabric
3
+ Copyright (c) 2023 Digital Fabric
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/ext/h1p/h1p.c CHANGED
@@ -1,3 +1,4 @@
1
+ #include <stdnoreturn.h>
1
2
  #include "h1p.h"
2
3
 
3
4
  // Security-related limits are defined in limits.rb and injected as
@@ -22,6 +23,7 @@ ID ID_call;
22
23
  ID ID_downcase;
23
24
  ID ID_eof_p;
24
25
  ID ID_eq;
26
+ ID ID_join;
25
27
  ID ID_read_method;
26
28
  ID ID_read;
27
29
  ID ID_readpartial;
@@ -56,6 +58,7 @@ VALUE STR_transfer_encoding_capitalized;
56
58
 
57
59
  VALUE STR_CRLF;
58
60
  VALUE STR_EMPTY_CHUNK;
61
+ VALUE STR_COMMA_SPACE;
59
62
 
60
63
  VALUE SYM_backend_read;
61
64
  VALUE SYM_backend_recv;
@@ -722,20 +725,7 @@ eof:
722
725
  return 0;
723
726
  }
724
727
 
725
- /* call-seq: parser.parse_headers -> headers
726
- *
727
- * Parses headers from the associated IO instance, returning a hash mapping
728
- * header keys to their respective values. Header keys are downcased and dashes
729
- * are converted to underscores. The returned headers will also include the
730
- * following pseudo-headers:
731
- *
732
- * - `':protocol'` - the protocol as specified in the query line / status line
733
- * - `':path'` - the query path (for HTTP requests)
734
- * - `':method'` - the HTTP method (for HTTP requests)
735
- * - `':status'` - the HTTP status (for HTTP responses)
736
- * - `':rx'` - the total number of bytes read by the parser
737
- */
738
- VALUE Parser_parse_headers(VALUE self) {
728
+ VALUE Parser_parse_headers_safe(VALUE self) {
739
729
  Parser_t *parser;
740
730
  GetParser(self, parser);
741
731
  parser->headers = rb_hash_new();
@@ -773,6 +763,31 @@ done:
773
763
  return parser->headers;
774
764
  }
775
765
 
766
+ noreturn VALUE Parser_parse_headers_rescue(VALUE args, VALUE error) {
767
+ RAISE_BAD_REQUEST("Invalid character sequences in method or header name");
768
+ }
769
+
770
+ /* call-seq: parser.parse_headers -> headers
771
+ *
772
+ * Parses headers from the associated IO instance, returning a hash mapping
773
+ * header keys to their respective values. Header keys are downcased and dashes
774
+ * are converted to underscores. The returned headers will also include the
775
+ * following pseudo-headers:
776
+ *
777
+ * - `':protocol'` - the protocol as specified in the query line / status line
778
+ * - `':path'` - the query path (for HTTP requests)
779
+ * - `':method'` - the HTTP method (for HTTP requests)
780
+ * - `':status'` - the HTTP status (for HTTP responses)
781
+ * - `':rx'` - the total number of bytes read by the parser
782
+ */
783
+ VALUE Parser_parse_headers(VALUE self) {
784
+ return rb_rescue2(
785
+ Parser_parse_headers_safe, self,
786
+ Parser_parse_headers_rescue, self,
787
+ eArgumentError, (VALUE)0
788
+ );
789
+ }
790
+
776
791
  ////////////////////////////////////////////////////////////////////////////////
777
792
 
778
793
  static inline int str_to_int(VALUE value, const char *error_msg) {
@@ -1176,12 +1191,24 @@ void send_response_write_status_line(send_response_ctx *ctx, VALUE protocol, VAL
1176
1191
  ctx->buffer_len += partlen + 2;
1177
1192
  }
1178
1193
 
1194
+ inline static VALUE format_comma_separated_header_values(VALUE array) {
1195
+ return rb_funcall(array, ID_join, 1, STR_COMMA_SPACE);
1196
+ }
1197
+
1179
1198
  int send_response_write_header(VALUE key, VALUE val, VALUE arg) {
1180
1199
  if (TYPE(key) != T_STRING) key = rb_funcall(key, ID_to_s, 0);
1181
1200
  char *keyptr = RSTRING_PTR(key);
1182
1201
  if (RSTRING_LEN(key) < 1 || keyptr[0] == ':') return 0;
1183
1202
 
1184
- if (TYPE(val) != T_STRING) val = rb_funcall(val, ID_to_s, 0);
1203
+ switch (TYPE(val)) {
1204
+ case T_STRING:
1205
+ break;
1206
+ case T_ARRAY:
1207
+ val = format_comma_separated_header_values(val);
1208
+ break;
1209
+ default:
1210
+ val = rb_funcall(val, ID_to_s, 0);
1211
+ }
1185
1212
  unsigned int keylen = RSTRING_LEN(key);
1186
1213
  char *valptr = RSTRING_PTR(val);
1187
1214
  unsigned int vallen = RSTRING_LEN(val);
@@ -1377,6 +1404,7 @@ void Init_H1P(void) {
1377
1404
  ID_downcase = rb_intern("downcase");
1378
1405
  ID_eof_p = rb_intern("eof?");
1379
1406
  ID_eq = rb_intern("==");
1407
+ ID_join = rb_intern("join");
1380
1408
  ID_read_method = rb_intern("__read_method__");
1381
1409
  ID_read = rb_intern("read");
1382
1410
  ID_readpartial = rb_intern("readpartial");
@@ -1405,8 +1433,9 @@ void Init_H1P(void) {
1405
1433
  GLOBAL_STR(STR_transfer_encoding, "transfer-encoding");
1406
1434
  GLOBAL_STR(STR_transfer_encoding_capitalized, "Transfer-Encoding");
1407
1435
 
1408
- GLOBAL_STR(STR_CRLF, "\r\n");
1409
- GLOBAL_STR(STR_EMPTY_CHUNK, "0\r\n\r\n");
1436
+ GLOBAL_STR(STR_CRLF, "\r\n");
1437
+ GLOBAL_STR(STR_EMPTY_CHUNK, "0\r\n\r\n");
1438
+ GLOBAL_STR(STR_COMMA_SPACE, ", ");
1410
1439
 
1411
1440
  SYM_backend_read = ID2SYM(ID_backend_read);
1412
1441
  SYM_backend_recv = ID2SYM(ID_backend_recv);
data/h1p.gemspec CHANGED
@@ -16,9 +16,9 @@ Gem::Specification.new do |s|
16
16
  s.extra_rdoc_files = ["README.md"]
17
17
  s.extensions = ["ext/h1p/extconf.rb"]
18
18
  s.require_paths = ["lib"]
19
- s.required_ruby_version = '>= 2.7'
19
+ s.required_ruby_version = '>= 3.0'
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.1'
4
+ VERSION = '1.1'
5
5
  end
data/test/test_h1p.rb CHANGED
@@ -35,6 +35,14 @@ class SendResponseTest < MiniTest::Test
35
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
36
  end
37
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
44
+ end
45
+
38
46
  def test_send_response_with_body
39
47
  i, o = IO.pipe
40
48
  H1P.send_response(o, {}, "foobar")
@@ -97,7 +105,7 @@ class SendBodyChunkTest < MiniTest::Test
97
105
  len = H1P.send_body_chunk(o, chunk)
98
106
  o.close
99
107
  end
100
-
108
+
101
109
  response = i.read
102
110
  assert_equal "#{chunk.bytesize.to_s(16)}\r\n#{chunk}\r\n", response
103
111
  assert_equal chunk.bytesize + chunk.bytesize.to_s(16).bytesize + 4, len
@@ -88,6 +88,11 @@ class H1PServerTest < MiniTest::Test
88
88
  assert_raises(Error) { @parser.parse_headers }
89
89
  end
90
90
 
91
+ def test_invalid_method_string
92
+ @o << "\x02\x78\x83\x2 / HTTP/1.1\r\n\r\n"
93
+ assert_raises(Error) { @parser.parse_headers }
94
+ end
95
+
91
96
  def test_path_characters
92
97
  @o << "GET /äBçDé¤23~{@€ HTTP/1.1\r\n\r\n"
93
98
  headers = @parser.parse_headers
@@ -191,6 +196,11 @@ class H1PServerTest < MiniTest::Test
191
196
  assert_equal 'ddd', headers['c']
192
197
  end
193
198
 
199
+ def test_invalid_headers
200
+ @o << "GET / HTTP/1.1\r\n\foo\x02\x78\x83\x02: bar\n\r\n"
201
+ assert_raises(Error) { @parser.parse_headers }
202
+ end
203
+
194
204
  def test_headers_multiple_values
195
205
  @o << "GET / HTTP/1.1\r\nFoo: Bar\r\nfoo: baz\r\n\r\n"
196
206
  headers = @parser.parse_headers
@@ -471,7 +481,7 @@ class H1PServerTest < MiniTest::Test
471
481
  @o.close
472
482
  end
473
483
  def w.__write_method__; :backend_write; end
474
-
484
+
475
485
  headers = @parser.parse_headers
476
486
  @parser.splice_body_to(w)
477
487
  w.close
@@ -492,7 +502,7 @@ class H1PServerTest < MiniTest::Test
492
502
  @o.close
493
503
  end
494
504
  def w.__write_method__; :backend_write; end
495
-
505
+
496
506
  headers = @parser.parse_headers
497
507
  @parser.splice_body_to(w)
498
508
  w.close
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.1
4
+ version: '1.1'
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-05-28 00:00:00.000000000 Z
11
+ date: 2023-07-01 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: []
@@ -102,7 +102,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
102
102
  requirements:
103
103
  - - ">="
104
104
  - !ruby/object:Gem::Version
105
- version: '2.7'
105
+ version: '3.0'
106
106
  required_rubygems_version: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - ">="