picohttp 0.1.0 → 0.2.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 +4 -4
- data/ext/picohttp/extconf.rb +3 -0
- data/ext/picohttp/picohttp.c +65 -12
- data/ext/picohttp/string_lookup.inc +120 -0
- data/lib/picohttp/version.rb +1 -1
- data/tool/generate_lookup.rb +98 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3b357892a265429f3731c5cc68f56aaa3649b1f346a9c86f021bb67be2e601ac
|
|
4
|
+
data.tar.gz: 7a499b9fe596ee583c238956d66f05f3ff6caeabd0594a5db1d0f3c1b6d3e023
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 704efdbcbd2daab576977d4f2857307a7de44a41b5785cc6d215886777b7d97e3e3ba257670e40afd4c0b49f05024c8e1b26f1a8a41864e5b70393736a26fe6a
|
|
7
|
+
data.tar.gz: 7318f991db2981ed09349edfd907141342121fa65f3a45fa0e51dc2503b973b1c83681356eacad29fd1f95c44af130ad1eb0952e52ae04e4a139678bf172d016
|
data/ext/picohttp/extconf.rb
CHANGED
data/ext/picohttp/picohttp.c
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
#include "picohttpparser.h"
|
|
3
3
|
|
|
4
4
|
#define MAX_HEADER_NAME_LEN 256
|
|
5
|
+
#define MAX_HEADERS 100
|
|
5
6
|
|
|
6
7
|
VALUE rb_mPicohttp;
|
|
7
8
|
VALUE rb_ePicohttpParseError;
|
|
@@ -12,6 +13,30 @@ static VALUE rb_str_server_protocol;
|
|
|
12
13
|
static VALUE rb_str_path_info;
|
|
13
14
|
static VALUE rb_str_query_string;
|
|
14
15
|
static VALUE rb_str_empty;
|
|
16
|
+
static VALUE rb_str_http_1_0;
|
|
17
|
+
static VALUE rb_str_http_1_1;
|
|
18
|
+
|
|
19
|
+
#include "string_lookup.inc"
|
|
20
|
+
|
|
21
|
+
static VALUE
|
|
22
|
+
http_version_string(int minor_version)
|
|
23
|
+
{
|
|
24
|
+
if (minor_version == 0) {
|
|
25
|
+
return rb_str_http_1_0;
|
|
26
|
+
} else if (minor_version == 1) {
|
|
27
|
+
return rb_str_http_1_1;
|
|
28
|
+
} else {
|
|
29
|
+
return rb_sprintf("HTTP/1.%d", minor_version);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static VALUE
|
|
34
|
+
http_method_string(const char *method, size_t method_len)
|
|
35
|
+
{
|
|
36
|
+
VALUE str = lookup_method(method, method_len);
|
|
37
|
+
if (str == Qnil) str = rb_str_new(method, method_len);
|
|
38
|
+
return str;
|
|
39
|
+
}
|
|
15
40
|
|
|
16
41
|
static VALUE
|
|
17
42
|
header_name_to_env_key(const char *name, size_t name_len)
|
|
@@ -20,6 +45,11 @@ header_name_to_env_key(const char *name, size_t name_len)
|
|
|
20
45
|
rb_raise(rb_ePicohttpParseError, "Header name too long");
|
|
21
46
|
}
|
|
22
47
|
|
|
48
|
+
VALUE str = lookup_header(name, name_len);
|
|
49
|
+
if (str != Qnil) {
|
|
50
|
+
return str;
|
|
51
|
+
}
|
|
52
|
+
|
|
23
53
|
char env_name[MAX_HEADER_NAME_LEN + 6]; // "HTTP_" + name + null terminator
|
|
24
54
|
strcpy(env_name, "HTTP_");
|
|
25
55
|
|
|
@@ -48,7 +78,7 @@ picohttp_parse_request(VALUE self, VALUE str)
|
|
|
48
78
|
|
|
49
79
|
const char *method, *path;
|
|
50
80
|
int minor_version;
|
|
51
|
-
struct phr_header headers[
|
|
81
|
+
struct phr_header headers[MAX_HEADERS];
|
|
52
82
|
size_t method_len, path_len, num_headers = sizeof(headers) / sizeof(headers[0]);
|
|
53
83
|
|
|
54
84
|
int result = phr_parse_request(buf, len, &method, &method_len, &path, &path_len,
|
|
@@ -89,7 +119,7 @@ picohttp_parse_request_env(VALUE self, VALUE str)
|
|
|
89
119
|
|
|
90
120
|
const char *method, *path;
|
|
91
121
|
int minor_version;
|
|
92
|
-
struct phr_header headers[
|
|
122
|
+
struct phr_header headers[MAX_HEADERS];
|
|
93
123
|
size_t method_len, path_len, num_headers = sizeof(headers) / sizeof(headers[0]);
|
|
94
124
|
|
|
95
125
|
int result = phr_parse_request(buf, len, &method, &method_len, &path, &path_len,
|
|
@@ -102,22 +132,33 @@ picohttp_parse_request_env(VALUE self, VALUE str)
|
|
|
102
132
|
rb_raise(rb_ePicohttpParseError, "Invalid HTTP request");
|
|
103
133
|
}
|
|
104
134
|
|
|
105
|
-
VALUE
|
|
135
|
+
VALUE header_values[MAX_HEADERS * 2];
|
|
136
|
+
int idx = 0;
|
|
106
137
|
|
|
107
138
|
// Standard CGI/Rack environment variables
|
|
108
|
-
|
|
109
|
-
|
|
139
|
+
header_values[idx++] = rb_str_request_method;
|
|
140
|
+
header_values[idx++] = http_method_string(method, method_len);
|
|
141
|
+
|
|
142
|
+
header_values[idx++] = rb_str_server_protocol;
|
|
143
|
+
header_values[idx++] = http_version_string(minor_version);
|
|
110
144
|
|
|
111
145
|
// Parse path and query string in C
|
|
112
146
|
const char *query_start = memchr(path, '?', path_len);
|
|
113
147
|
if (query_start) {
|
|
114
148
|
size_t path_info_len = query_start - path;
|
|
115
149
|
size_t query_len = path_len - path_info_len - 1;
|
|
116
|
-
|
|
117
|
-
|
|
150
|
+
|
|
151
|
+
header_values[idx++] = rb_str_path_info;
|
|
152
|
+
header_values[idx++] = rb_str_new(path, path_info_len);
|
|
153
|
+
|
|
154
|
+
header_values[idx++] = rb_str_query_string;
|
|
155
|
+
header_values[idx++] = rb_str_new(query_start + 1, query_len);
|
|
118
156
|
} else {
|
|
119
|
-
|
|
120
|
-
|
|
157
|
+
header_values[idx++] = rb_str_path_info;
|
|
158
|
+
header_values[idx++] = rb_str_new(path, path_len);
|
|
159
|
+
|
|
160
|
+
header_values[idx++] = rb_str_query_string;
|
|
161
|
+
header_values[idx++] = rb_str_empty;
|
|
121
162
|
}
|
|
122
163
|
|
|
123
164
|
// Convert headers to HTTP_ prefixed environment variables
|
|
@@ -126,11 +167,18 @@ picohttp_parse_request_env(VALUE self, VALUE str)
|
|
|
126
167
|
rb_raise(rb_ePicohttpParseError, "HTTP line folding not supported");
|
|
127
168
|
}
|
|
128
169
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
rb_hash_aset(env, header_name, header_value);
|
|
170
|
+
header_values[idx++] = header_name_to_env_key(headers[i].name, headers[i].name_len);
|
|
171
|
+
header_values[idx++] = rb_str_new(headers[i].value, headers[i].value_len);
|
|
132
172
|
}
|
|
133
173
|
|
|
174
|
+
#ifdef HAVE_RB_HASH_NEW_CAPA
|
|
175
|
+
VALUE env = rb_hash_new_capa(idx / 2);
|
|
176
|
+
#else
|
|
177
|
+
VALUE env = rb_hash_new();
|
|
178
|
+
#endif
|
|
179
|
+
|
|
180
|
+
rb_hash_bulk_insert(idx, header_values, env);
|
|
181
|
+
|
|
134
182
|
return env;
|
|
135
183
|
}
|
|
136
184
|
|
|
@@ -147,11 +195,14 @@ Init_picohttp(void)
|
|
|
147
195
|
rb_define_module_function(rb_mPicohttp, "parse_request_env", picohttp_parse_request_env, 1);
|
|
148
196
|
|
|
149
197
|
// Initialize interned string constants
|
|
198
|
+
init_string_lookup();
|
|
150
199
|
rb_str_request_method = rb_interned_str_cstr("REQUEST_METHOD");
|
|
151
200
|
rb_str_server_protocol = rb_interned_str_cstr("SERVER_PROTOCOL");
|
|
152
201
|
rb_str_path_info = rb_interned_str_cstr("PATH_INFO");
|
|
153
202
|
rb_str_query_string = rb_interned_str_cstr("QUERY_STRING");
|
|
154
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");
|
|
155
206
|
|
|
156
207
|
// Prevent garbage collection of constants
|
|
157
208
|
rb_gc_register_address(&rb_str_request_method);
|
|
@@ -159,4 +210,6 @@ Init_picohttp(void)
|
|
|
159
210
|
rb_gc_register_address(&rb_str_path_info);
|
|
160
211
|
rb_gc_register_address(&rb_str_query_string);
|
|
161
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);
|
|
162
215
|
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/* This file is auto-generated by tool/generate_lookup.rb */
|
|
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" */
|
|
22
|
+
|
|
23
|
+
static VALUE lookup_header(const char *s, size_t len) {
|
|
24
|
+
switch (len) {
|
|
25
|
+
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;
|
|
27
|
+
break;
|
|
28
|
+
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
|
+
break;
|
|
32
|
+
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;
|
|
34
|
+
break;
|
|
35
|
+
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
|
+
break;
|
|
39
|
+
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;
|
|
41
|
+
break;
|
|
42
|
+
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
|
+
break;
|
|
46
|
+
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;
|
|
48
|
+
break;
|
|
49
|
+
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
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
return Qnil;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static VALUE lookup_method(const char *s, size_t len) {
|
|
58
|
+
switch (len) {
|
|
59
|
+
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
|
+
break;
|
|
63
|
+
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
|
+
break;
|
|
67
|
+
case 5:
|
|
68
|
+
if (s[0] == 'P' && s[1] == 'A' && s[2] == 'T' && s[3] == 'C' && s[4] == 'H') return rb_s_PATCH;
|
|
69
|
+
break;
|
|
70
|
+
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;
|
|
72
|
+
break;
|
|
73
|
+
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;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
return Qnil;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
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");
|
|
119
|
+
}
|
|
120
|
+
|
data/lib/picohttp/version.rb
CHANGED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Generates fast C lookup code for known string sets
|
|
5
|
+
|
|
6
|
+
puts "/* This file is auto-generated by #{__FILE__} */"
|
|
7
|
+
puts
|
|
8
|
+
|
|
9
|
+
class StringLookup
|
|
10
|
+
def initialize
|
|
11
|
+
@string_table = Hash.new do |h, k|
|
|
12
|
+
h[k] = name_for_string(k)
|
|
13
|
+
end
|
|
14
|
+
@functions = []
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def generate
|
|
18
|
+
@string_table.each do |v, name|
|
|
19
|
+
puts "static VALUE #{name}; /* #{v.inspect} */"
|
|
20
|
+
end
|
|
21
|
+
puts
|
|
22
|
+
|
|
23
|
+
@functions.each do |code|
|
|
24
|
+
puts code
|
|
25
|
+
puts
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
puts "static void init_string_lookup(void) {"
|
|
29
|
+
@string_table.each do |v, name|
|
|
30
|
+
puts " rb_gc_register_address(&#{name});"
|
|
31
|
+
puts " #{name} = rb_interned_str_cstr(#{v.dump});"
|
|
32
|
+
end
|
|
33
|
+
puts "}"
|
|
34
|
+
puts
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def name_for_string(string)
|
|
38
|
+
"rb_s_#{string.gsub(/[^a-z0-9]/i, "_")}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def generate_comparison(var, string, ignore_case: false)
|
|
42
|
+
0.upto(string.length - 1).map do |i|
|
|
43
|
+
if ignore_case
|
|
44
|
+
"(#{var}[#{i}] | 32) == '#{string[i].downcase}'"
|
|
45
|
+
else
|
|
46
|
+
"#{var}[#{i}] == '#{string[i]}'"
|
|
47
|
+
end
|
|
48
|
+
end.join(" && ")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def add_function(function, mapping, ignore_case: false)
|
|
52
|
+
if mapping.is_a?(Array)
|
|
53
|
+
mapping = mapping.map do |str|
|
|
54
|
+
[str, str]
|
|
55
|
+
end.to_h
|
|
56
|
+
end
|
|
57
|
+
by_length = mapping.group_by do |(k,v)|
|
|
58
|
+
k.length
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
code = []
|
|
62
|
+
code << "static VALUE lookup_#{function}(const char *s, size_t len) {"
|
|
63
|
+
code << " switch (len) {"
|
|
64
|
+
|
|
65
|
+
by_length.sort.each do |len, strs|
|
|
66
|
+
code << " case #{len}:"
|
|
67
|
+
strs.each do |match, target|
|
|
68
|
+
name = @string_table[target]
|
|
69
|
+
code << " if (#{generate_comparison("s", match, ignore_case:)}) return #{name};"
|
|
70
|
+
end
|
|
71
|
+
code << " break;"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
code << " }"
|
|
75
|
+
code << " return Qnil;"
|
|
76
|
+
code << "}"
|
|
77
|
+
|
|
78
|
+
@functions << code.join("\n")
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
HEADERS = %w[Content-Type Content-Length Host User-Agent Accept Accept-Encoding
|
|
83
|
+
Accept-Language Connection Cache-Control Cookie Authorization Referer]
|
|
84
|
+
|
|
85
|
+
METHODS = %w[GET POST PUT DELETE HEAD OPTIONS PATCH]
|
|
86
|
+
|
|
87
|
+
lookup = StringLookup.new
|
|
88
|
+
header_mapping = HEADERS.map do |header|
|
|
89
|
+
header = header.downcase
|
|
90
|
+
rack_key = header.upcase.gsub("-", "_")
|
|
91
|
+
unless header == "content-type" || header == "content-length"
|
|
92
|
+
rack_key = "HTTP_#{rack_key}"
|
|
93
|
+
end
|
|
94
|
+
[header, rack_key]
|
|
95
|
+
end.to_h
|
|
96
|
+
lookup.add_function("header", header_mapping, ignore_case: true)
|
|
97
|
+
lookup.add_function("method", METHODS)
|
|
98
|
+
lookup.generate
|
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.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- John Hawthorn
|
|
@@ -27,8 +27,10 @@ files:
|
|
|
27
27
|
- ext/picohttp/picohttp.h
|
|
28
28
|
- ext/picohttp/picohttpparser.c
|
|
29
29
|
- ext/picohttp/picohttpparser.h
|
|
30
|
+
- ext/picohttp/string_lookup.inc
|
|
30
31
|
- lib/picohttp.rb
|
|
31
32
|
- lib/picohttp/version.rb
|
|
33
|
+
- tool/generate_lookup.rb
|
|
32
34
|
homepage: https://github.com/jhawthorn/picohttp
|
|
33
35
|
licenses:
|
|
34
36
|
- MIT
|
|
@@ -52,7 +54,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
52
54
|
- !ruby/object:Gem::Version
|
|
53
55
|
version: '0'
|
|
54
56
|
requirements: []
|
|
55
|
-
rubygems_version:
|
|
57
|
+
rubygems_version: 4.0.2
|
|
56
58
|
specification_version: 4
|
|
57
59
|
summary: Fast HTTP request parser using picohttpparser
|
|
58
60
|
test_files: []
|