http_parser.rb 0.6.0 → 0.7.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,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- MGMxN2Q5N2MzMTlmNjEyY2NhMmQzNjdjMDRiYmQxNGY4ZjIyMzM0Mg==
5
- data.tar.gz: !binary |-
6
- YmVhZTVjYmMxNGM3YjUwOWQ3NmUwYzEwZThlZWM5NDQ0ZGMwYTgwNA==
2
+ SHA256:
3
+ metadata.gz: afdb28464ccb0f753a1d66be02b667683d6aadfe31fe03a88f889252b457761d
4
+ data.tar.gz: 6041b3dc3f212160ae00b72b193e4620c5facd59096a28b122c0ef5a901dc80b
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- YWM3ZDZmMjdmYzQxMTZiN2VjZTMxYjczMjgxN2ZjYWY0YjE3ODllYjM5Nzg0
10
- OTUwOTk3ODYwZGJhNGIwMjA0Mjg1MTFkNDgxY2ZlMGIzZjQwNmQ1ODVkMjFj
11
- ZjQyYzVmNjlkYTQ0NDZkMzhhYjE1M2FhZTdiOTdlYzAxMzljOWY=
12
- data.tar.gz: !binary |-
13
- M2ZiYTcwYTg2ZDJjZDc2N2Y0NzhmMzE0MzExNTY5M2I3MzAxMTkzNzUyYzUz
14
- NGM2YzRmOGM3OGQ4NDNkMmIwYjM2MjYyMGJlNTU1MTVlNzFiYjgxZjJiYmNi
15
- Y2Q4N2YyZWFjODYwYzlhNGMwMzcxNTcwZjIwMWJjNDViNjQ1MTc=
6
+ metadata.gz: 41249c628f931cd9721e32bd378a44b8e49b25706b491ae3761d641e55bac1205afe9d3d02d88d5b4f3161adee4a65416c5eec2a51fcacad1b050d6f6d2637fc
7
+ data.tar.gz: ae5718390cce25d2ee7fcbaf923fac015189079b64ea2f37759c41ee2e7f2714dafaf6bf37ed6434f540e82e87c6a13723e226ac27000d1f0e2d7b2681643514
@@ -0,0 +1,23 @@
1
+ name: Testing on Ubuntu
2
+ on:
3
+ - push
4
+ - pull_request
5
+ jobs:
6
+ build:
7
+ runs-on: ${{ matrix.os }}
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ ruby: [ '2.6', '2.7', '3.0' ]
12
+ os: [ 'ubuntu-latest' ]
13
+ name: Ruby ${{ matrix.ruby }} unit testing on ${{ matrix.os }}
14
+ steps:
15
+ - uses: actions/checkout@v2
16
+ - uses: ruby/setup-ruby@v1
17
+ with:
18
+ ruby-version: ${{ matrix.ruby }}
19
+ - name: unit testing
20
+ run: |
21
+ gem install bundler rake
22
+ bundle install --jobs 4 --retry 3
23
+ bundle exec rake
@@ -0,0 +1,23 @@
1
+ name: Testing on Windows
2
+ on:
3
+ - push
4
+ - pull_request
5
+ jobs:
6
+ build:
7
+ runs-on: ${{ matrix.os }}
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ ruby: [ '2.6', '2.7', '3.0' ]
12
+ os: [ 'windows-latest' ]
13
+ name: Ruby ${{ matrix.ruby }} unit testing on ${{ matrix.os }}
14
+ steps:
15
+ - uses: actions/checkout@v2
16
+ - uses: ruby/setup-ruby@v1
17
+ with:
18
+ ruby-version: ${{ matrix.ruby }}
19
+ - name: unit testing
20
+ run: |
21
+ gem install bundler rake
22
+ bundle install --jobs 4 --retry 3
23
+ bundle exec rake
data/.gitignore CHANGED
@@ -1,11 +1,12 @@
1
- tmp
1
+ *.bundle
2
2
  *.bundle
3
3
  *.gem
4
+ *.jar
4
5
  *.o
6
+ *.rbc
5
7
  *.so
6
- *.bundle
7
- *.jar
8
8
  *.swp
9
+ Gemfile.lock
9
10
  Makefile
10
11
  tags
11
- *.rbc
12
+ tmp
data/.gitmodules CHANGED
@@ -1,6 +1,6 @@
1
1
  [submodule "http-parser"]
2
2
  path = ext/ruby_http_parser/vendor/http-parser
3
- url = git://github.com/joyent/http-parser.git
3
+ url = https://github.com/nodejs/http-parser.git
4
4
  [submodule "http-parser-java"]
5
5
  path = ext/ruby_http_parser/vendor/http-parser-java
6
- url = git://github.com/tmm1/http-parser.java
6
+ url = https://github.com/tmm1/http-parser.java
data/README.md CHANGED
@@ -3,13 +3,13 @@
3
3
  A simple callback-based HTTP request/response parser for writing http
4
4
  servers, clients and proxies.
5
5
 
6
- This gem is built on top of [joyent/http-parser](http://github.com/joyent/http-parser) and its java port [http-parser/http-parser.java](http://github.com/http-parser/http-parser.java).
6
+ This gem is built on top of [joyent/http-parser](https://github.com/joyent/http-parser) and its java port [http-parser/http-parser.java](https://github.com/http-parser/http-parser.java).
7
7
 
8
8
  ## Supported Platforms
9
9
 
10
10
  This gem aims to work on all major Ruby platforms, including:
11
11
 
12
- - MRI 1.8 and 1.9
12
+ - MRI 1.8, 1.9 and 2.0; should work on MRI 2.4+
13
13
  - Rubinius
14
14
  - JRuby
15
15
  - win32
data/Rakefile CHANGED
@@ -1,4 +1,5 @@
1
1
  # load tasks
2
+ require 'bundler/gem_tasks'
2
3
  Dir['tasks/*.rake'].sort.each { |f| load f }
3
4
 
4
5
  # default task
@@ -18,7 +18,7 @@ src_dir = File.expand_path('../', __FILE__)
18
18
  }
19
19
  end
20
20
 
21
- $CFLAGS << " -I#{src_dir}"
21
+ $CFLAGS << " -I\"#{src_dir}\""
22
22
 
23
23
  dir_config("ruby_http_parser")
24
24
  create_makefile("ruby_http_parser")
@@ -64,6 +64,7 @@ public class RubyHttpParser extends RubyObject {
64
64
  private IRubyObject on_body;
65
65
  private IRubyObject on_message_complete;
66
66
 
67
+ private IRubyObject status;
67
68
  private IRubyObject requestUrl;
68
69
  private IRubyObject requestPath;
69
70
  private IRubyObject queryString;
@@ -106,6 +107,18 @@ public class RubyHttpParser extends RubyObject {
106
107
  private void initSettings() {
107
108
  this.settings = new ParserSettings();
108
109
 
110
+ this.settings.on_status = new HTTPDataCallback() {
111
+ public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
112
+ byte[] data = fetchBytes(buf, pos, len);
113
+ if (runtime.is1_9() || runtime.is2_0()) {
114
+ ((RubyString) status).cat(data, 0, data.length, UTF8);
115
+ } else {
116
+ ((RubyString) status).cat(data);
117
+ }
118
+ return 0;
119
+ }
120
+ };
121
+
109
122
  this.settings.on_url = new HTTPDataCallback() {
110
123
  public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
111
124
  byte[] data = fetchBytes(buf, pos, len);
@@ -202,12 +215,14 @@ public class RubyHttpParser extends RubyObject {
202
215
  headers = new RubyHash(runtime);
203
216
 
204
217
  if (runtime.is1_9() || runtime.is2_0()) {
218
+ status = RubyString.newEmptyString(runtime, UTF8);
205
219
  requestUrl = RubyString.newEmptyString(runtime, UTF8);
206
220
  requestPath = RubyString.newEmptyString(runtime, UTF8);
207
221
  queryString = RubyString.newEmptyString(runtime, UTF8);
208
222
  fragment = RubyString.newEmptyString(runtime, UTF8);
209
223
  upgradeData = RubyString.newEmptyString(runtime, UTF8);
210
224
  } else {
225
+ status = RubyString.newEmptyString(runtime);
211
226
  requestUrl = RubyString.newEmptyString(runtime);
212
227
  requestPath = RubyString.newEmptyString(runtime);
213
228
  queryString = RubyString.newEmptyString(runtime);
@@ -309,7 +324,8 @@ public class RubyHttpParser extends RubyObject {
309
324
  this.parser = new HTTPParser();
310
325
  this.parser.HTTP_PARSER_STRICT = true;
311
326
  this.headers = null;
312
-
327
+
328
+ this.status = runtime.getNil();
313
329
  this.requestUrl = runtime.getNil();
314
330
  this.requestPath = runtime.getNil();
315
331
  this.queryString = runtime.getNil();
@@ -446,6 +462,11 @@ public class RubyHttpParser extends RubyObject {
446
462
  return headers == null ? runtime.getNil() : headers;
447
463
  }
448
464
 
465
+ @JRubyMethod(name = "status")
466
+ public IRubyObject getStatus() {
467
+ return status == null ? runtime.getNil() : status;
468
+ }
469
+
449
470
  @JRubyMethod(name = "request_url")
450
471
  public IRubyObject getRequestUrl() {
451
472
  return requestUrl == null ? runtime.getNil() : requestUrl;
@@ -16,6 +16,7 @@
16
16
  typedef struct ParserWrapper {
17
17
  ryah_http_parser parser;
18
18
 
19
+ VALUE status;
19
20
  VALUE request_url;
20
21
 
21
22
  VALUE headers;
@@ -45,6 +46,7 @@ void ParserWrapper_init(ParserWrapper *wrapper) {
45
46
  wrapper->parser.http_major = 0;
46
47
  wrapper->parser.http_minor = 0;
47
48
 
49
+ wrapper->status = Qnil;
48
50
  wrapper->request_url = Qnil;
49
51
 
50
52
  wrapper->upgrade_data = Qnil;
@@ -59,6 +61,7 @@ void ParserWrapper_init(ParserWrapper *wrapper) {
59
61
  void ParserWrapper_mark(void *data) {
60
62
  if(data) {
61
63
  ParserWrapper *wrapper = (ParserWrapper *) data;
64
+ rb_gc_mark_maybe(wrapper->status);
62
65
  rb_gc_mark_maybe(wrapper->request_url);
63
66
  rb_gc_mark_maybe(wrapper->upgrade_data);
64
67
  rb_gc_mark_maybe(wrapper->headers);
@@ -101,6 +104,7 @@ static VALUE Smixed;
101
104
  int on_message_begin(ryah_http_parser *parser) {
102
105
  GET_WRAPPER(wrapper, parser);
103
106
 
107
+ wrapper->status = rb_str_new2("");
104
108
  wrapper->request_url = rb_str_new2("");
105
109
  wrapper->headers = rb_hash_new();
106
110
  wrapper->upgrade_data = rb_str_new2("");
@@ -121,9 +125,28 @@ int on_message_begin(ryah_http_parser *parser) {
121
125
  }
122
126
  }
123
127
 
128
+ int on_status(ryah_http_parser *parser, const char *at, size_t length) {
129
+ GET_WRAPPER(wrapper, parser);
130
+
131
+ if (at && length) {
132
+ if (wrapper->status == Qnil) {
133
+ wrapper->status = rb_str_new(at, length);
134
+ } else {
135
+ rb_str_cat(wrapper->status, at, length);
136
+ }
137
+ }
138
+ return 0;
139
+ }
140
+
124
141
  int on_url(ryah_http_parser *parser, const char *at, size_t length) {
125
142
  GET_WRAPPER(wrapper, parser);
126
- rb_str_cat(wrapper->request_url, at, length);
143
+ if (at && length) {
144
+ if (wrapper->request_url == Qnil) {
145
+ wrapper->request_url = rb_str_new(at, length);
146
+ } else {
147
+ rb_str_cat(wrapper->request_url, at, length);
148
+ }
149
+ }
127
150
  return 0;
128
151
  }
129
152
 
@@ -136,7 +159,6 @@ int on_header_field(ryah_http_parser *parser, const char *at, size_t length) {
136
159
  } else {
137
160
  rb_str_cat(wrapper->curr_field_name, at, length);
138
161
  }
139
-
140
162
  return 0;
141
163
  }
142
164
 
@@ -248,6 +270,7 @@ int on_message_complete(ryah_http_parser *parser) {
248
270
 
249
271
  static ryah_http_parser_settings settings = {
250
272
  .on_message_begin = on_message_begin,
273
+ .on_status = on_status,
251
274
  .on_url = on_url,
252
275
  .on_header_field = on_header_field,
253
276
  .on_header_value = on_header_value,
@@ -320,11 +343,12 @@ VALUE Parser_execute(VALUE self, VALUE data) {
320
343
  size_t nparsed = ryah_http_parser_execute(&wrapper->parser, &settings, ptr, len);
321
344
 
322
345
  if (wrapper->parser.upgrade) {
323
- if (RTEST(wrapper->stopped))
346
+ if (RTEST(wrapper->stopped) && !RTEST(wrapper->completed))
324
347
  nparsed += 1;
325
348
 
326
- rb_str_cat(wrapper->upgrade_data, ptr + nparsed, len - nparsed);
327
-
349
+ if (nparsed < len)
350
+ rb_str_cat(wrapper->upgrade_data, ptr + nparsed, len - nparsed);
351
+
328
352
  } else if (nparsed != (size_t)len) {
329
353
  if (!RTEST(wrapper->stopped) && !RTEST(wrapper->completed))
330
354
  rb_raise(eParserError, "Could not parse data entirely (%zu != %zu)", nparsed, len);
@@ -438,6 +462,7 @@ VALUE Parser_status_code(VALUE self) {
438
462
  return wrapper->name; \
439
463
  }
440
464
 
465
+ DEFINE_GETTER(status);
441
466
  DEFINE_GETTER(request_url);
442
467
  DEFINE_GETTER(headers);
443
468
  DEFINE_GETTER(upgrade_data);
@@ -505,6 +530,7 @@ void Init_ruby_http_parser() {
505
530
  rb_define_method(cParser, "http_method", Parser_http_method, 0);
506
531
  rb_define_method(cParser, "status_code", Parser_status_code, 0);
507
532
 
533
+ rb_define_method(cParser, "status", Parser_status, 0);
508
534
  rb_define_method(cParser, "request_url", Parser_request_url, 0);
509
535
  rb_define_method(cParser, "headers", Parser_headers, 0);
510
536
  rb_define_method(cParser, "upgrade_data", Parser_upgrade_data, 0);
@@ -28,5 +28,41 @@ Andre Caron <andre.l.caron@gmail.com>
28
28
  Ivo Raisr <ivosh@ivosh.net>
29
29
  James McLaughlin <jamie@lacewing-project.org>
30
30
  David Gwynne <loki@animata.net>
31
- LE ROUX Thomas <thomas@procheo.fr>
31
+ Thomas LE ROUX <thomas@november-eleven.fr>
32
32
  Randy Rizun <rrizun@ortivawireless.com>
33
+ Andre Louis Caron <andre.louis.caron@usherbrooke.ca>
34
+ Simon Zimmermann <simonz05@gmail.com>
35
+ Erik Dubbelboer <erik@dubbelboer.com>
36
+ Martell Malone <martellmalone@gmail.com>
37
+ Bertrand Paquet <bpaquet@octo.com>
38
+ BogDan Vatra <bogdan@kde.org>
39
+ Peter Faiman <peter@thepicard.org>
40
+ Corey Richardson <corey@octayn.net>
41
+ Tóth Tamás <tomika_nospam@freemail.hu>
42
+ Cam Swords <cam.swords@gmail.com>
43
+ Chris Dickinson <christopher.s.dickinson@gmail.com>
44
+ Uli Köhler <ukoehler@btronik.de>
45
+ Charlie Somerville <charlie@charliesomerville.com>
46
+ Patrik Stutz <patrik.stutz@gmail.com>
47
+ Fedor Indutny <fedor.indutny@gmail.com>
48
+ runner <runner.mei@gmail.com>
49
+ Alexis Campailla <alexis@janeasystems.com>
50
+ David Wragg <david@wragg.org>
51
+ Vinnie Falco <vinnie.falco@gmail.com>
52
+ Alex Butum <alexbutum@linux.com>
53
+ Rex Feng <rexfeng@gmail.com>
54
+ Alex Kocharin <alex@kocharin.ru>
55
+ Mark Koopman <markmontymark@yahoo.com>
56
+ Helge Heß <me@helgehess.eu>
57
+ Alexis La Goutte <alexis.lagoutte@gmail.com>
58
+ George Miroshnykov <george.miroshnykov@gmail.com>
59
+ Maciej Małecki <me@mmalecki.com>
60
+ Marc O'Morain <github.com@marcomorain.com>
61
+ Jeff Pinner <jpinner@twitter.com>
62
+ Timothy J Fontaine <tjfontaine@gmail.com>
63
+ Akagi201 <akagi201@gmail.com>
64
+ Romain Giraud <giraud.romain@gmail.com>
65
+ Jay Satiro <raysatiro@yahoo.com>
66
+ Arne Steen <Arne.Steen@gmx.de>
67
+ Kjell Schubert <kjell.schubert@gmail.com>
68
+ Olivier Mengué <dolmen@cpan.org>
@@ -1,8 +1,4 @@
1
- http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright
2
- Igor Sysoev.
3
-
4
- Additional changes are licensed under the same terms as NGINX and
5
- copyright Joyent, Inc. and other Node contributors. All rights reserved.
1
+ Copyright Joyent, Inc. and other Node contributors.
6
2
 
7
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
8
4
  of this software and associated documentation files (the "Software"), to
@@ -1,6 +1,8 @@
1
1
  HTTP Parser
2
2
  ===========
3
3
 
4
+ [![Build Status](https://api.travis-ci.org/nodejs/http-parser.svg?branch=master)](https://travis-ci.org/nodejs/http-parser)
5
+
4
6
  This is a parser for HTTP messages written in C. It parses both requests and
5
7
  responses. The parser is designed to be used in performance HTTP
6
8
  applications. It does not make any syscalls nor allocations, it does not
@@ -34,43 +36,46 @@ Usage
34
36
  One `http_parser` object is used per TCP connection. Initialize the struct
35
37
  using `http_parser_init()` and set the callbacks. That might look something
36
38
  like this for a request parser:
39
+ ```c
40
+ http_parser_settings settings;
41
+ settings.on_url = my_url_callback;
42
+ settings.on_header_field = my_header_field_callback;
43
+ /* ... */
37
44
 
38
- http_parser_settings settings;
39
- settings.on_path = my_path_callback;
40
- settings.on_header_field = my_header_field_callback;
41
- /* ... */
42
-
43
- http_parser *parser = malloc(sizeof(http_parser));
44
- http_parser_init(parser, HTTP_REQUEST);
45
- parser->data = my_socket;
45
+ http_parser *parser = malloc(sizeof(http_parser));
46
+ http_parser_init(parser, HTTP_REQUEST);
47
+ parser->data = my_socket;
48
+ ```
46
49
 
47
50
  When data is received on the socket execute the parser and check for errors.
48
51
 
49
- size_t len = 80*1024, nparsed;
50
- char buf[len];
51
- ssize_t recved;
52
+ ```c
53
+ size_t len = 80*1024, nparsed;
54
+ char buf[len];
55
+ ssize_t recved;
52
56
 
53
- recved = recv(fd, buf, len, 0);
57
+ recved = recv(fd, buf, len, 0);
54
58
 
55
- if (recved < 0) {
56
- /* Handle error. */
57
- }
59
+ if (recved < 0) {
60
+ /* Handle error. */
61
+ }
58
62
 
59
- /* Start up / continue the parser.
60
- * Note we pass recved==0 to signal that EOF has been recieved.
61
- */
62
- nparsed = http_parser_execute(parser, &settings, buf, recved);
63
+ /* Start up / continue the parser.
64
+ * Note we pass recved==0 to signal that EOF has been received.
65
+ */
66
+ nparsed = http_parser_execute(parser, &settings, buf, recved);
63
67
 
64
- if (parser->upgrade) {
65
- /* handle new protocol */
66
- } else if (nparsed != recved) {
67
- /* Handle error. Usually just close the connection. */
68
- }
68
+ if (parser->upgrade) {
69
+ /* handle new protocol */
70
+ } else if (nparsed != recved) {
71
+ /* Handle error. Usually just close the connection. */
72
+ }
73
+ ```
69
74
 
70
- HTTP needs to know where the end of the stream is. For example, sometimes
75
+ `http_parser` needs to know where the end of the stream is. For example, sometimes
71
76
  servers send responses without Content-Length and expect the client to
72
- consume input (for the body) until EOF. To tell http_parser about EOF, give
73
- `0` as the forth parameter to `http_parser_execute()`. Callbacks and errors
77
+ consume input (for the body) until EOF. To tell `http_parser` about EOF, give
78
+ `0` as the fourth parameter to `http_parser_execute()`. Callbacks and errors
74
79
  can still be encountered during an EOF, so one must still be prepared
75
80
  to receive them.
76
81
 
@@ -88,8 +93,8 @@ the on_body callback.
88
93
  The Special Problem of Upgrade
89
94
  ------------------------------
90
95
 
91
- HTTP supports upgrading the connection to a different protocol. An
92
- increasingly common example of this is the Web Socket protocol which sends
96
+ `http_parser` supports upgrading the connection to a different protocol. An
97
+ increasingly common example of this is the WebSocket protocol which sends
93
98
  a request like
94
99
 
95
100
  GET /demo HTTP/1.1
@@ -101,11 +106,11 @@ a request like
101
106
 
102
107
  followed by non-HTTP data.
103
108
 
104
- (See http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 for more
105
- information the Web Socket protocol.)
109
+ (See [RFC6455](https://tools.ietf.org/html/rfc6455) for more information the
110
+ WebSocket protocol.)
106
111
 
107
112
  To support this, the parser will treat this as a normal HTTP message without a
108
- body. Issuing both on_headers_complete and on_message_complete callbacks. However
113
+ body, issuing both on_headers_complete and on_message_complete callbacks. However
109
114
  http_parser_execute() will stop parsing at the end of the headers and return.
110
115
 
111
116
  The user is expected to check if `parser->upgrade` has been set to 1 after
@@ -126,21 +131,84 @@ There are two types of callbacks:
126
131
  * notification `typedef int (*http_cb) (http_parser*);`
127
132
  Callbacks: on_message_begin, on_headers_complete, on_message_complete.
128
133
  * data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);`
129
- Callbacks: (requests only) on_uri,
134
+ Callbacks: (requests only) on_url,
130
135
  (common) on_header_field, on_header_value, on_body;
131
136
 
132
137
  Callbacks must return 0 on success. Returning a non-zero value indicates
133
138
  error to the parser, making it exit immediately.
134
139
 
140
+ For cases where it is necessary to pass local information to/from a callback,
141
+ the `http_parser` object's `data` field can be used.
142
+ An example of such a case is when using threads to handle a socket connection,
143
+ parse a request, and then give a response over that socket. By instantiation
144
+ of a thread-local struct containing relevant data (e.g. accepted socket,
145
+ allocated memory for callbacks to write into, etc), a parser's callbacks are
146
+ able to communicate data between the scope of the thread and the scope of the
147
+ callback in a threadsafe manner. This allows `http_parser` to be used in
148
+ multi-threaded contexts.
149
+
150
+ Example:
151
+ ```c
152
+ typedef struct {
153
+ socket_t sock;
154
+ void* buffer;
155
+ int buf_len;
156
+ } custom_data_t;
157
+
158
+
159
+ int my_url_callback(http_parser* parser, const char *at, size_t length) {
160
+ /* access to thread local custom_data_t struct.
161
+ Use this access save parsed data for later use into thread local
162
+ buffer, or communicate over socket
163
+ */
164
+ parser->data;
165
+ ...
166
+ return 0;
167
+ }
168
+
169
+ ...
170
+
171
+ void http_parser_thread(socket_t sock) {
172
+ int nparsed = 0;
173
+ /* allocate memory for user data */
174
+ custom_data_t *my_data = malloc(sizeof(custom_data_t));
175
+
176
+ /* some information for use by callbacks.
177
+ * achieves thread -> callback information flow */
178
+ my_data->sock = sock;
179
+
180
+ /* instantiate a thread-local parser */
181
+ http_parser *parser = malloc(sizeof(http_parser));
182
+ http_parser_init(parser, HTTP_REQUEST); /* initialise parser */
183
+ /* this custom data reference is accessible through the reference to the
184
+ parser supplied to callback functions */
185
+ parser->data = my_data;
186
+
187
+ http_parser_settings settings; /* set up callbacks */
188
+ settings.on_url = my_url_callback;
189
+
190
+ /* execute parser */
191
+ nparsed = http_parser_execute(parser, &settings, buf, recved);
192
+
193
+ ...
194
+ /* parsed information copied from callback.
195
+ can now perform action on data copied into thread-local memory from callbacks.
196
+ achieves callback -> thread information flow */
197
+ my_data->buffer;
198
+ ...
199
+ }
200
+
201
+ ```
202
+
135
203
  In case you parse HTTP message in chunks (i.e. `read()` request line
136
204
  from socket, parse, read half headers, parse, etc) your data callbacks
137
- may be called more than once. Http-parser guarantees that data pointer is only
205
+ may be called more than once. `http_parser` guarantees that data pointer is only
138
206
  valid for the lifetime of callback. You can also `read()` into a heap allocated
139
207
  buffer to avoid copying memory around if this fits your application.
140
208
 
141
209
  Reading headers may be a tricky task if you read/parse headers partially.
142
210
  Basically, you need to remember whether last header callback was field or value
143
- and apply following logic:
211
+ and apply the following logic:
144
212
 
145
213
  (on_header_field and on_header_value shortened to on_h_*)
146
214
  ------------------------ ------------ --------------------------------------------
@@ -174,5 +242,5 @@ consecutive `on_url` callbacks.
174
242
  See examples of reading in headers:
175
243
 
176
244
  * [partial example](http://gist.github.com/155877) in C
177
- * [from http-parser tests](http://github.com/ry/http-parser/blob/37a0ff8928fb0d83cec0d0d8909c5a4abcd221af/test.c#L403) in C
178
- * [from Node library](http://github.com/ry/node/blob/842eaf446d2fdcb33b296c67c911c32a0dabc747/src/http.js#L284) in Javascript
245
+ * [from http-parser tests](http://github.com/joyent/http-parser/blob/37a0ff8/test.c#L403) in C
246
+ * [from Node library](http://github.com/joyent/node/blob/842eaf4/src/http.js#L284) in Javascript