cool.io 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/.gitignore +25 -0
  2. data/CHANGES +199 -0
  3. data/LICENSE +20 -0
  4. data/README.markdown +4 -0
  5. data/Rakefile +98 -0
  6. data/VERSION +1 -0
  7. data/examples/echo_client.rb +38 -0
  8. data/examples/echo_server.rb +27 -0
  9. data/examples/google.rb +9 -0
  10. data/examples/httpclient.rb +38 -0
  11. data/ext/cool.io/.gitignore +5 -0
  12. data/ext/cool.io/cool.io.h +58 -0
  13. data/ext/cool.io/cool.io_ext.c +25 -0
  14. data/ext/cool.io/ev_wrap.h +8 -0
  15. data/ext/cool.io/extconf.rb +69 -0
  16. data/ext/cool.io/iowatcher.c +189 -0
  17. data/ext/cool.io/libev.c +8 -0
  18. data/ext/cool.io/loop.c +303 -0
  19. data/ext/cool.io/stat_watcher.c +191 -0
  20. data/ext/cool.io/timer_watcher.c +219 -0
  21. data/ext/cool.io/utils.c +122 -0
  22. data/ext/cool.io/watcher.c +264 -0
  23. data/ext/cool.io/watcher.h +71 -0
  24. data/ext/http11_client/.gitignore +5 -0
  25. data/ext/http11_client/ext_help.h +14 -0
  26. data/ext/http11_client/extconf.rb +6 -0
  27. data/ext/http11_client/http11_client.c +300 -0
  28. data/ext/http11_client/http11_parser.c +403 -0
  29. data/ext/http11_client/http11_parser.h +48 -0
  30. data/ext/http11_client/http11_parser.rl +173 -0
  31. data/ext/libev/Changes +364 -0
  32. data/ext/libev/LICENSE +36 -0
  33. data/ext/libev/README +58 -0
  34. data/ext/libev/README.embed +3 -0
  35. data/ext/libev/ev.c +3867 -0
  36. data/ext/libev/ev.h +826 -0
  37. data/ext/libev/ev_epoll.c +234 -0
  38. data/ext/libev/ev_kqueue.c +198 -0
  39. data/ext/libev/ev_poll.c +148 -0
  40. data/ext/libev/ev_port.c +164 -0
  41. data/ext/libev/ev_select.c +307 -0
  42. data/ext/libev/ev_vars.h +197 -0
  43. data/ext/libev/ev_win32.c +153 -0
  44. data/ext/libev/ev_wrap.h +186 -0
  45. data/ext/libev/test_libev_win32.c +123 -0
  46. data/ext/libev/update_ev_wrap +19 -0
  47. data/lib/.gitignore +2 -0
  48. data/lib/cool.io.rb +30 -0
  49. data/lib/cool.io/async_watcher.rb +43 -0
  50. data/lib/cool.io/dns_resolver.rb +220 -0
  51. data/lib/cool.io/eventmachine.rb +234 -0
  52. data/lib/cool.io/http_client.rb +419 -0
  53. data/lib/cool.io/io.rb +174 -0
  54. data/lib/cool.io/iowatcher.rb +17 -0
  55. data/lib/cool.io/listener.rb +93 -0
  56. data/lib/cool.io/loop.rb +130 -0
  57. data/lib/cool.io/meta.rb +49 -0
  58. data/lib/cool.io/server.rb +74 -0
  59. data/lib/cool.io/socket.rb +224 -0
  60. data/lib/cool.io/timer_watcher.rb +17 -0
  61. data/lib/coolio.rb +2 -0
  62. data/lib/rev.rb +4 -0
  63. data/spec/async_watcher_spec.rb +57 -0
  64. data/spec/possible_tests/schedules_other_threads.rb +48 -0
  65. data/spec/possible_tests/test_on_resolve_failed.rb +9 -0
  66. data/spec/possible_tests/test_resolves.rb +27 -0
  67. data/spec/possible_tests/test_write_during_resolve.rb +27 -0
  68. data/spec/possible_tests/works_straight.rb +71 -0
  69. data/spec/spec_helper.rb +5 -0
  70. data/spec/timer_watcher_spec.rb +55 -0
  71. data/spec/unix_listener_spec.rb +25 -0
  72. data/spec/unix_server_spec.rb +25 -0
  73. metadata +184 -0
@@ -0,0 +1,264 @@
1
+ /*
2
+ * Copyright (C) 2007-10 Tony Arcieri
3
+ * You may redistribute this under the terms of the Ruby license.
4
+ * See LICENSE for details
5
+ */
6
+
7
+ #include "ruby.h"
8
+ #include "ev_wrap.h"
9
+
10
+ #include "cool.io.h"
11
+
12
+ static VALUE mCoolio = Qnil;
13
+ static VALUE cCoolio_Watcher = Qnil;
14
+
15
+ static VALUE Coolio_Watcher_allocate(VALUE klass);
16
+ static void Coolio_Watcher_mark(struct Coolio_Watcher *watcher);
17
+ static void Coolio_Watcher_free(struct Coolio_Watcher *watcher);
18
+
19
+ static VALUE Coolio_Watcher_initialize(VALUE self);
20
+ static VALUE Coolio_Watcher_attach(VALUE self, VALUE loop);
21
+ static VALUE Coolio_Watcher_detach(VALUE self);
22
+ static VALUE Coolio_Watcher_enable(VALUE self);
23
+ static VALUE Coolio_Watcher_disable(VALUE self);
24
+ static VALUE Coolio_Watcher_evloop(VALUE self);
25
+ static VALUE Coolio_Watcher_attached(VALUE self);
26
+ static VALUE Coolio_Watcher_enabled(VALUE self);
27
+
28
+ /*
29
+ * Watchers are Coolio's event observers. They contain a set of callback
30
+ * methods prefixed by on_* which fire whenever events occur.
31
+ *
32
+ * In order for a watcher to fire events it must be attached to a running
33
+ * loop. Every watcher has an attach and detach method to control which
34
+ * loop it's associated with.
35
+ *
36
+ * Watchers also have an enable and disable method. This allows a watcher
37
+ * to temporarily ignore certain events while remaining attached to a given
38
+ * loop. This is good for watchers which need to be toggled on and off.
39
+ */
40
+ void Init_coolio_watcher()
41
+ {
42
+ mCoolio = rb_define_module("Coolio");
43
+ cCoolio_Watcher = rb_define_class_under(mCoolio, "Watcher", rb_cObject);
44
+ rb_define_alloc_func(cCoolio_Watcher, Coolio_Watcher_allocate);
45
+
46
+ rb_define_method(cCoolio_Watcher, "initialize", Coolio_Watcher_initialize, 0);
47
+ rb_define_method(cCoolio_Watcher, "attach", Coolio_Watcher_attach, 1);
48
+ rb_define_method(cCoolio_Watcher, "detach", Coolio_Watcher_detach, 0);
49
+ rb_define_method(cCoolio_Watcher, "enable", Coolio_Watcher_enable, 0);
50
+ rb_define_method(cCoolio_Watcher, "disable", Coolio_Watcher_disable, 0);
51
+ rb_define_method(cCoolio_Watcher, "evloop", Coolio_Watcher_evloop, 0);
52
+ rb_define_method(cCoolio_Watcher, "attached?", Coolio_Watcher_attached, 0);
53
+ rb_define_method(cCoolio_Watcher, "enabled?", Coolio_Watcher_enabled, 0);
54
+ }
55
+
56
+ static VALUE Coolio_Watcher_allocate(VALUE klass)
57
+ {
58
+ struct Coolio_Watcher *watcher_data = (struct Coolio_Watcher *)xmalloc(sizeof(struct Coolio_Watcher));
59
+
60
+ watcher_data->loop = Qnil;
61
+ watcher_data->enabled = 0;
62
+
63
+ return Data_Wrap_Struct(klass, Coolio_Watcher_mark, Coolio_Watcher_free, watcher_data);
64
+ }
65
+
66
+ static void Coolio_Watcher_mark(struct Coolio_Watcher *watcher_data)
67
+ {
68
+ if(watcher_data->loop != Qnil)
69
+ rb_gc_mark(watcher_data->loop);
70
+ }
71
+
72
+ static void Coolio_Watcher_free(struct Coolio_Watcher *watcher_data)
73
+ {
74
+ xfree(watcher_data);
75
+ }
76
+
77
+ static VALUE Coolio_Watcher_initialize(VALUE self)
78
+ {
79
+ rb_raise(rb_eRuntimeError, "watcher base class should not be initialized directly");
80
+ }
81
+
82
+ /**
83
+ * call-seq:
84
+ * Coolio::Watcher.attach(loop) -> Coolio::Watcher
85
+ *
86
+ * Attach the watcher to the given Coolio::Loop. If the watcher is already attached
87
+ * to a loop, detach it from the old one and attach it to the new one.
88
+ */
89
+ static VALUE Coolio_Watcher_attach(VALUE self, VALUE loop)
90
+ {
91
+ VALUE loop_watchers, active_watchers;
92
+ struct Coolio_Watcher *watcher_data;
93
+
94
+ Data_Get_Struct(self, struct Coolio_Watcher, watcher_data);
95
+ watcher_data->enabled = 1;
96
+
97
+ loop_watchers = rb_iv_get(loop, "@watchers");
98
+
99
+ if(loop_watchers == Qnil) {
100
+ /* we should never get here */
101
+ loop_watchers = rb_hash_new();
102
+ rb_iv_set(loop, "@watchers", loop_watchers);
103
+ }
104
+
105
+ /* Add us to the loop's array of active watchers. This is mainly done
106
+ * to keep the VM from garbage collecting watchers that are associated
107
+ * with a loop (and also lets you see within Ruby which watchers are
108
+ * associated with a given loop), but isn't really necessary for any
109
+ * other reason */
110
+ rb_hash_aset(loop_watchers, self, Qtrue);
111
+
112
+ active_watchers = rb_iv_get(loop, "@active_watchers");
113
+ if(active_watchers == Qnil)
114
+ active_watchers = INT2NUM(1);
115
+ else
116
+ active_watchers = INT2NUM(NUM2INT(active_watchers) + 1);
117
+ rb_iv_set(loop, "@active_watchers", active_watchers);
118
+
119
+ return self;
120
+ }
121
+
122
+ /**
123
+ * call-seq:
124
+ * Coolio::Watcher.detach -> Coolio::Watcher
125
+ *
126
+ * Detach the watcher from its current Coolio::Loop.
127
+ */
128
+ static VALUE Coolio_Watcher_detach(VALUE self)
129
+ {
130
+ struct Coolio_Watcher *watcher_data;
131
+ struct Coolio_Loop *loop_data;
132
+ VALUE loop_watchers;
133
+ int i;
134
+
135
+ Data_Get_Struct(self, struct Coolio_Watcher, watcher_data);
136
+
137
+ if(watcher_data->loop == Qnil)
138
+ rb_raise(rb_eRuntimeError, "not attached to a loop");
139
+
140
+ loop_watchers = rb_iv_get(watcher_data->loop, "@watchers");
141
+
142
+ /* Remove us from the loop's array of active watchers. This likely
143
+ * has negative performance and scalability characteristics as this
144
+ * isn't an O(1) operation. Hopefully there's a better way...
145
+ * Trying a hash for now... */
146
+ rb_hash_delete(loop_watchers, self);
147
+
148
+ if(watcher_data->enabled) {
149
+ rb_iv_set(
150
+ watcher_data->loop,
151
+ "@active_watchers",
152
+ INT2NUM(NUM2INT(rb_iv_get(watcher_data->loop, "@active_watchers")) - 1)
153
+ );
154
+ }
155
+
156
+ watcher_data->enabled = 0;
157
+
158
+ Data_Get_Struct(watcher_data->loop, struct Coolio_Loop, loop_data);
159
+
160
+ /* Iterate through the events in the loop's event buffer. If there
161
+ * are any pending events from this watcher, mark them NULL. The
162
+ * dispatch loop will skip them. This prevents watchers earlier
163
+ * in the event buffer from detaching others which may have pending
164
+ * events in the buffer but get garbage collected in the meantime */
165
+ for(i = 0; i < loop_data->events_received; i++) {
166
+ if(loop_data->eventbuf[i].watcher == self)
167
+ loop_data->eventbuf[i].watcher = Qnil;
168
+ }
169
+
170
+ watcher_data->loop = Qnil;
171
+
172
+ return self;
173
+ }
174
+
175
+ /**
176
+ * call-seq:
177
+ * Coolio::Watcher.enable -> Coolio::Watcher
178
+ *
179
+ * Re-enable a watcher which has been temporarily disabled. See the
180
+ * disable method for a more thorough explanation.
181
+ */
182
+ static VALUE Coolio_Watcher_enable(VALUE self)
183
+ {
184
+ struct Coolio_Watcher *watcher_data;
185
+ Data_Get_Struct(self, struct Coolio_Watcher, watcher_data);
186
+
187
+ if(watcher_data->enabled)
188
+ rb_raise(rb_eRuntimeError, "already enabled");
189
+
190
+ watcher_data->enabled = 1;
191
+
192
+ rb_iv_set(
193
+ watcher_data->loop,
194
+ "@active_watchers",
195
+ INT2NUM(NUM2INT(rb_iv_get(watcher_data->loop, "@active_watchers")) + 1)
196
+ );
197
+
198
+ return self;
199
+ }
200
+
201
+ /**
202
+ * call-seq:
203
+ * Coolio::Watcher.disable -> Coolio::Watcher
204
+ *
205
+ * Temporarily disable an event watcher which is attached to a loop.
206
+ * This is useful if you wish to toggle event monitoring on and off.
207
+ */
208
+ static VALUE Coolio_Watcher_disable(VALUE self)
209
+ {
210
+ struct Coolio_Watcher *watcher_data;
211
+ Data_Get_Struct(self, struct Coolio_Watcher, watcher_data);
212
+
213
+ if(!watcher_data->enabled)
214
+ rb_raise(rb_eRuntimeError, "already disabled");
215
+
216
+ watcher_data->enabled = 0;
217
+
218
+ rb_iv_set(
219
+ watcher_data->loop,
220
+ "@active_watchers",
221
+ INT2NUM(NUM2INT(rb_iv_get(watcher_data->loop, "@active_watchers")) - 1)
222
+ );
223
+
224
+ return self;
225
+ }
226
+
227
+ /**
228
+ * call-seq:
229
+ * Coolio::Watcher.evloop -> Coolio::Loop
230
+ *
231
+ * Return the loop to which we're currently attached
232
+ */
233
+ static VALUE Coolio_Watcher_evloop(VALUE self)
234
+ {
235
+ struct Coolio_Watcher *watcher_data;
236
+
237
+ Data_Get_Struct(self, struct Coolio_Watcher, watcher_data);
238
+ return watcher_data->loop;
239
+ }
240
+
241
+ /**
242
+ * call-seq:
243
+ * Coolio::Watcher.attached? -> Boolean
244
+ *
245
+ * Is the watcher currently attached to an event loop?
246
+ */
247
+ static VALUE Coolio_Watcher_attached(VALUE self)
248
+ {
249
+ return Coolio_Watcher_evloop(self) != Qnil;
250
+ }
251
+
252
+ /**
253
+ * call-seq:
254
+ * Coolio::Watcher.enabled? -> Boolean
255
+ *
256
+ * Is the watcher currently enabled?
257
+ */
258
+ static VALUE Coolio_Watcher_enabled(VALUE self)
259
+ {
260
+ struct Coolio_Watcher *watcher_data;
261
+ Data_Get_Struct(self, struct Coolio_Watcher, watcher_data);
262
+
263
+ return watcher_data->enabled ? Qtrue : Qfalse;
264
+ }
@@ -0,0 +1,71 @@
1
+ /*
2
+ * Copyright (C) 2007-10 Tony Arcieri
3
+ * You may redistribute this under the terms of the Ruby license.
4
+ * See LICENSE for details
5
+ */
6
+
7
+ #ifndef WATCHER_H
8
+ #define WATCHER_H
9
+
10
+ #define Watcher_Attach(watcher_type, detach_func, watcher, loop) \
11
+ struct Coolio_Watcher *watcher_data; \
12
+ struct Coolio_Loop *loop_data; \
13
+ \
14
+ if(!rb_obj_is_kind_of(loop, cCoolio_Loop)) \
15
+ rb_raise(rb_eArgError, "expected loop to be an instance of Coolio::Loop"); \
16
+ \
17
+ Data_Get_Struct(watcher, struct Coolio_Watcher, watcher_data); \
18
+ Data_Get_Struct(loop, struct Coolio_Loop, loop_data); \
19
+ \
20
+ if(watcher_data->loop != Qnil) \
21
+ detach_func(watcher); \
22
+ \
23
+ watcher_data->loop = loop; \
24
+ ev_##watcher_type##_start(loop_data->ev_loop, &watcher_data->event_types.ev_##watcher_type); \
25
+ rb_call_super(1, &loop)
26
+
27
+ #define Watcher_Detach(watcher_type, watcher) \
28
+ struct Coolio_Watcher *watcher_data; \
29
+ struct Coolio_Loop *loop_data; \
30
+ \
31
+ Data_Get_Struct(watcher, struct Coolio_Watcher, watcher_data); \
32
+ \
33
+ if(watcher_data->loop == Qnil) \
34
+ rb_raise(rb_eRuntimeError, "not attached to a loop"); \
35
+ \
36
+ Data_Get_Struct(watcher_data->loop, struct Coolio_Loop, loop_data); \
37
+ \
38
+ ev_##watcher_type##_stop(loop_data->ev_loop, &watcher_data->event_types.ev_##watcher_type); \
39
+ rb_call_super(0, 0)
40
+
41
+ #define Watcher_Enable(watcher_type, watcher) \
42
+ struct Coolio_Watcher *watcher_data; \
43
+ struct Coolio_Loop *loop_data; \
44
+ \
45
+ Data_Get_Struct(watcher, struct Coolio_Watcher, watcher_data); \
46
+ \
47
+ if(watcher_data->loop == Qnil) \
48
+ rb_raise(rb_eRuntimeError, "not attached to a loop"); \
49
+ \
50
+ rb_call_super(0, 0); \
51
+ \
52
+ Data_Get_Struct(watcher_data->loop, struct Coolio_Loop, loop_data); \
53
+ \
54
+ ev_##watcher_type##_start(loop_data->ev_loop, &watcher_data->event_types.ev_##watcher_type)
55
+
56
+ #define Watcher_Disable(watcher_type, watcher) \
57
+ struct Coolio_Watcher *watcher_data; \
58
+ struct Coolio_Loop *loop_data; \
59
+ \
60
+ Data_Get_Struct(watcher, struct Coolio_Watcher, watcher_data); \
61
+ \
62
+ if(watcher_data->loop == Qnil) \
63
+ rb_raise(rb_eRuntimeError, "not attached to a loop"); \
64
+ \
65
+ rb_call_super(0, 0); \
66
+ \
67
+ Data_Get_Struct(watcher_data->loop, struct Coolio_Loop, loop_data); \
68
+ \
69
+ ev_##watcher_type##_stop(loop_data->ev_loop, &watcher_data->event_types.ev_##watcher_type)
70
+
71
+ #endif
@@ -0,0 +1,5 @@
1
+ Makefile
2
+ mkmf.log
3
+ *.o
4
+ *.so
5
+ *.bundle
@@ -0,0 +1,14 @@
1
+ #ifndef ext_help_h
2
+ #define ext_help_h
3
+
4
+ #define RAISE_NOT_NULL(T) if(T == NULL) rb_raise(rb_eArgError, "NULL found for " # T " when shouldn't be.");
5
+ #define DATA_GET(from,type,name) Data_Get_Struct(from,type,name); RAISE_NOT_NULL(name);
6
+ #define REQUIRE_TYPE(V, T) if(TYPE(V) != T) rb_raise(rb_eTypeError, "Wrong argument type for " # V " required " # T);
7
+
8
+ #ifdef DEBUG
9
+ #define TRACE() fprintf(stderr, "> %s:%d:%s\n", __FILE__, __LINE__, __FUNCTION__)
10
+ #else
11
+ #define TRACE()
12
+ #endif
13
+
14
+ #endif
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+
3
+ dir_config("http11_client")
4
+ have_library("c", "main")
5
+
6
+ create_makefile("http11_client")
@@ -0,0 +1,300 @@
1
+ /**
2
+ * Copyright (c) 2005 Zed A. Shaw
3
+ * You can redistribute it and/or modify it under the same terms as Ruby.
4
+ */
5
+
6
+ #include "ruby.h"
7
+ #include "ext_help.h"
8
+ #include <assert.h>
9
+ #include <string.h>
10
+ #include "http11_parser.h"
11
+ #include <ctype.h>
12
+
13
+ static VALUE mCoolio;
14
+ static VALUE cHttpClientParser;
15
+ static VALUE eHttpClientParserError;
16
+
17
+ #define id_reason rb_intern("@http_reason")
18
+ #define id_status rb_intern("@http_status")
19
+ #define id_version rb_intern("@http_version")
20
+ #define id_body rb_intern("@http_body")
21
+ #define id_chunk_size rb_intern("@http_chunk_size")
22
+ #define id_last_chunk rb_intern("@last_chunk")
23
+
24
+ void client_http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen)
25
+ {
26
+ char *ch, *end;
27
+ VALUE req = (VALUE)data;
28
+ VALUE v = Qnil;
29
+ VALUE f = Qnil;
30
+
31
+ v = rb_str_new(value, vlen);
32
+ f = rb_str_new(field, flen);
33
+
34
+ /* Yes Children, rb_str_upcase_bang isn't even available as an intern.h function.
35
+ * how incredibly handy to not have that. Nope, I have to do it by hand.*/
36
+ for(ch = RSTRING_PTR(f), end = ch + RSTRING_LEN(f); ch < end; ch++) {
37
+ if(*ch == '-') {
38
+ *ch = '_';
39
+ } else {
40
+ *ch = toupper(*ch);
41
+ }
42
+ }
43
+
44
+ rb_hash_aset(req, f, v);
45
+ }
46
+
47
+ void client_reason_phrase(void *data, const char *at, size_t length)
48
+ {
49
+ VALUE req = (VALUE)data;
50
+ VALUE v = Qnil;
51
+
52
+ v = rb_str_new(at, length);
53
+
54
+ rb_ivar_set(req, id_reason, v);
55
+ }
56
+
57
+ void client_status_code(void *data, const char *at, size_t length)
58
+ {
59
+ VALUE req = (VALUE)data;
60
+ VALUE v = Qnil;
61
+
62
+ v = rb_str_new(at, length);
63
+
64
+ rb_ivar_set(req, id_status, v);
65
+ }
66
+
67
+ void client_http_version(void *data, const char *at, size_t length)
68
+ {
69
+ VALUE req = (VALUE)data;
70
+ VALUE v = Qnil;
71
+
72
+ v = rb_str_new(at, length);
73
+
74
+ rb_ivar_set(req, id_version, v);
75
+ }
76
+
77
+ /** Finalizes the request header to have a bunch of stuff that's
78
+ needed. */
79
+ void client_header_done(void *data, const char *at, size_t length)
80
+ {
81
+ VALUE req = (VALUE)data;
82
+ VALUE v = Qnil;
83
+
84
+ v = rb_str_new(at, length);
85
+ rb_ivar_set(req, id_body, v);
86
+ }
87
+
88
+ void client_chunk_size(void *data, const char *at, size_t length)
89
+ {
90
+ VALUE req = (VALUE)data;
91
+ VALUE v = Qnil;
92
+
93
+ if(length <= 0) {
94
+ rb_raise(eHttpClientParserError, "Chunked Encoding gave <= 0 chunk size.");
95
+ }
96
+
97
+ v = rb_str_new(at, length);
98
+
99
+ rb_ivar_set(req, id_chunk_size, v);
100
+ }
101
+
102
+ void client_last_chunk(void *data, const char *at, size_t length) {
103
+ VALUE req = (VALUE)data;
104
+ rb_ivar_set(req, id_last_chunk,Qtrue);
105
+ }
106
+
107
+
108
+ void HttpClientParser_free(void *data) {
109
+ TRACE();
110
+
111
+ if(data) {
112
+ free(data);
113
+ }
114
+ }
115
+
116
+
117
+ VALUE HttpClientParser_alloc(VALUE klass)
118
+ {
119
+ VALUE obj;
120
+ httpclient_parser *hp = ALLOC_N(httpclient_parser, 1);
121
+ TRACE();
122
+ hp->http_field = client_http_field;
123
+ hp->status_code = client_status_code;
124
+ hp->reason_phrase = client_reason_phrase;
125
+ hp->http_version = client_http_version;
126
+ hp->header_done = client_header_done;
127
+ hp->chunk_size = client_chunk_size;
128
+ hp->last_chunk = client_last_chunk;
129
+ httpclient_parser_init(hp);
130
+
131
+ obj = Data_Wrap_Struct(klass, NULL, HttpClientParser_free, hp);
132
+
133
+ return obj;
134
+ }
135
+
136
+
137
+ /**
138
+ * call-seq:
139
+ * parser.new -> parser
140
+ *
141
+ * Creates a new parser.
142
+ */
143
+ VALUE HttpClientParser_init(VALUE self)
144
+ {
145
+ httpclient_parser *http = NULL;
146
+ DATA_GET(self, httpclient_parser, http);
147
+ httpclient_parser_init(http);
148
+
149
+ return self;
150
+ }
151
+
152
+
153
+ /**
154
+ * call-seq:
155
+ * parser.reset -> nil
156
+ *
157
+ * Resets the parser to it's initial state so that you can reuse it
158
+ * rather than making new ones.
159
+ */
160
+ VALUE HttpClientParser_reset(VALUE self)
161
+ {
162
+ httpclient_parser *http = NULL;
163
+ DATA_GET(self, httpclient_parser, http);
164
+ httpclient_parser_init(http);
165
+
166
+ return Qnil;
167
+ }
168
+
169
+
170
+ /**
171
+ * call-seq:
172
+ * parser.finish -> true/false
173
+ *
174
+ * Finishes a parser early which could put in a "good" or bad state.
175
+ * You should call reset after finish it or bad things will happen.
176
+ */
177
+ VALUE HttpClientParser_finish(VALUE self)
178
+ {
179
+ httpclient_parser *http = NULL;
180
+ DATA_GET(self, httpclient_parser, http);
181
+ httpclient_parser_finish(http);
182
+
183
+ return httpclient_parser_is_finished(http) ? Qtrue : Qfalse;
184
+ }
185
+
186
+
187
+ /**
188
+ * call-seq:
189
+ * parser.execute(req_hash, data, start) -> Integer
190
+ *
191
+ * Takes a Hash and a String of data, parses the String of data filling in the Hash
192
+ * returning an Integer to indicate how much of the data has been read. No matter
193
+ * what the return value, you should call HttpClientParser#finished? and HttpClientParser#error?
194
+ * to figure out if it's done parsing or there was an error.
195
+ *
196
+ * This function now throws an exception when there is a parsing error. This makes
197
+ * the logic for working with the parser much easier. You can still test for an
198
+ * error, but now you need to wrap the parser with an exception handling block.
199
+ *
200
+ * The third argument allows for parsing a partial request and then continuing
201
+ * the parsing from that position. It needs all of the original data as well
202
+ * so you have to append to the data buffer as you read.
203
+ */
204
+ VALUE HttpClientParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
205
+ {
206
+ httpclient_parser *http = NULL;
207
+ int from = 0;
208
+ char *dptr = NULL;
209
+ long dlen = 0;
210
+
211
+ REQUIRE_TYPE(req_hash, T_HASH);
212
+ REQUIRE_TYPE(data, T_STRING);
213
+ REQUIRE_TYPE(start, T_FIXNUM);
214
+
215
+ DATA_GET(self, httpclient_parser, http);
216
+
217
+ from = FIX2INT(start);
218
+ dptr = RSTRING_PTR(data);
219
+ dlen = RSTRING_LEN(data);
220
+
221
+ if(from >= dlen) {
222
+ rb_raise(eHttpClientParserError, "Requested start is after data buffer end.");
223
+ } else {
224
+ http->data = (void *)req_hash;
225
+ httpclient_parser_execute(http, dptr, dlen, from);
226
+
227
+ if(httpclient_parser_has_error(http)) {
228
+ rb_raise(eHttpClientParserError, "Invalid HTTP format, parsing fails.");
229
+ } else {
230
+ return INT2FIX(httpclient_parser_nread(http));
231
+ }
232
+ }
233
+ }
234
+
235
+
236
+
237
+ /**
238
+ * call-seq:
239
+ * parser.error? -> true/false
240
+ *
241
+ * Tells you whether the parser is in an error state.
242
+ */
243
+ VALUE HttpClientParser_has_error(VALUE self)
244
+ {
245
+ httpclient_parser *http = NULL;
246
+ DATA_GET(self, httpclient_parser, http);
247
+
248
+ return httpclient_parser_has_error(http) ? Qtrue : Qfalse;
249
+ }
250
+
251
+
252
+ /**
253
+ * call-seq:
254
+ * parser.finished? -> true/false
255
+ *
256
+ * Tells you whether the parser is finished or not and in a good state.
257
+ */
258
+ VALUE HttpClientParser_is_finished(VALUE self)
259
+ {
260
+ httpclient_parser *http = NULL;
261
+ DATA_GET(self, httpclient_parser, http);
262
+
263
+ return httpclient_parser_is_finished(http) ? Qtrue : Qfalse;
264
+ }
265
+
266
+
267
+ /**
268
+ * call-seq:
269
+ * parser.nread -> Integer
270
+ *
271
+ * Returns the amount of data processed so far during this processing cycle. It is
272
+ * set to 0 on initialize or reset calls and is incremented each time execute is called.
273
+ */
274
+ VALUE HttpClientParser_nread(VALUE self)
275
+ {
276
+ httpclient_parser *http = NULL;
277
+ DATA_GET(self, httpclient_parser, http);
278
+
279
+ return INT2FIX(http->nread);
280
+ }
281
+
282
+
283
+
284
+ void Init_http11_client()
285
+ {
286
+
287
+ mCoolio = rb_define_module("Coolio");
288
+
289
+ eHttpClientParserError = rb_define_class_under(mCoolio, "HttpClientParserError", rb_eIOError);
290
+
291
+ cHttpClientParser = rb_define_class_under(mCoolio, "HttpClientParser", rb_cObject);
292
+ rb_define_alloc_func(cHttpClientParser, HttpClientParser_alloc);
293
+ rb_define_method(cHttpClientParser, "initialize", HttpClientParser_init,0);
294
+ rb_define_method(cHttpClientParser, "reset", HttpClientParser_reset,0);
295
+ rb_define_method(cHttpClientParser, "finish", HttpClientParser_finish,0);
296
+ rb_define_method(cHttpClientParser, "execute", HttpClientParser_execute,3);
297
+ rb_define_method(cHttpClientParser, "error?", HttpClientParser_has_error,0);
298
+ rb_define_method(cHttpClientParser, "finished?", HttpClientParser_is_finished,0);
299
+ rb_define_method(cHttpClientParser, "nread", HttpClientParser_nread,0);
300
+ }