pico_http_parser 0.0.2 → 0.0.3

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
  SHA1:
3
- metadata.gz: 77d9ce494785aca207b88dd3bdc5340d94cac9ad
4
- data.tar.gz: 9ee980d7dd5d1c5f5c2467603dffc0444513fb71
3
+ metadata.gz: 417f57e6c7289be7606e9fdda1b65346751f3c10
4
+ data.tar.gz: 420957b9c7d8ed9338c29d20030109ea0f6c17c0
5
5
  SHA512:
6
- metadata.gz: 7fb958f767c35d5d71234f729ad74e346685329072122b612766787ff000e4ff21c2d12b42b5e73f6096ed63682f570b56fb7ae9f1defed42b8c1fb87d9935d5
7
- data.tar.gz: 7dfa7423fda76a94ea59df99a0e6d15b59ba61620d3e4575269508a1a3411de1e064bb0b0d99bd7d03f2e95f02e65eb0c45783cbf8c24ac570f8f3ae3d118cf7
6
+ metadata.gz: 1cd7184656ea42efb5f22aa27a42d30ed381f241c665859fbc9e94937195248db6216a4be25903799841148c51842fd2fa93bf0ddf39b699cb48192eb44d0e1a
7
+ data.tar.gz: 8656caa1f569d72acc097392e67c9316e26c7aa5546d165d1d600566e80a6e1057660696e8096fb00db9a6b7a88860fa890d8a801dbdbedf9f877c4f06e51f62
data/README.md CHANGED
@@ -54,16 +54,17 @@ given request is incomplete
54
54
  On my Macbook Air
55
55
 
56
56
  ```
57
+ $ ruby benchmark.rb
57
58
  Calculating -------------------------------------
58
- PicoHTTPParser 13.988k i/100ms
59
- Unicorn's HttpParser 12.250k i/100ms
59
+ PicoHTTPParser 13.655k i/100ms
60
+ Unicorn's HttpParser 11.554k i/100ms
60
61
  -------------------------------------------------
61
- PicoHTTPParser 160.563k (± 4.3%) i/s - 811.304k
62
- Unicorn's HttpParser 135.976k (± 4.1%) i/s - 686.000k
62
+ PicoHTTPParser 155.331k (�� 3.7%) i/s - 778.335k
63
+ Unicorn's HttpParser 127.455k (�� 3.5%) i/s - 647.024k
63
64
 
64
65
  Comparison:
65
- PicoHTTPParser: 160562.7 i/s
66
- Unicorn's HttpParser: 135975.8 i/s - 1.18x slower
66
+ PicoHTTPParser: 155331.2 i/s
67
+ Unicorn's HttpParser: 127455.1 i/s - 1.22x slower
67
68
  ```
68
69
 
69
70
  ## SEE ALSO
@@ -2,15 +2,9 @@ require File.expand_path(File.dirname(__FILE__) + '/bench_helper')
2
2
 
3
3
  require 'pico_http_parser'
4
4
 
5
- request_body = <<REQ
6
- GET /blakjsdfkas HTTP/1.1\r
7
- Host: blooperblorp\r
8
- Cookie: blah=woop\r
9
- \r
10
- REQ
11
- loop = 300000
12
-
13
- #File.read(File.expand_path(File.dirname(__FILE__) + '/sample_request.http'))
5
+ # request_body = "GET /foo/bar/baz.html?key=value HTTP/1.0\r\nHost: blooperblorp\r\n\r\n"
6
+ request_body = "GET /foo/bar/baz.html?key=value HTTP/1.0\r\nHost: blooperblorp\r\nCookie: foobar\r\nX-Forwared-For: 127.0.0.1\r\n\r\n"
7
+ # request_body = "GET /foo/bar/baz.html?key=value HTTP/1.0\r\n\r\n"
14
8
 
15
9
  Benchmark.ips do |x|
16
10
  x.time = 5
@@ -8,6 +8,58 @@
8
8
 
9
9
  VALUE cPicoHTTPParser;
10
10
 
11
+ static VALUE request_method_key;
12
+ static VALUE request_uri_key;
13
+ static VALUE script_name_key;
14
+ static VALUE server_protocol_key;
15
+ static VALUE query_string_key;
16
+
17
+ struct common_header {
18
+ const char * name;
19
+ size_t name_len;
20
+ VALUE key;
21
+ };
22
+ static int common_headers_num = 0;
23
+ static struct common_header common_headers[20];
24
+
25
+ static
26
+ void set_common_header(const char * key, int key_len, const int raw)
27
+ {
28
+ char tmp[MAX_HEADER_NAME_LEN + sizeof("HTTP_") - 1];
29
+ const char* name;
30
+ size_t name_len;
31
+ const char * s;
32
+ char* d;
33
+ size_t n;
34
+ VALUE env_key;
35
+
36
+ if ( raw == 1) {
37
+ for (s = key, n = key_len, d = tmp;
38
+ n != 0;
39
+ s++, --n, d++) {
40
+ *d = *s == '-' ? '_' : TOU(*s);
41
+ name = tmp;
42
+ name_len = key_len;
43
+ }
44
+ } else {
45
+ strcpy(tmp, "HTTP_");
46
+ for (s = key, n = key_len, d = tmp + 5;
47
+ n != 0;
48
+ s++, --n, d++) {
49
+ *d = *s == '-' ? '_' : TOU(*s);
50
+ name = tmp;
51
+ name_len = key_len + 5;
52
+ }
53
+ }
54
+ env_key = rb_obj_freeze(rb_str_new(name,name_len));
55
+ common_headers[common_headers_num].name = key;
56
+ common_headers[common_headers_num].name_len = key_len;
57
+ common_headers[common_headers_num].key = env_key;
58
+ rb_gc_register_address(&common_headers[common_headers_num].key);
59
+ common_headers_num++;
60
+ }
61
+
62
+
11
63
  static
12
64
  size_t find_ch(const char* s, size_t len, char ch)
13
65
  {
@@ -31,6 +83,16 @@ int header_is(const struct phr_header* header, const char* name,
31
83
  return 1;
32
84
  }
33
85
 
86
+ static
87
+ VALUE find_common_header(const struct phr_header* header) {
88
+ int i;
89
+ for ( i = 0; i < common_headers_num; i++ ) {
90
+ if ( header_is(header, common_headers[i].name, common_headers[i].name_len) ) {
91
+ return common_headers[i].key;
92
+ }
93
+ }
94
+ return Qnil;
95
+ }
34
96
 
35
97
  static
36
98
  int store_path_info(VALUE envref, const char* src, size_t src_len) {
@@ -93,11 +155,12 @@ VALUE phr_parse_http_request(VALUE self, VALUE buf, VALUE envref)
93
155
  if (ret < 0)
94
156
  goto done;
95
157
 
96
- rb_hash_aset(envref, rb_str_new2("REQUEST_METHOD"), rb_str_new(method,method_len));
97
- rb_hash_aset(envref, rb_str_new2("REQUEST_URI"), rb_str_new(path, path_len));
98
- rb_hash_aset(envref, rb_str_new2("SCRIPT_NAME"), rb_str_new2(""));
99
- i = sprintf(tmp,"HTTP/1.%d",minor_version);
100
- rb_hash_aset(envref, rb_str_new2("SERVER_PROTOCOL"), rb_str_new(tmp, i));
158
+ rb_hash_aset(envref, request_method_key, rb_str_new(method,method_len));
159
+ rb_hash_aset(envref, request_uri_key, rb_str_new(path, path_len));
160
+ rb_hash_aset(envref, script_name_key, rb_str_new2(""));
161
+ strcpy(tmp, "HTTP/1.");
162
+ tmp[7] = 48 + ((minor_version > 1 || minor_version < 0 ) ? 0 : minor_version);
163
+ rb_hash_aset(envref, server_protocol_key, rb_str_new(tmp, sizeof("HTTP/1.0") - 1));
101
164
 
102
165
  /* PATH_INFO QUERY_STRING */
103
166
  path_len = find_ch(path, path_len, '#'); /* strip off all text after # after storing request_uri */
@@ -108,7 +171,7 @@ VALUE phr_parse_http_request(VALUE self, VALUE buf, VALUE envref)
108
171
  goto done;
109
172
  }
110
173
  if (question_at != path_len) ++question_at;
111
- rb_hash_aset(envref, rb_str_new2("QUERY_STRING"), rb_str_new(path + question_at, path_len - question_at));
174
+ rb_hash_aset(envref, query_string_key, rb_str_new(path + question_at, path_len - question_at));
112
175
 
113
176
  last_value = Qnil;
114
177
  for (i = 0; i < num_headers; ++i) {
@@ -116,13 +179,9 @@ VALUE phr_parse_http_request(VALUE self, VALUE buf, VALUE envref)
116
179
  const char* name;
117
180
  size_t name_len;
118
181
  VALUE slot;
119
- if (header_is(headers + i, "CONTENT-TYPE", sizeof("CONTENT-TYPE") - 1)) {
120
- name = "CONTENT_TYPE";
121
- name_len = sizeof("CONTENT_TYPE") - 1;
122
- } else if (header_is(headers + i, "CONTENT-LENGTH", sizeof("CONTENT-LENGTH") - 1)) {
123
- name = "CONTENT_LENGTH";
124
- name_len = sizeof("CONTENT_LENGTH") - 1;
125
- } else {
182
+ VALUE env_key;
183
+ env_key = find_common_header(headers + i);
184
+ if ( env_key == Qnil ) {
126
185
  const char* s;
127
186
  char* d;
128
187
  size_t n;
@@ -138,20 +197,22 @@ VALUE phr_parse_http_request(VALUE self, VALUE buf, VALUE envref)
138
197
  *d = *s == '-' ? '_' : TOU(*s);
139
198
  name = tmp;
140
199
  name_len = headers[i].name_len + 5;
200
+ env_key = rb_str_new(name, name_len);
141
201
  }
142
202
  }
143
- slot = rb_hash_aref(envref, rb_str_new(name, name_len));
203
+ slot = rb_hash_aref(envref, env_key);
144
204
  if ( slot != Qnil ) {
145
205
  rb_str_cat2(slot, ", ");
146
206
  rb_str_cat(slot, headers[i].value, headers[i].value_len);
147
207
  } else {
148
208
  slot = rb_str_new(headers[i].value, headers[i].value_len);
149
- rb_hash_aset(envref, rb_str_new(name, name_len), slot);
209
+ rb_hash_aset(envref, env_key, slot);
150
210
  last_value = slot;
151
211
  }
152
212
  } else {
153
213
  /* continuing lines of a mulitiline header */
154
- rb_str_cat(last_value, headers[i].value, headers[i].value_len);
214
+ if ( last_value != Qnil )
215
+ rb_str_cat(last_value, headers[i].value, headers[i].value_len);
155
216
  }
156
217
  }
157
218
 
@@ -161,6 +222,30 @@ VALUE phr_parse_http_request(VALUE self, VALUE buf, VALUE envref)
161
222
 
162
223
  void Init_pico_http_parser()
163
224
  {
225
+ request_method_key = rb_obj_freeze(rb_str_new2("REQUEST_METHOD"));
226
+ rb_gc_register_address(&request_method_key);
227
+ request_uri_key = rb_obj_freeze(rb_str_new2("REQUEST_URI"));
228
+ rb_gc_register_address(&request_uri_key);
229
+ script_name_key = rb_obj_freeze(rb_str_new2("SCRIPT_NAME"));
230
+ rb_gc_register_address(&script_name_key);
231
+ server_protocol_key = rb_obj_freeze(rb_str_new2("SERVER_PROTOCOL"));
232
+ rb_gc_register_address(&server_protocol_key);
233
+ query_string_key = rb_obj_freeze(rb_str_new2("QUERY_STRING"));
234
+ rb_gc_register_address(&query_string_key);
235
+
236
+ set_common_header("ACCEPT",sizeof("ACCEPT") - 1, 0);
237
+ set_common_header("ACCEPT-ENCODING",sizeof("ACCEPT-ENCODING") - 1, 0);
238
+ set_common_header("ACCEPT-LANGUAGE",sizeof("ACCEPT-LANGUAGE") - 1, 0);
239
+ set_common_header("CACHE-CONTROL",sizeof("CACHE-CONTROL") - 1, 0);
240
+ set_common_header("CONNECTION",sizeof("CONNECTION") - 1, 0);
241
+ set_common_header("CONTENT-LENGTH",sizeof("CONTENT-LENGTH") - 1, 1);
242
+ set_common_header("CONTENT-TYPE",sizeof("CONTENT-TYPE") - 1, 1);
243
+ set_common_header("COOKIE",sizeof("COOKIE") - 1, 0);
244
+ set_common_header("HOST",sizeof("HOST") - 1, 0);
245
+ set_common_header("IF-MODIFIED-SINCE",sizeof("IF-MODIFIED-SINCE") - 1, 0);
246
+ set_common_header("REFERER",sizeof("REFERER") - 1, 0);
247
+ set_common_header("USER-AGENT",sizeof("USER-AGENT") - 1, 0);
248
+ set_common_header("X-FORWARDED-FOR",sizeof("X-FORWARDED-FOR") - 1, 0);
164
249
 
165
250
  cPicoHTTPParser = rb_const_get(rb_cObject, rb_intern("PicoHTTPParser"));
166
251
  rb_define_module_function(cPicoHTTPParser, "parse_http_request", phr_parse_http_request, 2);
@@ -1,5 +1,6 @@
1
1
  #
2
- # Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase
2
+ # Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
3
+ # Shigeo Mitsunari
3
4
  #
4
5
  # The software is licensed under either the MIT License (below) or the Perl
5
6
  # license.
@@ -1,7 +1,7 @@
1
1
  PicoHTTPParser
2
2
  =============
3
3
 
4
- Copyright (c) 2009-2014 [Kazuho Oku](https://github.com/kazuho), [Tokuhiro Matsuno](https://github.com/tokuhirom), [Daisuke Murase](https://github.com/typester)
4
+ Copyright (c) 2009-2014 [Kazuho Oku](https://github.com/kazuho), [Tokuhiro Matsuno](https://github.com/tokuhirom), [Daisuke Murase](https://github.com/typester), [Shigeo Mitsunari](https://github.com/herumi)
5
5
 
6
6
  PicoHTTPParser is a tiny, primitive, fast HTTP request/response parser.
7
7
 
@@ -17,8 +17,8 @@ The software is dual-licensed under the Perl License or the MIT License.
17
17
  Benchmark
18
18
  ---------
19
19
 
20
- ![benchmark results](http://i.gyazo.com/7e098703c29128d69d02c9a216bfb6fb.png)
20
+ ![benchmark results](http://i.gyazo.com/a85c18d3162dfb46b485bb41e0ad443a.png)
21
21
 
22
- The benchmark code is from [fukamachi/fast-http](https://github.com/fukamachi/fast-http/).
22
+ The benchmark code is from [fukamachi/fast-http@6b91103](https://github.com/fukamachi/fast-http/tree/6b9110347c7a3407310c08979aefd65078518478).
23
23
 
24
24
  The internals of picohttpparser has been described to some extent in [my blog entry]( http://blog.kazuhooku.com/2014/11/the-internals-h2o-or-how-to-write-fast.html).
@@ -1,5 +1,6 @@
1
1
  /*
2
- * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase
2
+ * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
3
+ * Shigeo Mitsunari
3
4
  *
4
5
  * The software is licensed under either the MIT License (below) or the Perl
5
6
  * license.
@@ -40,7 +41,7 @@ int main(void)
40
41
  size_t num_headers;
41
42
  int i, ret;
42
43
 
43
- for (i = 0; i < 1000000; i++) {
44
+ for (i = 0; i < 10000000; i++) {
44
45
  num_headers = sizeof(headers) / sizeof(headers[0]);
45
46
  ret = phr_parse_request(REQ, sizeof(REQ) - 1, &method, &method_len, &path,
46
47
  &path_len, &minor_version, headers, &num_headers,
@@ -1,5 +1,6 @@
1
1
  /*
2
- * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase
2
+ * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
3
+ * Shigeo Mitsunari
3
4
  *
4
5
  * The software is licensed under either the MIT License (below) or the Perl
5
6
  * license.
@@ -24,6 +25,9 @@
24
25
  */
25
26
 
26
27
  #include <stddef.h>
28
+ #ifdef __SSE4_2__
29
+ # include <x86intrin.h>
30
+ #endif
27
31
  #include "picohttpparser.h"
28
32
 
29
33
  /* $Id$ */
@@ -53,8 +57,13 @@
53
57
 
54
58
  #define ADVANCE_TOKEN(tok, toklen) do { \
55
59
  const char* tok_start = buf; \
56
- for (; ; ++buf) { \
60
+ static const char ranges2[] __attribute__((aligned(16))) = "\000\040\177\177"; \
61
+ int found2; \
62
+ buf = findchar_fast(buf, buf_end, ranges2, sizeof(ranges2) - 1, &found2); \
63
+ if (! found2) { \
57
64
  CHECK_EOF(); \
65
+ } \
66
+ while (1) { \
58
67
  if (*buf == ' ') { \
59
68
  break; \
60
69
  } else if (unlikely(! IS_PRINTABLE_ASCII(*buf))) { \
@@ -63,6 +72,8 @@
63
72
  return NULL; \
64
73
  } \
65
74
  } \
75
+ ++buf; \
76
+ CHECK_EOF(); \
66
77
  } \
67
78
  tok = tok_start; \
68
79
  toklen = buf - tok_start; \
@@ -78,12 +89,50 @@ static const char* token_char_map =
78
89
  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
79
90
  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
80
91
 
92
+ static const char* findchar_fast(const char* buf, const char* buf_end, const char *ranges, size_t ranges_size, int* found)
93
+ {
94
+ *found = 0;
95
+ #if __SSE4_2__
96
+ if (likely(buf_end - buf >= 16)) {
97
+ __m128i ranges16 = _mm_loadu_si128((const __m128i*)ranges);
98
+
99
+ size_t left = (buf_end - buf) & ~15;
100
+ do {
101
+ __m128i b16 = _mm_loadu_si128((void*)buf);
102
+ int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS);
103
+ if (unlikely(r != 16)) {
104
+ buf += r;
105
+ *found = 1;
106
+ break;
107
+ }
108
+ buf += 16;
109
+ left -= 16;
110
+ } while (likely(left != 0));
111
+ }
112
+ #endif
113
+ return buf;
114
+ }
115
+
81
116
  static const char* get_token_to_eol(const char* buf, const char* buf_end,
82
117
  const char** token, size_t* token_len,
83
118
  int* ret)
84
119
  {
85
120
  const char* token_start = buf;
86
121
 
122
+ #ifdef __SSE4_2__
123
+ static const char ranges1[] =
124
+ "\0\010"
125
+ /* allow HT */
126
+ "\012\037"
127
+ /* allow SP and up to but not including DEL */
128
+ "\177\177"
129
+ /* allow chars w. MSB set */
130
+ ;
131
+ int found;
132
+ buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found);
133
+ if (found)
134
+ goto FOUND_CTL;
135
+ #else
87
136
  /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */
88
137
  while (likely(buf_end - buf >= 8)) {
89
138
  #define DOIT() if (unlikely(! IS_PRINTABLE_ASCII(*buf))) goto NonPrintable; ++buf
@@ -97,6 +146,7 @@ static const char* get_token_to_eol(const char* buf, const char* buf_end,
97
146
  }
98
147
  ++buf;
99
148
  }
149
+ #endif
100
150
  for (; ; ++buf) {
101
151
  CHECK_EOF();
102
152
  if (unlikely(! IS_PRINTABLE_ASCII(*buf))) {
@@ -211,14 +261,21 @@ static const char* parse_headers(const char* buf, const char* buf_end,
211
261
  /* parsing name, but do not discard SP before colon, see
212
262
  * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */
213
263
  headers[*num_headers].name = buf;
214
- for (; ; ++buf) {
264
+ static const char ranges1[] __attribute__((aligned(16))) = "::\x00\037";
265
+ int found;
266
+ buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found);
267
+ if (! found) {
215
268
  CHECK_EOF();
269
+ }
270
+ while (1) {
216
271
  if (*buf == ':') {
217
272
  break;
218
273
  } else if (*buf < ' ') {
219
274
  *ret = -1;
220
275
  return NULL;
221
276
  }
277
+ ++buf;
278
+ CHECK_EOF();
222
279
  }
223
280
  headers[*num_headers].name_len = buf - headers[*num_headers].name;
224
281
  ++buf;
@@ -1,5 +1,6 @@
1
1
  /*
2
- * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase
2
+ * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
3
+ * Shigeo Mitsunari
3
4
  *
4
5
  * The software is licensed under either the MIT License (below) or the Perl
5
6
  * license.
@@ -1,6 +1,7 @@
1
1
  /* use `make test` to run the test */
2
2
  /*
3
- * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase
3
+ * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
4
+ * Shigeo Mitsunari
4
5
  *
5
6
  * The software is licensed under either the MIT License (below) or the Perl
6
7
  * license.
@@ -97,6 +98,15 @@ static void test_request(void)
97
98
  ok(headers[2].name == NULL);
98
99
  ok(bufis(headers[2].value, headers[2].value_len, " \tc"));
99
100
 
101
+ PARSE("GET / HTTP/1.0\r\nfoo : ab\r\n\r\n", 0, 0,
102
+ "parse header name with trailing space");
103
+ ok(num_headers == 1);
104
+ ok(bufis(method, method_len, "GET"));
105
+ ok(bufis(path, path_len, "/"));
106
+ ok(minor_version == 0);
107
+ ok(bufis(headers[0].name, headers[0].name_len, "foo "));
108
+ ok(bufis(headers[0].value, headers[0].value_len, "ab"));
109
+
100
110
  PARSE("GET", 0, -2, "incomplete 1");
101
111
  ok(method == NULL);
102
112
  PARSE("GET ", 0, -2, "incomplete 2");
@@ -125,6 +135,8 @@ static void test_request(void)
125
135
  PARSE("GET /\x7fhello HTTP/1.0\r\n\r\n", 0, -1, "DEL in uri-path");
126
136
  PARSE("GET / HTTP/1.0\r\na\0b: c\r\n\r\n", 0, -1, "NUL in header name");
127
137
  PARSE("GET / HTTP/1.0\r\nab: c\0d\r\n\r\n", 0, -1, "NUL in header value");
138
+ PARSE("GET / HTTP/1.0\r\na\033b: c\r\n\r\n", 0, -1, "CTL in header name");
139
+ PARSE("GET / HTTP/1.0\r\nab: c\033\r\n\r\n", 0, -1, "CTL in header value");
128
140
  PARSE("GET /\xa0 HTTP/1.0\r\nh: c\xa2y\r\n\r\n", 0, 0, "accept MSB chars");
129
141
  ok(num_headers == 1);
130
142
  ok(bufis(method, method_len, "GET"));
@@ -1,3 +1,3 @@
1
1
  class PicoHTTPParser
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pico_http_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masahiro Nagano
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-21 00:00:00.000000000 Z
11
+ date: 2014-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler