h1p 0.6.1 → 1.1

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: 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
  - - ">="