picohttp 0.2.0 → 0.3.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: 3b357892a265429f3731c5cc68f56aaa3649b1f346a9c86f021bb67be2e601ac
4
- data.tar.gz: 7a499b9fe596ee583c238956d66f05f3ff6caeabd0594a5db1d0f3c1b6d3e023
3
+ metadata.gz: cd749cf10a5d5718c50fee68b7598b44cf01e3709789931d2ed8ee6c8d69adb0
4
+ data.tar.gz: 664f012d7379f4c4ae09213f6fc68aa7152ce86f1ba1e7138eddbb17a7e56864
5
5
  SHA512:
6
- metadata.gz: 704efdbcbd2daab576977d4f2857307a7de44a41b5785cc6d215886777b7d97e3e3ba257670e40afd4c0b49f05024c8e1b26f1a8a41864e5b70393736a26fe6a
7
- data.tar.gz: 7318f991db2981ed09349edfd907141342121fa65f3a45fa0e51dc2503b973b1c83681356eacad29fd1f95c44af130ad1eb0952e52ae04e4a139678bf172d016
6
+ metadata.gz: 5c3af0a35ffd06aefd5bb57f993394c775030aecd60c9dea69e9deb9c0342d1c4d8db89e3c722b68c7a7839fd02c0458f92613e66dcd115ac0153525f3695828
7
+ data.tar.gz: 719f7c6ded60d90a21e14cf9f3c9220fda942f79a2d2a73be1a16dfb90a14ee95dabddf55b37ca176460abd33874064a2f0838992b44f2781676106172a489c2
@@ -2,7 +2,8 @@
2
2
  #include "picohttpparser.h"
3
3
 
4
4
  #define MAX_HEADER_NAME_LEN 256
5
- #define MAX_HEADERS 100
5
+ #define MAX_HTTP_HEADERS 100
6
+ #define EXTRA_RACK_HEADERS 8
6
7
 
7
8
  VALUE rb_mPicohttp;
8
9
  VALUE rb_ePicohttpParseError;
@@ -12,6 +13,10 @@ static VALUE rb_str_request_method;
12
13
  static VALUE rb_str_server_protocol;
13
14
  static VALUE rb_str_path_info;
14
15
  static VALUE rb_str_query_string;
16
+ static VALUE rb_str_request_uri;
17
+ static VALUE rb_str_script_name;
18
+ static VALUE rb_str_server_name;
19
+ static VALUE rb_str_server_port;
15
20
  static VALUE rb_str_empty;
16
21
  static VALUE rb_str_http_1_0;
17
22
  static VALUE rb_str_http_1_1;
@@ -78,7 +83,7 @@ picohttp_parse_request(VALUE self, VALUE str)
78
83
 
79
84
  const char *method, *path;
80
85
  int minor_version;
81
- struct phr_header headers[MAX_HEADERS];
86
+ struct phr_header headers[MAX_HTTP_HEADERS];
82
87
  size_t method_len, path_len, num_headers = sizeof(headers) / sizeof(headers[0]);
83
88
 
84
89
  int result = phr_parse_request(buf, len, &method, &method_len, &path, &path_len,
@@ -109,6 +114,24 @@ picohttp_parse_request(VALUE self, VALUE str)
109
114
  INT2FIX(result));
110
115
  }
111
116
 
117
+ static VALUE
118
+ build_hash_with_combined_duplicates(VALUE *header_values, int count)
119
+ {
120
+ VALUE env = rb_hash_new();
121
+ for (int i = 0; i < count; i += 2) {
122
+ VALUE key = header_values[i];
123
+ VALUE val = header_values[i + 1];
124
+ VALUE existing = rb_hash_aref(env, key);
125
+ if (existing == Qnil) {
126
+ rb_hash_aset(env, key, val);
127
+ } else {
128
+ rb_str_cat2(existing, ", ");
129
+ rb_str_cat(existing, RSTRING_PTR(val), RSTRING_LEN(val));
130
+ }
131
+ }
132
+ return env;
133
+ }
134
+
112
135
  static VALUE
113
136
  picohttp_parse_request_env(VALUE self, VALUE str)
114
137
  {
@@ -119,7 +142,7 @@ picohttp_parse_request_env(VALUE self, VALUE str)
119
142
 
120
143
  const char *method, *path;
121
144
  int minor_version;
122
- struct phr_header headers[MAX_HEADERS];
145
+ struct phr_header headers[MAX_HTTP_HEADERS];
123
146
  size_t method_len, path_len, num_headers = sizeof(headers) / sizeof(headers[0]);
124
147
 
125
148
  int result = phr_parse_request(buf, len, &method, &method_len, &path, &path_len,
@@ -132,7 +155,7 @@ picohttp_parse_request_env(VALUE self, VALUE str)
132
155
  rb_raise(rb_ePicohttpParseError, "Invalid HTTP request");
133
156
  }
134
157
 
135
- VALUE header_values[MAX_HEADERS * 2];
158
+ VALUE header_values[(MAX_HTTP_HEADERS + EXTRA_RACK_HEADERS) * 2];
136
159
  int idx = 0;
137
160
 
138
161
  // Standard CGI/Rack environment variables
@@ -161,6 +184,14 @@ picohttp_parse_request_env(VALUE self, VALUE str)
161
184
  header_values[idx++] = rb_str_empty;
162
185
  }
163
186
 
187
+ // REQUEST_URI is the full path including query string
188
+ header_values[idx++] = rb_str_request_uri;
189
+ header_values[idx++] = rb_str_new(path, path_len);
190
+
191
+ // SCRIPT_NAME is always empty
192
+ header_values[idx++] = rb_str_script_name;
193
+ header_values[idx++] = rb_str_empty;
194
+
164
195
  // Convert headers to HTTP_ prefixed environment variables
165
196
  for (size_t i = 0; i < num_headers; i++) {
166
197
  if (headers[i].name == NULL) {
@@ -169,6 +200,27 @@ picohttp_parse_request_env(VALUE self, VALUE str)
169
200
 
170
201
  header_values[idx++] = header_name_to_env_key(headers[i].name, headers[i].name_len);
171
202
  header_values[idx++] = rb_str_new(headers[i].value, headers[i].value_len);
203
+
204
+ // Extract SERVER_NAME/SERVER_PORT from Host header
205
+ if (headers[i].name_len == 4 &&
206
+ (headers[i].name[0] | 0x20) == 'h' &&
207
+ (headers[i].name[1] | 0x20) == 'o' &&
208
+ (headers[i].name[2] | 0x20) == 's' &&
209
+ (headers[i].name[3] | 0x20) == 't') {
210
+ const char *host = headers[i].value;
211
+ size_t host_len = headers[i].value_len;
212
+ const char *colon = memchr(host, ':', host_len);
213
+
214
+ if (colon) {
215
+ header_values[idx++] = rb_str_server_name;
216
+ header_values[idx++] = rb_str_new(host, colon - host);
217
+ header_values[idx++] = rb_str_server_port;
218
+ header_values[idx++] = rb_str_new(colon + 1, host_len - (colon - host) - 1);
219
+ } else {
220
+ header_values[idx++] = rb_str_server_name;
221
+ header_values[idx++] = rb_str_new(host, host_len);
222
+ }
223
+ }
172
224
  }
173
225
 
174
226
  #ifdef HAVE_RB_HASH_NEW_CAPA
@@ -179,9 +231,22 @@ picohttp_parse_request_env(VALUE self, VALUE str)
179
231
 
180
232
  rb_hash_bulk_insert(idx, header_values, env);
181
233
 
234
+ // Handle duplicate headers per RFC 7230
235
+ if (RHASH_SIZE(env) != (size_t)(idx / 2)) {
236
+ return build_hash_with_combined_duplicates(header_values, idx);
237
+ }
238
+
182
239
  return env;
183
240
  }
184
241
 
242
+ static VALUE
243
+ register_interned_string(const char *str)
244
+ {
245
+ VALUE val = rb_interned_str_cstr(str);
246
+ rb_gc_register_mark_object(val);
247
+ return val;
248
+ }
249
+
185
250
  RUBY_FUNC_EXPORTED void
186
251
  Init_picohttp(void)
187
252
  {
@@ -196,20 +261,16 @@ Init_picohttp(void)
196
261
 
197
262
  // Initialize interned string constants
198
263
  init_string_lookup();
199
- rb_str_request_method = rb_interned_str_cstr("REQUEST_METHOD");
200
- rb_str_server_protocol = rb_interned_str_cstr("SERVER_PROTOCOL");
201
- rb_str_path_info = rb_interned_str_cstr("PATH_INFO");
202
- rb_str_query_string = rb_interned_str_cstr("QUERY_STRING");
203
- rb_str_empty = rb_interned_str_cstr("");
204
- rb_str_http_1_0 = rb_interned_str_cstr("HTTP/1.0");
205
- rb_str_http_1_1 = rb_interned_str_cstr("HTTP/1.1");
206
-
207
- // Prevent garbage collection of constants
208
- rb_gc_register_address(&rb_str_request_method);
209
- rb_gc_register_address(&rb_str_server_protocol);
210
- rb_gc_register_address(&rb_str_path_info);
211
- rb_gc_register_address(&rb_str_query_string);
212
- rb_gc_register_address(&rb_str_empty);
213
- rb_gc_register_address(&rb_str_http_1_0);
214
- rb_gc_register_address(&rb_str_http_1_1);
264
+
265
+ rb_str_request_method = register_interned_string("REQUEST_METHOD");
266
+ rb_str_server_protocol = register_interned_string("SERVER_PROTOCOL");
267
+ rb_str_path_info = register_interned_string("PATH_INFO");
268
+ rb_str_query_string = register_interned_string("QUERY_STRING");
269
+ rb_str_request_uri = register_interned_string("REQUEST_URI");
270
+ rb_str_script_name = register_interned_string("SCRIPT_NAME");
271
+ rb_str_server_name = register_interned_string("SERVER_NAME");
272
+ rb_str_server_port = register_interned_string("SERVER_PORT");
273
+ rb_str_empty = register_interned_string("");
274
+ rb_str_http_1_0 = register_interned_string("HTTP/1.0");
275
+ rb_str_http_1_1 = register_interned_string("HTTP/1.1");
215
276
  }
@@ -1,54 +1,56 @@
1
1
  /* This file is auto-generated by tool/generate_lookup.rb */
2
2
 
3
- static VALUE rb_s_HTTP_HOST; /* "HTTP_HOST" */
4
- static VALUE rb_s_HTTP_ACCEPT; /* "HTTP_ACCEPT" */
5
- static VALUE rb_s_HTTP_COOKIE; /* "HTTP_COOKIE" */
6
- static VALUE rb_s_HTTP_REFERER; /* "HTTP_REFERER" */
7
- static VALUE rb_s_HTTP_USER_AGENT; /* "HTTP_USER_AGENT" */
8
- static VALUE rb_s_HTTP_CONNECTION; /* "HTTP_CONNECTION" */
9
- static VALUE rb_s_CONTENT_TYPE; /* "CONTENT_TYPE" */
10
- static VALUE rb_s_HTTP_CACHE_CONTROL; /* "HTTP_CACHE_CONTROL" */
11
- static VALUE rb_s_HTTP_AUTHORIZATION; /* "HTTP_AUTHORIZATION" */
12
- static VALUE rb_s_CONTENT_LENGTH; /* "CONTENT_LENGTH" */
13
- static VALUE rb_s_HTTP_ACCEPT_ENCODING; /* "HTTP_ACCEPT_ENCODING" */
14
- static VALUE rb_s_HTTP_ACCEPT_LANGUAGE; /* "HTTP_ACCEPT_LANGUAGE" */
15
- static VALUE rb_s_GET; /* "GET" */
16
- static VALUE rb_s_PUT; /* "PUT" */
17
- static VALUE rb_s_POST; /* "POST" */
18
- static VALUE rb_s_HEAD; /* "HEAD" */
19
- static VALUE rb_s_PATCH; /* "PATCH" */
20
- static VALUE rb_s_DELETE; /* "DELETE" */
21
- static VALUE rb_s_OPTIONS; /* "OPTIONS" */
3
+ static struct string_lookup_t {
4
+ VALUE http_host; /* "HTTP_HOST" */
5
+ VALUE http_accept; /* "HTTP_ACCEPT" */
6
+ VALUE http_cookie; /* "HTTP_COOKIE" */
7
+ VALUE http_referer; /* "HTTP_REFERER" */
8
+ VALUE http_user_agent; /* "HTTP_USER_AGENT" */
9
+ VALUE http_connection; /* "HTTP_CONNECTION" */
10
+ VALUE content_type; /* "CONTENT_TYPE" */
11
+ VALUE http_cache_control; /* "HTTP_CACHE_CONTROL" */
12
+ VALUE http_authorization; /* "HTTP_AUTHORIZATION" */
13
+ VALUE content_length; /* "CONTENT_LENGTH" */
14
+ VALUE http_accept_encoding; /* "HTTP_ACCEPT_ENCODING" */
15
+ VALUE http_accept_language; /* "HTTP_ACCEPT_LANGUAGE" */
16
+ VALUE get; /* "GET" */
17
+ VALUE put; /* "PUT" */
18
+ VALUE post; /* "POST" */
19
+ VALUE head; /* "HEAD" */
20
+ VALUE patch; /* "PATCH" */
21
+ VALUE delete; /* "DELETE" */
22
+ VALUE options; /* "OPTIONS" */
23
+ } string_lookup;
22
24
 
23
25
  static VALUE lookup_header(const char *s, size_t len) {
24
26
  switch (len) {
25
27
  case 4:
26
- if ((s[0] | 32) == 'h' && (s[1] | 32) == 'o' && (s[2] | 32) == 's' && (s[3] | 32) == 't') return rb_s_HTTP_HOST;
28
+ if ((s[0] | 32) == 'h' && (s[1] | 32) == 'o' && (s[2] | 32) == 's' && (s[3] | 32) == 't') return string_lookup.http_host;
27
29
  break;
28
30
  case 6:
29
- if ((s[0] | 32) == 'a' && (s[1] | 32) == 'c' && (s[2] | 32) == 'c' && (s[3] | 32) == 'e' && (s[4] | 32) == 'p' && (s[5] | 32) == 't') return rb_s_HTTP_ACCEPT;
30
- if ((s[0] | 32) == 'c' && (s[1] | 32) == 'o' && (s[2] | 32) == 'o' && (s[3] | 32) == 'k' && (s[4] | 32) == 'i' && (s[5] | 32) == 'e') return rb_s_HTTP_COOKIE;
31
+ if ((s[0] | 32) == 'a' && (s[1] | 32) == 'c' && (s[2] | 32) == 'c' && (s[3] | 32) == 'e' && (s[4] | 32) == 'p' && (s[5] | 32) == 't') return string_lookup.http_accept;
32
+ if ((s[0] | 32) == 'c' && (s[1] | 32) == 'o' && (s[2] | 32) == 'o' && (s[3] | 32) == 'k' && (s[4] | 32) == 'i' && (s[5] | 32) == 'e') return string_lookup.http_cookie;
31
33
  break;
32
34
  case 7:
33
- if ((s[0] | 32) == 'r' && (s[1] | 32) == 'e' && (s[2] | 32) == 'f' && (s[3] | 32) == 'e' && (s[4] | 32) == 'r' && (s[5] | 32) == 'e' && (s[6] | 32) == 'r') return rb_s_HTTP_REFERER;
35
+ if ((s[0] | 32) == 'r' && (s[1] | 32) == 'e' && (s[2] | 32) == 'f' && (s[3] | 32) == 'e' && (s[4] | 32) == 'r' && (s[5] | 32) == 'e' && (s[6] | 32) == 'r') return string_lookup.http_referer;
34
36
  break;
35
37
  case 10:
36
- if ((s[0] | 32) == 'u' && (s[1] | 32) == 's' && (s[2] | 32) == 'e' && (s[3] | 32) == 'r' && (s[4] | 32) == '-' && (s[5] | 32) == 'a' && (s[6] | 32) == 'g' && (s[7] | 32) == 'e' && (s[8] | 32) == 'n' && (s[9] | 32) == 't') return rb_s_HTTP_USER_AGENT;
37
- if ((s[0] | 32) == 'c' && (s[1] | 32) == 'o' && (s[2] | 32) == 'n' && (s[3] | 32) == 'n' && (s[4] | 32) == 'e' && (s[5] | 32) == 'c' && (s[6] | 32) == 't' && (s[7] | 32) == 'i' && (s[8] | 32) == 'o' && (s[9] | 32) == 'n') return rb_s_HTTP_CONNECTION;
38
+ if ((s[0] | 32) == 'u' && (s[1] | 32) == 's' && (s[2] | 32) == 'e' && (s[3] | 32) == 'r' && (s[4] | 32) == '-' && (s[5] | 32) == 'a' && (s[6] | 32) == 'g' && (s[7] | 32) == 'e' && (s[8] | 32) == 'n' && (s[9] | 32) == 't') return string_lookup.http_user_agent;
39
+ if ((s[0] | 32) == 'c' && (s[1] | 32) == 'o' && (s[2] | 32) == 'n' && (s[3] | 32) == 'n' && (s[4] | 32) == 'e' && (s[5] | 32) == 'c' && (s[6] | 32) == 't' && (s[7] | 32) == 'i' && (s[8] | 32) == 'o' && (s[9] | 32) == 'n') return string_lookup.http_connection;
38
40
  break;
39
41
  case 12:
40
- if ((s[0] | 32) == 'c' && (s[1] | 32) == 'o' && (s[2] | 32) == 'n' && (s[3] | 32) == 't' && (s[4] | 32) == 'e' && (s[5] | 32) == 'n' && (s[6] | 32) == 't' && (s[7] | 32) == '-' && (s[8] | 32) == 't' && (s[9] | 32) == 'y' && (s[10] | 32) == 'p' && (s[11] | 32) == 'e') return rb_s_CONTENT_TYPE;
42
+ if ((s[0] | 32) == 'c' && (s[1] | 32) == 'o' && (s[2] | 32) == 'n' && (s[3] | 32) == 't' && (s[4] | 32) == 'e' && (s[5] | 32) == 'n' && (s[6] | 32) == 't' && (s[7] | 32) == '-' && (s[8] | 32) == 't' && (s[9] | 32) == 'y' && (s[10] | 32) == 'p' && (s[11] | 32) == 'e') return string_lookup.content_type;
41
43
  break;
42
44
  case 13:
43
- if ((s[0] | 32) == 'c' && (s[1] | 32) == 'a' && (s[2] | 32) == 'c' && (s[3] | 32) == 'h' && (s[4] | 32) == 'e' && (s[5] | 32) == '-' && (s[6] | 32) == 'c' && (s[7] | 32) == 'o' && (s[8] | 32) == 'n' && (s[9] | 32) == 't' && (s[10] | 32) == 'r' && (s[11] | 32) == 'o' && (s[12] | 32) == 'l') return rb_s_HTTP_CACHE_CONTROL;
44
- if ((s[0] | 32) == 'a' && (s[1] | 32) == 'u' && (s[2] | 32) == 't' && (s[3] | 32) == 'h' && (s[4] | 32) == 'o' && (s[5] | 32) == 'r' && (s[6] | 32) == 'i' && (s[7] | 32) == 'z' && (s[8] | 32) == 'a' && (s[9] | 32) == 't' && (s[10] | 32) == 'i' && (s[11] | 32) == 'o' && (s[12] | 32) == 'n') return rb_s_HTTP_AUTHORIZATION;
45
+ if ((s[0] | 32) == 'c' && (s[1] | 32) == 'a' && (s[2] | 32) == 'c' && (s[3] | 32) == 'h' && (s[4] | 32) == 'e' && (s[5] | 32) == '-' && (s[6] | 32) == 'c' && (s[7] | 32) == 'o' && (s[8] | 32) == 'n' && (s[9] | 32) == 't' && (s[10] | 32) == 'r' && (s[11] | 32) == 'o' && (s[12] | 32) == 'l') return string_lookup.http_cache_control;
46
+ if ((s[0] | 32) == 'a' && (s[1] | 32) == 'u' && (s[2] | 32) == 't' && (s[3] | 32) == 'h' && (s[4] | 32) == 'o' && (s[5] | 32) == 'r' && (s[6] | 32) == 'i' && (s[7] | 32) == 'z' && (s[8] | 32) == 'a' && (s[9] | 32) == 't' && (s[10] | 32) == 'i' && (s[11] | 32) == 'o' && (s[12] | 32) == 'n') return string_lookup.http_authorization;
45
47
  break;
46
48
  case 14:
47
- if ((s[0] | 32) == 'c' && (s[1] | 32) == 'o' && (s[2] | 32) == 'n' && (s[3] | 32) == 't' && (s[4] | 32) == 'e' && (s[5] | 32) == 'n' && (s[6] | 32) == 't' && (s[7] | 32) == '-' && (s[8] | 32) == 'l' && (s[9] | 32) == 'e' && (s[10] | 32) == 'n' && (s[11] | 32) == 'g' && (s[12] | 32) == 't' && (s[13] | 32) == 'h') return rb_s_CONTENT_LENGTH;
49
+ if ((s[0] | 32) == 'c' && (s[1] | 32) == 'o' && (s[2] | 32) == 'n' && (s[3] | 32) == 't' && (s[4] | 32) == 'e' && (s[5] | 32) == 'n' && (s[6] | 32) == 't' && (s[7] | 32) == '-' && (s[8] | 32) == 'l' && (s[9] | 32) == 'e' && (s[10] | 32) == 'n' && (s[11] | 32) == 'g' && (s[12] | 32) == 't' && (s[13] | 32) == 'h') return string_lookup.content_length;
48
50
  break;
49
51
  case 15:
50
- if ((s[0] | 32) == 'a' && (s[1] | 32) == 'c' && (s[2] | 32) == 'c' && (s[3] | 32) == 'e' && (s[4] | 32) == 'p' && (s[5] | 32) == 't' && (s[6] | 32) == '-' && (s[7] | 32) == 'e' && (s[8] | 32) == 'n' && (s[9] | 32) == 'c' && (s[10] | 32) == 'o' && (s[11] | 32) == 'd' && (s[12] | 32) == 'i' && (s[13] | 32) == 'n' && (s[14] | 32) == 'g') return rb_s_HTTP_ACCEPT_ENCODING;
51
- if ((s[0] | 32) == 'a' && (s[1] | 32) == 'c' && (s[2] | 32) == 'c' && (s[3] | 32) == 'e' && (s[4] | 32) == 'p' && (s[5] | 32) == 't' && (s[6] | 32) == '-' && (s[7] | 32) == 'l' && (s[8] | 32) == 'a' && (s[9] | 32) == 'n' && (s[10] | 32) == 'g' && (s[11] | 32) == 'u' && (s[12] | 32) == 'a' && (s[13] | 32) == 'g' && (s[14] | 32) == 'e') return rb_s_HTTP_ACCEPT_LANGUAGE;
52
+ if ((s[0] | 32) == 'a' && (s[1] | 32) == 'c' && (s[2] | 32) == 'c' && (s[3] | 32) == 'e' && (s[4] | 32) == 'p' && (s[5] | 32) == 't' && (s[6] | 32) == '-' && (s[7] | 32) == 'e' && (s[8] | 32) == 'n' && (s[9] | 32) == 'c' && (s[10] | 32) == 'o' && (s[11] | 32) == 'd' && (s[12] | 32) == 'i' && (s[13] | 32) == 'n' && (s[14] | 32) == 'g') return string_lookup.http_accept_encoding;
53
+ if ((s[0] | 32) == 'a' && (s[1] | 32) == 'c' && (s[2] | 32) == 'c' && (s[3] | 32) == 'e' && (s[4] | 32) == 'p' && (s[5] | 32) == 't' && (s[6] | 32) == '-' && (s[7] | 32) == 'l' && (s[8] | 32) == 'a' && (s[9] | 32) == 'n' && (s[10] | 32) == 'g' && (s[11] | 32) == 'u' && (s[12] | 32) == 'a' && (s[13] | 32) == 'g' && (s[14] | 32) == 'e') return string_lookup.http_accept_language;
52
54
  break;
53
55
  }
54
56
  return Qnil;
@@ -57,64 +59,108 @@ static VALUE lookup_header(const char *s, size_t len) {
57
59
  static VALUE lookup_method(const char *s, size_t len) {
58
60
  switch (len) {
59
61
  case 3:
60
- if (s[0] == 'G' && s[1] == 'E' && s[2] == 'T') return rb_s_GET;
61
- if (s[0] == 'P' && s[1] == 'U' && s[2] == 'T') return rb_s_PUT;
62
+ if (s[0] == 'G' && s[1] == 'E' && s[2] == 'T') return string_lookup.get;
63
+ if (s[0] == 'P' && s[1] == 'U' && s[2] == 'T') return string_lookup.put;
62
64
  break;
63
65
  case 4:
64
- if (s[0] == 'P' && s[1] == 'O' && s[2] == 'S' && s[3] == 'T') return rb_s_POST;
65
- if (s[0] == 'H' && s[1] == 'E' && s[2] == 'A' && s[3] == 'D') return rb_s_HEAD;
66
+ if (s[0] == 'P' && s[1] == 'O' && s[2] == 'S' && s[3] == 'T') return string_lookup.post;
67
+ if (s[0] == 'H' && s[1] == 'E' && s[2] == 'A' && s[3] == 'D') return string_lookup.head;
66
68
  break;
67
69
  case 5:
68
- if (s[0] == 'P' && s[1] == 'A' && s[2] == 'T' && s[3] == 'C' && s[4] == 'H') return rb_s_PATCH;
70
+ if (s[0] == 'P' && s[1] == 'A' && s[2] == 'T' && s[3] == 'C' && s[4] == 'H') return string_lookup.patch;
69
71
  break;
70
72
  case 6:
71
- if (s[0] == 'D' && s[1] == 'E' && s[2] == 'L' && s[3] == 'E' && s[4] == 'T' && s[5] == 'E') return rb_s_DELETE;
73
+ if (s[0] == 'D' && s[1] == 'E' && s[2] == 'L' && s[3] == 'E' && s[4] == 'T' && s[5] == 'E') return string_lookup.delete;
72
74
  break;
73
75
  case 7:
74
- if (s[0] == 'O' && s[1] == 'P' && s[2] == 'T' && s[3] == 'I' && s[4] == 'O' && s[5] == 'N' && s[6] == 'S') return rb_s_OPTIONS;
76
+ if (s[0] == 'O' && s[1] == 'P' && s[2] == 'T' && s[3] == 'I' && s[4] == 'O' && s[5] == 'N' && s[6] == 'S') return string_lookup.options;
75
77
  break;
76
78
  }
77
79
  return Qnil;
78
80
  }
79
81
 
82
+ static void string_lookup_mark(void *ptr) {
83
+ struct string_lookup_t *s = ptr;
84
+ rb_gc_mark_movable(s->http_host);
85
+ rb_gc_mark_movable(s->http_accept);
86
+ rb_gc_mark_movable(s->http_cookie);
87
+ rb_gc_mark_movable(s->http_referer);
88
+ rb_gc_mark_movable(s->http_user_agent);
89
+ rb_gc_mark_movable(s->http_connection);
90
+ rb_gc_mark_movable(s->content_type);
91
+ rb_gc_mark_movable(s->http_cache_control);
92
+ rb_gc_mark_movable(s->http_authorization);
93
+ rb_gc_mark_movable(s->content_length);
94
+ rb_gc_mark_movable(s->http_accept_encoding);
95
+ rb_gc_mark_movable(s->http_accept_language);
96
+ rb_gc_mark_movable(s->get);
97
+ rb_gc_mark_movable(s->put);
98
+ rb_gc_mark_movable(s->post);
99
+ rb_gc_mark_movable(s->head);
100
+ rb_gc_mark_movable(s->patch);
101
+ rb_gc_mark_movable(s->delete);
102
+ rb_gc_mark_movable(s->options);
103
+ }
104
+
105
+ static void string_lookup_compact(void *ptr) {
106
+ struct string_lookup_t *s = ptr;
107
+ s->http_host = rb_gc_location(s->http_host);
108
+ s->http_accept = rb_gc_location(s->http_accept);
109
+ s->http_cookie = rb_gc_location(s->http_cookie);
110
+ s->http_referer = rb_gc_location(s->http_referer);
111
+ s->http_user_agent = rb_gc_location(s->http_user_agent);
112
+ s->http_connection = rb_gc_location(s->http_connection);
113
+ s->content_type = rb_gc_location(s->content_type);
114
+ s->http_cache_control = rb_gc_location(s->http_cache_control);
115
+ s->http_authorization = rb_gc_location(s->http_authorization);
116
+ s->content_length = rb_gc_location(s->content_length);
117
+ s->http_accept_encoding = rb_gc_location(s->http_accept_encoding);
118
+ s->http_accept_language = rb_gc_location(s->http_accept_language);
119
+ s->get = rb_gc_location(s->get);
120
+ s->put = rb_gc_location(s->put);
121
+ s->post = rb_gc_location(s->post);
122
+ s->head = rb_gc_location(s->head);
123
+ s->patch = rb_gc_location(s->patch);
124
+ s->delete = rb_gc_location(s->delete);
125
+ s->options = rb_gc_location(s->options);
126
+ }
127
+
128
+ static const rb_data_type_t string_lookup_type = {
129
+ .wrap_struct_name = "picohttp_string_lookup",
130
+ .function = {
131
+ .dmark = string_lookup_mark,
132
+ .dfree = NULL,
133
+ .dsize = NULL,
134
+ .dcompact = string_lookup_compact,
135
+ },
136
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
137
+ };
138
+
139
+ static void intern_str(VALUE wrapper, VALUE *field, const char *str) {
140
+ RB_OBJ_WRITE(wrapper, field, rb_interned_str_cstr(str));
141
+ }
142
+
80
143
  static void init_string_lookup(void) {
81
- rb_gc_register_address(&rb_s_HTTP_HOST);
82
- rb_s_HTTP_HOST = rb_interned_str_cstr("HTTP_HOST");
83
- rb_gc_register_address(&rb_s_HTTP_ACCEPT);
84
- rb_s_HTTP_ACCEPT = rb_interned_str_cstr("HTTP_ACCEPT");
85
- rb_gc_register_address(&rb_s_HTTP_COOKIE);
86
- rb_s_HTTP_COOKIE = rb_interned_str_cstr("HTTP_COOKIE");
87
- rb_gc_register_address(&rb_s_HTTP_REFERER);
88
- rb_s_HTTP_REFERER = rb_interned_str_cstr("HTTP_REFERER");
89
- rb_gc_register_address(&rb_s_HTTP_USER_AGENT);
90
- rb_s_HTTP_USER_AGENT = rb_interned_str_cstr("HTTP_USER_AGENT");
91
- rb_gc_register_address(&rb_s_HTTP_CONNECTION);
92
- rb_s_HTTP_CONNECTION = rb_interned_str_cstr("HTTP_CONNECTION");
93
- rb_gc_register_address(&rb_s_CONTENT_TYPE);
94
- rb_s_CONTENT_TYPE = rb_interned_str_cstr("CONTENT_TYPE");
95
- rb_gc_register_address(&rb_s_HTTP_CACHE_CONTROL);
96
- rb_s_HTTP_CACHE_CONTROL = rb_interned_str_cstr("HTTP_CACHE_CONTROL");
97
- rb_gc_register_address(&rb_s_HTTP_AUTHORIZATION);
98
- rb_s_HTTP_AUTHORIZATION = rb_interned_str_cstr("HTTP_AUTHORIZATION");
99
- rb_gc_register_address(&rb_s_CONTENT_LENGTH);
100
- rb_s_CONTENT_LENGTH = rb_interned_str_cstr("CONTENT_LENGTH");
101
- rb_gc_register_address(&rb_s_HTTP_ACCEPT_ENCODING);
102
- rb_s_HTTP_ACCEPT_ENCODING = rb_interned_str_cstr("HTTP_ACCEPT_ENCODING");
103
- rb_gc_register_address(&rb_s_HTTP_ACCEPT_LANGUAGE);
104
- rb_s_HTTP_ACCEPT_LANGUAGE = rb_interned_str_cstr("HTTP_ACCEPT_LANGUAGE");
105
- rb_gc_register_address(&rb_s_GET);
106
- rb_s_GET = rb_interned_str_cstr("GET");
107
- rb_gc_register_address(&rb_s_PUT);
108
- rb_s_PUT = rb_interned_str_cstr("PUT");
109
- rb_gc_register_address(&rb_s_POST);
110
- rb_s_POST = rb_interned_str_cstr("POST");
111
- rb_gc_register_address(&rb_s_HEAD);
112
- rb_s_HEAD = rb_interned_str_cstr("HEAD");
113
- rb_gc_register_address(&rb_s_PATCH);
114
- rb_s_PATCH = rb_interned_str_cstr("PATCH");
115
- rb_gc_register_address(&rb_s_DELETE);
116
- rb_s_DELETE = rb_interned_str_cstr("DELETE");
117
- rb_gc_register_address(&rb_s_OPTIONS);
118
- rb_s_OPTIONS = rb_interned_str_cstr("OPTIONS");
144
+ VALUE wrapper = TypedData_Wrap_Struct(0, &string_lookup_type, &string_lookup);
145
+ rb_gc_register_mark_object(wrapper);
146
+ intern_str(wrapper, &string_lookup.http_host, "HTTP_HOST");
147
+ intern_str(wrapper, &string_lookup.http_accept, "HTTP_ACCEPT");
148
+ intern_str(wrapper, &string_lookup.http_cookie, "HTTP_COOKIE");
149
+ intern_str(wrapper, &string_lookup.http_referer, "HTTP_REFERER");
150
+ intern_str(wrapper, &string_lookup.http_user_agent, "HTTP_USER_AGENT");
151
+ intern_str(wrapper, &string_lookup.http_connection, "HTTP_CONNECTION");
152
+ intern_str(wrapper, &string_lookup.content_type, "CONTENT_TYPE");
153
+ intern_str(wrapper, &string_lookup.http_cache_control, "HTTP_CACHE_CONTROL");
154
+ intern_str(wrapper, &string_lookup.http_authorization, "HTTP_AUTHORIZATION");
155
+ intern_str(wrapper, &string_lookup.content_length, "CONTENT_LENGTH");
156
+ intern_str(wrapper, &string_lookup.http_accept_encoding, "HTTP_ACCEPT_ENCODING");
157
+ intern_str(wrapper, &string_lookup.http_accept_language, "HTTP_ACCEPT_LANGUAGE");
158
+ intern_str(wrapper, &string_lookup.get, "GET");
159
+ intern_str(wrapper, &string_lookup.put, "PUT");
160
+ intern_str(wrapper, &string_lookup.post, "POST");
161
+ intern_str(wrapper, &string_lookup.head, "HEAD");
162
+ intern_str(wrapper, &string_lookup.patch, "PATCH");
163
+ intern_str(wrapper, &string_lookup.delete, "DELETE");
164
+ intern_str(wrapper, &string_lookup.options, "OPTIONS");
119
165
  }
120
166
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Picohttp
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -15,9 +15,12 @@ class StringLookup
15
15
  end
16
16
 
17
17
  def generate
18
+ # Generate the struct to hold all strings
19
+ puts "static struct string_lookup_t {"
18
20
  @string_table.each do |v, name|
19
- puts "static VALUE #{name}; /* #{v.inspect} */"
21
+ puts " VALUE #{name}; /* #{v.inspect} */"
20
22
  end
23
+ puts "} string_lookup;"
21
24
  puts
22
25
 
23
26
  @functions.each do |code|
@@ -25,17 +28,54 @@ class StringLookup
25
28
  puts
26
29
  end
27
30
 
31
+ # Generate mark function
32
+ puts "static void string_lookup_mark(void *ptr) {"
33
+ puts " struct string_lookup_t *s = ptr;"
34
+ @string_table.each do |v, name|
35
+ puts " rb_gc_mark_movable(s->#{name});"
36
+ end
37
+ puts "}"
38
+ puts
39
+
40
+ # Generate compact function
41
+ puts "static void string_lookup_compact(void *ptr) {"
42
+ puts " struct string_lookup_t *s = ptr;"
43
+ @string_table.each do |v, name|
44
+ puts " s->#{name} = rb_gc_location(s->#{name});"
45
+ end
46
+ puts "}"
47
+ puts
48
+
49
+ # Generate TypedData type
50
+ puts "static const rb_data_type_t string_lookup_type = {"
51
+ puts " .wrap_struct_name = \"picohttp_string_lookup\","
52
+ puts " .function = {"
53
+ puts " .dmark = string_lookup_mark,"
54
+ puts " .dfree = NULL,"
55
+ puts " .dsize = NULL,"
56
+ puts " .dcompact = string_lookup_compact,"
57
+ puts " },"
58
+ puts " .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,"
59
+ puts "};"
60
+ puts
61
+
62
+ puts "static void intern_str(VALUE wrapper, VALUE *field, const char *str) {"
63
+ puts " RB_OBJ_WRITE(wrapper, field, rb_interned_str_cstr(str));"
64
+ puts "}"
65
+ puts
66
+
28
67
  puts "static void init_string_lookup(void) {"
68
+ puts " VALUE wrapper = TypedData_Wrap_Struct(0, &string_lookup_type, &string_lookup);"
69
+ puts " rb_gc_register_mark_object(wrapper);"
29
70
  @string_table.each do |v, name|
30
- puts " rb_gc_register_address(&#{name});"
31
- puts " #{name} = rb_interned_str_cstr(#{v.dump});"
71
+ puts " intern_str(wrapper, &string_lookup.#{name}, #{v.dump});"
32
72
  end
33
73
  puts "}"
34
74
  puts
35
75
  end
36
76
 
37
77
  def name_for_string(string)
38
- "rb_s_#{string.gsub(/[^a-z0-9]/i, "_")}"
78
+ string.gsub(/[^a-z0-9]/i, "_").downcase
39
79
  end
40
80
 
41
81
  def generate_comparison(var, string, ignore_case: false)
@@ -66,7 +106,7 @@ class StringLookup
66
106
  code << " case #{len}:"
67
107
  strs.each do |match, target|
68
108
  name = @string_table[target]
69
- code << " if (#{generate_comparison("s", match, ignore_case:)}) return #{name};"
109
+ code << " if (#{generate_comparison("s", match, ignore_case:)}) return string_lookup.#{name};"
70
110
  end
71
111
  code << " break;"
72
112
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: picohttp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Hawthorn
@@ -54,7 +54,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
56
  requirements: []
57
- rubygems_version: 4.0.2
57
+ rubygems_version: 4.0.3
58
58
  specification_version: 4
59
59
  summary: Fast HTTP request parser using picohttpparser
60
60
  test_files: []