lsqpack 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +19 -0
  3. data/Gemfile.lock +82 -0
  4. data/LICENSE +21 -0
  5. data/README.md +36 -0
  6. data/Rakefile +19 -0
  7. data/Steepfile +6 -0
  8. data/ext/lsqpack/extconf.rb +18 -0
  9. data/ext/lsqpack/lsqpack.c +426 -0
  10. data/ext/lsqpack/lsqpack.h +6 -0
  11. data/lib/lsqpack/version.rb +5 -0
  12. data/lib/lsqpack.rb +30 -0
  13. data/ls-qpack/.appveyor.yml +14 -0
  14. data/ls-qpack/.cirrus.yml +6 -0
  15. data/ls-qpack/.travis.yml +32 -0
  16. data/ls-qpack/CMakeLists.txt +66 -0
  17. data/ls-qpack/LICENSE +21 -0
  18. data/ls-qpack/README.md +65 -0
  19. data/ls-qpack/bin/CMakeLists.txt +21 -0
  20. data/ls-qpack/bin/encode-int.c +87 -0
  21. data/ls-qpack/bin/fuzz-decode.c +247 -0
  22. data/ls-qpack/bin/interop-decode.c +433 -0
  23. data/ls-qpack/bin/interop-encode.c +554 -0
  24. data/ls-qpack/deps/xxhash/xxhash.c +941 -0
  25. data/ls-qpack/deps/xxhash/xxhash.h +160 -0
  26. data/ls-qpack/fuzz/decode/a/README +3 -0
  27. data/ls-qpack/fuzz/decode/a/preamble +0 -0
  28. data/ls-qpack/fuzz/decode/a/test-cases/id_000000,sig_06,src_000390,op_havoc,rep_4 +0 -0
  29. data/ls-qpack/fuzz/decode/a/test-cases/id_000000,sig_06,src_000579,op_flip1,pos_14 +0 -0
  30. data/ls-qpack/fuzz/decode/a/test-cases/id_000000,src_000000,op_flip2,pos_12 +0 -0
  31. data/ls-qpack/fuzz/decode/a/test-cases/id_000001,sig_11,src_000579,op_havoc,rep_4 +0 -0
  32. data/ls-qpack/fuzz/decode/a/test-cases/id_000002,sig_11,src_000481,op_int16,pos_15,val_-1 +0 -0
  33. data/ls-qpack/fuzz/decode/a/test-cases/id_000002,src_000000,op_havoc,rep_8 +0 -0
  34. data/ls-qpack/fuzz/decode/a/test-cases/id_000006,src_000285,op_flip2,pos_14 +0 -0
  35. data/ls-qpack/fuzz/decode/a/test-cases/id_000008,src_000285,op_flip2,pos_20 +0 -0
  36. data/ls-qpack/fuzz/decode/a/test-cases/id_000010,src_000306,op_flip2,pos_75 +0 -0
  37. data/ls-qpack/fuzz/decode/a/test-cases/id_000011,src_000344,op_havoc,rep_2 +0 -0
  38. data/ls-qpack/fuzz/decode/a/test-cases/id_000014,src_000366,op_flip2,pos_28 +0 -0
  39. data/ls-qpack/fuzz/decode/b/README +1 -0
  40. data/ls-qpack/fuzz/decode/b/preamble +0 -0
  41. data/ls-qpack/fuzz/decode/b/test-cases/seed +0 -0
  42. data/ls-qpack/fuzz/decode/c/setup.sh +3 -0
  43. data/ls-qpack/fuzz/decode/c/test-cases/fb-req.qif.proxygen.out.256.100.0-chopped +0 -0
  44. data/ls-qpack/fuzz/decode/d/preamble +0 -0
  45. data/ls-qpack/fuzz/decode/d/setup.sh +3 -0
  46. data/ls-qpack/fuzz/decode/d/test-cases/fb-resp.minhq.256.128.0.ack +0 -0
  47. data/ls-qpack/fuzz/input/256.100.1/fb-req.out.256.100.1 +0 -0
  48. data/ls-qpack/fuzz/input/256.100.1/fb-resp.out.256.100.1 +0 -0
  49. data/ls-qpack/fuzz/input/256.100.1/netbsd.out.256.100.1 +0 -0
  50. data/ls-qpack/huff-tables.h +136247 -0
  51. data/ls-qpack/lsqpack.c +5547 -0
  52. data/ls-qpack/lsqpack.h +768 -0
  53. data/ls-qpack/test/CMakeLists.txt +76 -0
  54. data/ls-qpack/test/lsqpack-test.h +43 -0
  55. data/ls-qpack/test/qifs/fb-req.qif +4917 -0
  56. data/ls-qpack/test/qifs/fb-resp.qif +5982 -0
  57. data/ls-qpack/test/qifs/long-codes.qif +5984 -0
  58. data/ls-qpack/test/qifs/netbsd.qif +235 -0
  59. data/ls-qpack/test/run-qif.pl +97 -0
  60. data/ls-qpack/test/run-scenario.sh +68 -0
  61. data/ls-qpack/test/scenarios/0.95-reset.sce +10 -0
  62. data/ls-qpack/test/scenarios/cancel-stream.sce +22 -0
  63. data/ls-qpack/test/scenarios/drain-2.sce +37 -0
  64. data/ls-qpack/test/scenarios/drain.sce +37 -0
  65. data/ls-qpack/test/scenarios/end-dst-2.sce +14 -0
  66. data/ls-qpack/test/scenarios/end-dst.sce +14 -0
  67. data/ls-qpack/test/scenarios/incl-name.sce +13 -0
  68. data/ls-qpack/test/scenarios/multi-byte-int-dyn-ref-1.sce +110 -0
  69. data/ls-qpack/test/scenarios/multi-byte-int-dyn-ref-2.sce +161 -0
  70. data/ls-qpack/test/scenarios/post-base-1.sce +10 -0
  71. data/ls-qpack/test/scenarios/post-base-2.sce +13 -0
  72. data/ls-qpack/test/scenarios/post-base-nr.sce +10 -0
  73. data/ls-qpack/test/scenarios/set-max-cap.sce +15 -0
  74. data/ls-qpack/test/test_enc_str.c +139 -0
  75. data/ls-qpack/test/test_get_stx_id.c +144 -0
  76. data/ls-qpack/test/test_huff_dec.c +399 -0
  77. data/ls-qpack/test/test_int.c +220 -0
  78. data/ls-qpack/test/test_qpack.c +856 -0
  79. data/ls-qpack/test/test_read_enc_stream.c +256 -0
  80. data/ls-qpack/tools/har2qif.pl +139 -0
  81. data/ls-qpack/tools/randomize-cookies.pl +41 -0
  82. data/ls-qpack/tools/sort-qif.pl +31 -0
  83. data/ls-qpack/wincompat/getopt.c +758 -0
  84. data/ls-qpack/wincompat/getopt.h +131 -0
  85. data/ls-qpack/wincompat/getopt1.c +188 -0
  86. data/ls-qpack/wincompat/sys/queue.h +859 -0
  87. data/lsqpack.gemspec +39 -0
  88. data/sig/lsqpack.rbs +29 -0
  89. metadata +135 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b3817dceb640976607a1f80f70359dff77831721822c80bf9055b90bd3fb51e5
4
+ data.tar.gz: 057c391779387b76cfec1e07141fab5e75e713af5f26fa6310899a6dc8d046d6
5
+ SHA512:
6
+ metadata.gz: 2f6bec1a935ce51d1568eb83673cb14556cd82f437b219e7a7c1f23cbcbe948a123507ac5a5cb71b2af2d92395430eca3c37702c889f13e8a2edb1e3acca7206
7
+ data.tar.gz: 8c6f3f732c047544e8d5e41ec956832028e5f2bab18e044c0ba0620f91bfd9813be10b442493ae1e87dcdf80af968e59f7005430fffdbc311e72777b086afb53
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in lsqpack.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "rake-compiler"
11
+
12
+ group :development do
13
+ gem "steep"
14
+ end
15
+
16
+ group :test do
17
+ gem "minitest"
18
+ gem "minitest-reporters"
19
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,82 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ lsqpack (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ activesupport (7.0.4.3)
10
+ concurrent-ruby (~> 1.0, >= 1.0.2)
11
+ i18n (>= 1.6, < 2)
12
+ minitest (>= 5.1)
13
+ tzinfo (~> 2.0)
14
+ ansi (1.5.0)
15
+ ast (2.4.2)
16
+ builder (3.2.4)
17
+ concurrent-ruby (1.2.2)
18
+ csv (3.2.6)
19
+ ffi (1.15.5)
20
+ fileutils (1.7.0)
21
+ i18n (1.12.0)
22
+ concurrent-ruby (~> 1.0)
23
+ json (2.6.3)
24
+ language_server-protocol (3.17.0.3)
25
+ listen (3.8.0)
26
+ rb-fsevent (~> 0.10, >= 0.10.3)
27
+ rb-inotify (~> 0.9, >= 0.9.10)
28
+ logger (1.5.3)
29
+ minitest (5.18.0)
30
+ minitest-reporters (1.6.0)
31
+ ansi
32
+ builder
33
+ minitest (>= 5.0)
34
+ ruby-progressbar
35
+ parallel (1.22.1)
36
+ parser (3.2.1.1)
37
+ ast (~> 2.4.1)
38
+ rainbow (3.1.1)
39
+ rake (13.0.6)
40
+ rake-compiler (1.2.0)
41
+ rake
42
+ rb-fsevent (0.11.2)
43
+ rb-inotify (0.10.1)
44
+ ffi (~> 1.0)
45
+ rbs (2.8.4)
46
+ ruby-progressbar (1.13.0)
47
+ securerandom (0.2.2)
48
+ steep (1.3.2)
49
+ activesupport (>= 5.1)
50
+ csv (>= 3.0.9)
51
+ fileutils (>= 1.1.0)
52
+ json (>= 2.1.0)
53
+ language_server-protocol (>= 3.15, < 4.0)
54
+ listen (~> 3.0)
55
+ logger (>= 1.3.0)
56
+ parallel (>= 1.0.0)
57
+ parser (>= 3.1)
58
+ rainbow (>= 2.2.2, < 4.0)
59
+ rbs (~> 2.8.0)
60
+ securerandom (>= 0.1)
61
+ strscan (>= 1.0.0)
62
+ terminal-table (>= 2, < 4)
63
+ strscan (3.0.6)
64
+ terminal-table (3.0.2)
65
+ unicode-display_width (>= 1.1.1, < 3)
66
+ tzinfo (2.0.6)
67
+ concurrent-ruby (~> 1.0)
68
+ unicode-display_width (2.4.2)
69
+
70
+ PLATFORMS
71
+ x86_64-linux
72
+
73
+ DEPENDENCIES
74
+ lsqpack!
75
+ minitest
76
+ minitest-reporters
77
+ rake (~> 13.0)
78
+ rake-compiler
79
+ steep
80
+
81
+ BUNDLED WITH
82
+ 2.3.26
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Yusuke Nakamura
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # Lsqpack
2
+
3
+ lsqpack-ruby is a wrapper gem of the [litespeedtech/ls-qpack](https://github.com/litespeedtech/ls-qpack) library. It strongly influenced by [pylsqpack](https://github.com/aiortc/pylsqpack).
4
+ It strongly influenced by pylsqpack, so it has same api of pylsqpack.
5
+
6
+ ## Installation
7
+
8
+ Install the gem and add to the application's Gemfile by executing:
9
+
10
+ ```sh
11
+ $ bundle add lsqpack
12
+ ```
13
+
14
+ If bundler is not being used to manage dependencies, install the gem by executing:
15
+
16
+ ```sh
17
+ $ gem install lsqpack
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ See also <https://pylsqpack.readthedocs.io/en/latest/>
23
+
24
+ ## Development
25
+
26
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
27
+
28
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
29
+
30
+ ## Contributing
31
+
32
+ Bug reports and pull requests are welcome on GitHub at https://github.com/unasuke/lsqpack-ruby.
33
+
34
+ ## Acknowledgement
35
+ * <https://github.com/litespeedtech/ls-qpack>
36
+ * <https://github.com/aiortc/pylsqpack>
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/extensiontask"
5
+ require "rake/testtask"
6
+
7
+ task build: :compile
8
+
9
+ Rake::ExtensionTask.new("lsqpack") do |ext|
10
+ ext.lib_dir = "lib/lsqpack"
11
+ end
12
+
13
+ Rake::TestTask.new(:test) do |t|
14
+ t.libs << "test"
15
+ t.libs << "lib"
16
+ t.test_files = FileList["test/**/test_*.rb"]
17
+ end
18
+
19
+ task default: %i[clobber compile test]
data/Steepfile ADDED
@@ -0,0 +1,6 @@
1
+ # frosen_string_literal: true
2
+
3
+ target :lib do
4
+ signature "sig"
5
+ check "lib"
6
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mkmf"
4
+
5
+ repo_root = File.join(File.expand_path(__dir__), "../", "../")
6
+
7
+ $CPPFLAGS = "-I #{repo_root}"
8
+ $LDFLAGS = "-lls-qpack -L#{File.join(repo_root, "ls-qpack")}"
9
+
10
+ Dir.chdir(repo_root) do
11
+ Dir.chdir("ls-qpack") do
12
+ puts(system("git status"))
13
+ system("cmake .", exception: true)
14
+ system("make", exception: true)
15
+ end
16
+ end
17
+
18
+ create_makefile("lsqpack/lsqpack")
@@ -0,0 +1,426 @@
1
+ #include "lsqpack.h"
2
+ #include "ls-qpack/lsqpack.h"
3
+ #include <stdio.h>
4
+
5
+ #define HEADER_BUF_SIZE 4096
6
+ #define ENCODER_BUF_SIZE 4096
7
+ #define DECODER_BUF_SIZE 4096
8
+ #define PREFIX_MAX_SIZE 16
9
+
10
+ VALUE rb_mLsqpack;
11
+ VALUE rb_clsEncoder;
12
+ VALUE rb_clsDecoder;
13
+ VALUE rb_eError;
14
+ VALUE rb_eDecompressionFailed;
15
+ VALUE rb_eDecoderStreamErr;
16
+ VALUE rb_eEncoderStreamErr;
17
+ VALUE rb_eStreamBlocked;
18
+
19
+ struct header_block {
20
+ STAILQ_ENTRY(header_block) entries;
21
+ int blocked:1;
22
+ unsigned char* data;
23
+ size_t data_len;
24
+ const unsigned char* data_ptr;
25
+ struct lsqpack_header_list *hlist;
26
+ uint64_t stream_id;
27
+ };
28
+
29
+ static struct header_block *header_block_new(size_t stream_id, const unsigned char* data, size_t data_len) {
30
+ struct header_block* hblock = malloc(sizeof(struct header_block));
31
+ memset(hblock, 0, sizeof(*hblock));
32
+ hblock->data = malloc(data_len);
33
+ hblock->data_len = data_len;
34
+ hblock->data_ptr = hblock->data;
35
+ memcpy(hblock->data, data, data_len);
36
+ hblock->stream_id = stream_id;
37
+ return hblock;
38
+ }
39
+
40
+ static void header_block_free(struct header_block* hblock) {
41
+ free(hblock->data);
42
+ hblock->data = 0;
43
+ hblock->data_ptr = 0;
44
+ if(hblock->hlist) {
45
+ lsqpack_dec_destroy_header_list(hblock->hlist);
46
+ hblock->hlist = 0;
47
+ }
48
+ free(hblock);
49
+ }
50
+
51
+ VALUE hlist_to_headers(struct lsqpack_header_list* hlist) {
52
+ VALUE list, tuple, name, value;
53
+ struct lsqpack_header* header;
54
+
55
+ list = rb_ary_new_capa(hlist->qhl_count);
56
+ for (size_t i = 0; i < hlist->qhl_count; i++) {
57
+ header = hlist->qhl_headers[i];
58
+ name = rb_str_new(header->qh_name, header->qh_name_len);
59
+ value = rb_str_new(header->qh_value, header->qh_value_len);
60
+ tuple = rb_ary_new_capa(2);
61
+ rb_ary_store(tuple, 0, name);
62
+ rb_ary_store(tuple, 1, value);
63
+ rb_ary_store(list, i, tuple);
64
+ }
65
+ return list;
66
+ }
67
+
68
+ static void header_unblocked(void* opaque) {
69
+ struct header_block* hblock = opaque;
70
+ hblock->blocked = 0;
71
+ }
72
+
73
+ typedef struct {
74
+ struct lsqpack_dec dec;
75
+ unsigned char decoder_buf[DECODER_BUF_SIZE];
76
+ STAILQ_HEAD(, header_block) pending_blocks;
77
+ } DecoderObj;
78
+
79
+ size_t lsqpackrb_dec_size(DecoderObj* data) {
80
+ return sizeof(DecoderObj); // Is it correct?
81
+ }
82
+
83
+ void lsqpackrb_dec_free(DecoderObj* data) {
84
+ struct header_block* hblock;
85
+ lsqpack_dec_cleanup(&data->dec);
86
+ while (!STAILQ_EMPTY(&data->pending_blocks)) {
87
+ hblock = STAILQ_FIRST(&data->pending_blocks);
88
+ STAILQ_REMOVE_HEAD(&data->pending_blocks, entries);
89
+ header_block_free(hblock);
90
+ }
91
+ free(data);
92
+ }
93
+
94
+ static const rb_data_type_t decoder_data_type = {
95
+ .wrap_struct_name = "lsqpack_dec_rb",
96
+ .function = {
97
+ .dmark = NULL,
98
+ .dfree = lsqpackrb_dec_free,
99
+ .dsize = lsqpackrb_dec_size,
100
+ },
101
+ .data = NULL,
102
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
103
+ };
104
+
105
+ VALUE lsqpackrb_dec_alloc(VALUE self) {
106
+ DecoderObj* data = malloc(sizeof(DecoderObj));
107
+ return TypedData_Wrap_Struct(self, &decoder_data_type, data);
108
+ }
109
+
110
+
111
+ VALUE lsqpackrb_dec_init(int argc, VALUE *argv, VALUE self) {
112
+ DecoderObj* data;
113
+ TypedData_Get_Struct(self, DecoderObj, &decoder_data_type, data);
114
+
115
+ VALUE hash;
116
+ hash = rb_check_hash_type(argv[argc-1]);
117
+ unsigned int max_tbl_capa, blked_streams;
118
+ ID table[2];
119
+ VALUE values[2];
120
+ table[0] = rb_intern("max_table_capacity");
121
+ table[1] = rb_intern("blocked_streams");
122
+ rb_get_kwargs(hash, table, 0, 2, values);
123
+ max_tbl_capa = (values[0] != Qundef) ? (FIX2INT(values[0])) : 0;
124
+ blked_streams = (values[1] != Qundef) ? (FIX2INT(values[1])) : 0;
125
+
126
+ lsqpack_dec_init(&data->dec, NULL, max_tbl_capa, blked_streams, header_unblocked);
127
+ STAILQ_INIT(&data->pending_blocks);
128
+ return self;
129
+ }
130
+
131
+
132
+ VALUE lsqpackrb_dec_feed_encoder(VALUE self, VALUE data) {
133
+ DecoderObj* dec;
134
+ TypedData_Get_Struct(self, DecoderObj, &decoder_data_type, dec);
135
+ struct header_block* hblock;
136
+ Check_Type(data, T_STRING);
137
+ unsigned char* feed_data;
138
+ feed_data = StringValuePtr(data);
139
+ size_t feed_data_len;
140
+ feed_data_len = RSTRING_LEN(data);
141
+
142
+ if (lsqpack_dec_enc_in(&dec->dec, feed_data, feed_data_len) < 0) {
143
+ rb_raise(rb_eEncoderStreamErr, "lsqpack_dec_enc_in failed");
144
+ return NULL;
145
+ }
146
+
147
+ VALUE list = rb_ary_new();
148
+ STAILQ_FOREACH(hblock, &dec->pending_blocks, entries) {
149
+ if(!hblock->blocked) {
150
+ VALUE val = LONG2NUM(hblock->stream_id);
151
+ rb_ary_push(list, val);
152
+ }
153
+ }
154
+ return list;
155
+ }
156
+
157
+ VALUE lsqpackrb_dec_feed_header(VALUE self, VALUE stream_id, VALUE data) {
158
+ DecoderObj* dec;
159
+ TypedData_Get_Struct(self, DecoderObj, &decoder_data_type, dec);
160
+ uint64_t strm_id;
161
+ size_t dec_len = DECODER_BUF_SIZE;
162
+ unsigned char* feed_data;
163
+ int feed_data_len;
164
+ enum lsqpack_read_header_status status;
165
+ struct header_block* hblock;
166
+
167
+ strm_id = FIX2LONG(stream_id);
168
+ Check_Type(data, T_STRING);
169
+ feed_data = StringValuePtr(data);
170
+ feed_data_len = RSTRING_LEN(data);
171
+
172
+ STAILQ_FOREACH(hblock, &dec->pending_blocks, entries) {
173
+ if (hblock->stream_id == strm_id) {
174
+ rb_raise(rb_eError, "a header block for stream %d already exists", strm_id);
175
+ return NULL;
176
+ }
177
+ }
178
+ hblock = header_block_new(strm_id, feed_data, feed_data_len);
179
+ status = lsqpack_dec_header_in(
180
+ &dec->dec,
181
+ hblock,
182
+ strm_id,
183
+ hblock->data_len,
184
+ &hblock->data_ptr,
185
+ hblock->data_len,
186
+ &hblock->hlist,
187
+ dec->decoder_buf,
188
+ &dec_len
189
+ );
190
+ if (status == LQRHS_BLOCKED || status == LQRHS_NEED) {
191
+ hblock->blocked = 1;
192
+ STAILQ_INSERT_TAIL(&dec->pending_blocks, hblock, entries);
193
+ rb_raise(rb_eStreamBlocked, "stream %d is blocked", strm_id);
194
+ return NULL;
195
+ } else if (status != LQRHS_DONE) {
196
+ rb_raise(rb_eDecompressionFailed, "lsqpack_dec_header_in for stream %d failed", strm_id);
197
+ header_block_free(hblock);
198
+ return NULL;
199
+ }
200
+
201
+ VALUE control = rb_str_new(dec->decoder_buf, dec_len);
202
+ VALUE headers = hlist_to_headers(hblock->hlist);
203
+ header_block_free(hblock);
204
+ VALUE tuple = rb_ary_new_capa(2);
205
+ rb_ary_store(tuple, 0, control);
206
+ rb_ary_store(tuple, 1, headers);
207
+ return tuple;
208
+ }
209
+
210
+ VALUE lsqpackrb_dec_resume_header(VALUE self, VALUE stream_id) {
211
+ DecoderObj* dec;
212
+ TypedData_Get_Struct(self, DecoderObj, &decoder_data_type, dec);
213
+ uint64_t strm_id;
214
+ size_t dec_len = DECODER_BUF_SIZE;
215
+ enum lsqpack_read_header_status status;
216
+ struct header_block *hblock;
217
+ int found = 0;
218
+
219
+ Check_Type(stream_id, T_FIXNUM);
220
+ strm_id = NUM2LONG(stream_id);
221
+
222
+ STAILQ_FOREACH(hblock, &dec->pending_blocks, entries) {
223
+ if(hblock->stream_id == strm_id) {
224
+ found = 1;
225
+ break;
226
+ }
227
+ }
228
+ if (!found) {
229
+ rb_raise(rb_eError, "no pending header block for stream %d", strm_id);
230
+ return NULL;
231
+ }
232
+ if (hblock->blocked) {
233
+ status = LQRHS_BLOCKED;
234
+ } else {
235
+ status = lsqpack_dec_header_read(
236
+ &dec->dec,
237
+ hblock,
238
+ &hblock->data_ptr,
239
+ hblock->data_len - (hblock->data_ptr - hblock->data),
240
+ &hblock->hlist,
241
+ dec->decoder_buf,
242
+ &dec_len
243
+ );
244
+ }
245
+
246
+ if (status == LQRHS_BLOCKED || status == LQRHS_NEED) {
247
+ hblock->blocked = 1;
248
+ rb_raise(rb_eStreamBlocked, "stream %d is blocked", strm_id);
249
+ return NULL;
250
+ } else if (status != LQRHS_DONE) {
251
+ rb_raise(rb_eDecompressionFailed, "lsqpack_dec_header_in for stream %d failed (%d)", strm_id, status);
252
+ STAILQ_REMOVE(&dec->pending_blocks, hblock, header_block, entries);
253
+ header_block_free(hblock);
254
+ return NULL;
255
+ }
256
+ VALUE control = rb_str_new(dec->decoder_buf, dec_len);
257
+ VALUE headers = hlist_to_headers(hblock->hlist);
258
+ VALUE tuple = rb_ary_new_capa(2);
259
+ rb_ary_store(tuple, 0, control);
260
+ rb_ary_store(tuple, 1, headers);
261
+ return tuple;
262
+ }
263
+
264
+
265
+
266
+ typedef struct {
267
+ struct lsqpack_enc enc;
268
+ unsigned char header_buf[HEADER_BUF_SIZE];
269
+ unsigned char encoder_buf[ENCODER_BUF_SIZE];
270
+ unsigned char prefix_buf[PREFIX_MAX_SIZE];
271
+ } EncoderObj;
272
+
273
+ size_t lsqpackrb_enc_size(EncoderObj* data) {
274
+ return sizeof(EncoderObj);
275
+ }
276
+
277
+ void lsqpackrb_enc_free(EncoderObj* data) {
278
+ lsqpack_enc_cleanup(&data->enc);
279
+ free(data);
280
+ }
281
+
282
+ static const rb_data_type_t encoder_data_type = {
283
+ .wrap_struct_name = "lsqpack_enc_rb",
284
+ .function = {
285
+ .dmark = NULL,
286
+ .dfree = lsqpackrb_enc_free,
287
+ .dsize = lsqpackrb_enc_size,
288
+ },
289
+ .data = NULL,
290
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
291
+ };
292
+
293
+ VALUE lsqpackrb_enc_alloc(VALUE self) {
294
+ EncoderObj* data = malloc(sizeof(EncoderObj));
295
+ return TypedData_Wrap_Struct(self, &encoder_data_type, data);
296
+ }
297
+
298
+ VALUE lsqpackrb_enc_init(VALUE self) {
299
+ EncoderObj* data;
300
+ TypedData_Get_Struct(self, EncoderObj, &encoder_data_type, data);
301
+ lsqpack_enc_preinit(&data->enc, NULL);
302
+ return self;
303
+ }
304
+
305
+ VALUE lsqpackrb_enc_apply_settings(VALUE self, VALUE rb_max_table_capacity, VALUE rb_blocked_streams) {
306
+ EncoderObj* data;
307
+ TypedData_Get_Struct(self, EncoderObj, &encoder_data_type, data);
308
+
309
+ unsigned int max_table_capacity = FIX2INT(rb_max_table_capacity);
310
+ unsigned int blocked_streams = FIX2INT(rb_blocked_streams);
311
+ unsigned char stdc_buf[LSQPACK_LONGEST_SDTC];
312
+ size_t stdc_buf_len = sizeof(stdc_buf);
313
+
314
+ if(lsqpack_enc_init(&data->enc, NULL, max_table_capacity, max_table_capacity, blocked_streams, LSQPACK_ENC_OPT_STAGE_2, stdc_buf, &stdc_buf_len) != 0) {
315
+ rb_raise(rb_eError, "lsqpack_enc_init failed");
316
+ return NULL;
317
+ }
318
+ return rb_str_new(stdc_buf, stdc_buf_len);
319
+ }
320
+
321
+ VALUE lsqpackrb_enc_encode(VALUE self, VALUE stream_id, VALUE headers) {
322
+ EncoderObj* data;
323
+ TypedData_Get_Struct(self, EncoderObj, &encoder_data_type, data);
324
+
325
+ uint64_t str_id = FIX2LONG(stream_id);
326
+ unsigned int sequence_number = 0;
327
+ size_t enc_len, header_len, prefix_len;
328
+ size_t enc_off = 0, header_off = PREFIX_MAX_SIZE, prefix_off = 0;
329
+
330
+ Check_Type(headers, T_ARRAY);
331
+ if (rb_array_len(headers) == 0) {
332
+ // no headers present
333
+ return NULL;
334
+ }
335
+
336
+ if (lsqpack_enc_start_header(&data->enc, str_id, sequence_number) != 0) {
337
+ rb_raise(rb_eError, "lsqpack_enc_start_header failed");
338
+ return NULL;
339
+ }
340
+
341
+ for (int i = 0; i < rb_array_len(headers); i++) {
342
+ VALUE entry = rb_ary_entry(headers, i);
343
+ if (rb_array_len(entry) != 2) {
344
+ // no headers present
345
+ rb_raise(rb_eError, "Each header entry must be include 2 elements");
346
+ return NULL;
347
+ }
348
+
349
+ VALUE name_ = rb_ary_entry(entry, 0);
350
+ VALUE value_ = rb_ary_entry(entry, 1);
351
+ char* name = StringValuePtr(name_);
352
+ int name_len = RSTRING_LEN(name_);
353
+ char* value = StringValuePtr(value_);
354
+ int value_len = RSTRING_LEN(value_);
355
+
356
+ enc_len = ENCODER_BUF_SIZE - enc_off;
357
+ header_len = HEADER_BUF_SIZE - header_off;
358
+ // printf("==========name %s, len %d\n", name, name_len);
359
+ // printf("==========value %s, len %d\n", value, value_len);
360
+ // printf("==========enc_len(init) %d\n", enc_len);
361
+ if (lsqpack_enc_encode(&data->enc,
362
+ data->encoder_buf + enc_off, &enc_len,
363
+ data->header_buf + header_off, &header_len,
364
+ name, name_len, value, value_len, 0) != LQES_OK) {
365
+ rb_raise(rb_eError, "lsqpack_enc_encode failed");
366
+ return NULL;
367
+ }
368
+ // printf("==========enc_len %d\n", enc_len);
369
+ enc_off += enc_len;
370
+ header_off += header_len;
371
+ // printf("==========enc_off %d\n", enc_off);
372
+ }
373
+
374
+ prefix_len = lsqpack_enc_end_header(&data->enc, data->prefix_buf, PREFIX_MAX_SIZE, NULL);
375
+ if (prefix_len <= 0) {
376
+ rb_raise(rb_eError, "lsqpack_enc_end_header failed");
377
+ return NULL;
378
+ }
379
+ prefix_off = PREFIX_MAX_SIZE - prefix_len;
380
+ memcpy(data->header_buf + prefix_off, data->prefix_buf, prefix_len);
381
+ VALUE name = rb_str_new(data->encoder_buf, enc_off);
382
+ VALUE value = rb_str_new(data->header_buf + prefix_off, (int)(header_off - prefix_off));
383
+ VALUE tuple = rb_ary_new_from_args(2, name, value);
384
+ return tuple;
385
+ }
386
+
387
+ void lsqpackrb_enc_feed_decoder(VALUE self, VALUE rb_data) {
388
+ EncoderObj* data;
389
+ TypedData_Get_Struct(self, EncoderObj, &encoder_data_type, data);
390
+ char* feed_data = StringValuePtr(rb_data);
391
+ int feed_data_len = RSTRING_LEN(rb_data);
392
+
393
+ if (lsqpack_enc_decoder_in(&data->enc, feed_data, feed_data_len) < 0) {
394
+ rb_raise(rb_eDecoderStreamErr, "lsqpack_enc_decoder_in failed");
395
+ return NULL;
396
+ }
397
+ return NULL;
398
+ }
399
+
400
+
401
+ void
402
+ Init_lsqpack(void)
403
+ {
404
+ rb_mLsqpack = rb_define_module("Lsqpack");
405
+ rb_eError = rb_define_class_under(rb_mLsqpack, "Error", rb_eStandardError);
406
+ rb_eDecompressionFailed = rb_define_class_under(rb_mLsqpack, "DecompressionFailed", rb_eError);
407
+ rb_eDecoderStreamErr = rb_define_class_under(rb_mLsqpack, "DecoderStreamError", rb_eError);
408
+ rb_eEncoderStreamErr = rb_define_class_under(rb_mLsqpack, "EncoderStreamError", rb_eError);
409
+ rb_eStreamBlocked = rb_define_class_under(rb_mLsqpack, "StreamBlocked", rb_eError);
410
+
411
+ // Encoder
412
+ rb_clsEncoder = rb_define_class_under(rb_mLsqpack, "Encoder", rb_cObject);
413
+ rb_define_alloc_func(rb_clsEncoder, lsqpackrb_enc_alloc);
414
+ rb_define_method(rb_clsEncoder, "initialize", lsqpackrb_enc_init, 0);
415
+ rb_define_private_method(rb_clsEncoder, "_apply_settings", lsqpackrb_enc_apply_settings, 2);
416
+ rb_define_method(rb_clsEncoder, "encode", lsqpackrb_enc_encode, 2);
417
+ rb_define_method(rb_clsEncoder, "feed_decoder", lsqpackrb_enc_feed_decoder, 1);
418
+
419
+ // Decoder
420
+ rb_clsDecoder = rb_define_class_under(rb_mLsqpack, "Decoder", rb_cObject);
421
+ rb_define_alloc_func(rb_clsDecoder, lsqpackrb_dec_alloc);
422
+ rb_define_method(rb_clsDecoder, "initialize", lsqpackrb_dec_init, -1);
423
+ rb_define_method(rb_clsDecoder, "feed_encoder", lsqpackrb_dec_feed_encoder, 1);
424
+ rb_define_method(rb_clsDecoder, "feed_header", lsqpackrb_dec_feed_header, 2);
425
+ rb_define_method(rb_clsDecoder, "resume_header", lsqpackrb_dec_resume_header, 1);
426
+ }
@@ -0,0 +1,6 @@
1
+ #ifndef LSQPACK_RB_H
2
+ #define LSQPACK_RB_H 1
3
+
4
+ #include "ruby.h"
5
+
6
+ #endif /* LSQPACK_RB_H */
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lsqpack
4
+ VERSION = "0.1.0"
5
+ end
data/lib/lsqpack.rb ADDED
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lsqpack/version"
4
+ require_relative "lsqpack/lsqpack"
5
+
6
+ module Lsqpack
7
+ # class Error < StandardError; end
8
+ # class DecompressonFailed < Error; end
9
+ # class DecoderStreamError < Error; end
10
+ # class EncoderStreamError < Error; end
11
+ # class StreamBlocked < Error; end
12
+ # Your code goes here...
13
+
14
+ class Encoder
15
+ # def initialize
16
+ # end
17
+
18
+ def apply_settings(max_table_capacity: 0, blocked_streams: 0)
19
+ _apply_settings(max_table_capacity, blocked_streams)
20
+ end
21
+ end
22
+
23
+ # class Decoder
24
+ # def initialize
25
+ # end
26
+
27
+ # def decode
28
+ # end
29
+ # end
30
+ end
@@ -0,0 +1,14 @@
1
+ version: 1.0.{build}
2
+
3
+ before_build:
4
+ - cmake -DLSQPACK_TESTS=ON .
5
+
6
+ build_script:
7
+ - cmake --build .
8
+
9
+ before_test:
10
+ - set PATH=%PATH%;c:\projects\ls-qpack\bin\Debug
11
+
12
+ test_script:
13
+ - dir
14
+ - msbuild RUN_TESTS.vcxproj