ruby_http_parser 0.0.1

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.
@@ -0,0 +1,18 @@
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
+ /* for compatibility with Ruby 1.8.5, which doesn't declare RSTRING_PTR */
9
+ #ifndef RSTRING_PTR
10
+ #define RSTRING_PTR(s) (RSTRING(s)->ptr)
11
+ #endif
12
+
13
+ /* for compatibility with Ruby 1.8.5, which doesn't declare RSTRING_LEN */
14
+ #ifndef RSTRING_LEN
15
+ #define RSTRING_LEN(s) (RSTRING(s)->len)
16
+ #endif
17
+
18
+ #endif
@@ -0,0 +1,7 @@
1
+ require 'mkmf'
2
+
3
+ http_parser_dir = File.expand_path(File.dirname(__FILE__) + '/../http-parser')
4
+ $CFLAGS << " -I#{http_parser_dir} "
5
+
6
+ dir_config("ruby_http_parser")
7
+ create_makefile("ruby_http_parser")
@@ -0,0 +1,275 @@
1
+ #include "ruby.h"
2
+ #include "ext_help.h"
3
+ #include "http_parser.c"
4
+
5
+ #define HEADER_PREFIX "HTTP_"
6
+ #define DEF_CONST(N, val) N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&N)
7
+ #define GET_WRAPPER(N, from) ParserWrapper *N = (ParserWrapper *)(from)->data;
8
+
9
+ #define HASH_CAT(h, k, ptr, len) \
10
+ do { \
11
+ VALUE __v = rb_hash_aref(h, k); \
12
+ if (__v != Qnil) { \
13
+ rb_str_cat(__v, ptr, len); \
14
+ } else { \
15
+ rb_hash_aset(h, k, rb_str_new(ptr, len)); \
16
+ } \
17
+ } while(0)
18
+
19
+
20
+ // Stolen from Ebb
21
+ static char upcase[] =
22
+ "\0______________________________"
23
+ "_________________0123456789_____"
24
+ "__ABCDEFGHIJKLMNOPQRSTUVWXYZ____"
25
+ "__ABCDEFGHIJKLMNOPQRSTUVWXYZ____"
26
+ "________________________________"
27
+ "________________________________"
28
+ "________________________________"
29
+ "________________________________";
30
+
31
+ typedef struct ParserWrapper {
32
+ http_parser parser;
33
+ VALUE env;
34
+ VALUE on_message_complete;
35
+ VALUE on_headers_complete;
36
+ VALUE on_body;
37
+ VALUE last_field_name;
38
+ const char *last_field_name_at;
39
+ size_t last_field_name_length;
40
+ } ParserWrapper;
41
+
42
+ static VALUE eParserError;
43
+ static VALUE sCall;
44
+ static VALUE sPathInfo;
45
+ static VALUE sQueryString;
46
+ static VALUE sURL;
47
+ static VALUE sFragment;
48
+
49
+ /** Callbacks **/
50
+
51
+ int on_message_begin(http_parser *parser) {
52
+ GET_WRAPPER(wrapper, parser);
53
+ // Init env Rack hash
54
+ if (wrapper->env != Qnil)
55
+ rb_gc_unregister_address(&wrapper->env);
56
+ wrapper->env = rb_hash_new();
57
+ rb_gc_register_address(&wrapper->env);
58
+
59
+ return 0;
60
+ }
61
+
62
+ int on_path(http_parser *parser, const char *at, size_t length) {
63
+ GET_WRAPPER(wrapper, parser);
64
+ HASH_CAT(wrapper->env, sPathInfo, at, length);
65
+ return 0;
66
+ }
67
+
68
+ int on_query_string(http_parser *parser, const char *at, size_t length) {
69
+ GET_WRAPPER(wrapper, parser);
70
+ HASH_CAT(wrapper->env, sQueryString, at, length);
71
+ return 0;
72
+ }
73
+
74
+ int on_url(http_parser *parser, const char *at, size_t length) {
75
+ GET_WRAPPER(wrapper, parser);
76
+ HASH_CAT(wrapper->env, sURL, at, length);
77
+ return 0;
78
+ }
79
+
80
+ int on_fragment(http_parser *parser, const char *at, size_t length) {
81
+ GET_WRAPPER(wrapper, parser);
82
+ HASH_CAT(wrapper->env, sFragment, at, length);
83
+ return 0;
84
+ }
85
+
86
+ int on_header_field(http_parser *parser, const char *at, size_t length) {
87
+ GET_WRAPPER(wrapper, parser);
88
+
89
+ wrapper->last_field_name = Qnil;
90
+
91
+ if (wrapper->last_field_name_at == NULL) {
92
+ wrapper->last_field_name_at = at;
93
+ wrapper->last_field_name_length = length;
94
+ } else {
95
+ wrapper->last_field_name_length += length;
96
+ }
97
+
98
+ return 0;
99
+ }
100
+
101
+ int on_header_value(http_parser *parser, const char *at, size_t length) {
102
+ GET_WRAPPER(wrapper, parser);
103
+
104
+ VALUE name = Qnil;
105
+
106
+ if (wrapper->last_field_name == Qnil) {
107
+ wrapper->last_field_name = rb_str_new(HEADER_PREFIX, sizeof(HEADER_PREFIX) - 1 + wrapper->last_field_name_length);
108
+
109
+ // normalize header name
110
+ size_t name_length = wrapper->last_field_name_length;
111
+ const char *name_at = wrapper->last_field_name_at;
112
+ char *name_ptr = RSTRING_PTR(wrapper->last_field_name) + sizeof(HEADER_PREFIX) - 1;
113
+ int i;
114
+ for(i = 0; i < name_length; i++) {
115
+ char *ch = name_ptr + i;
116
+ *ch = upcase[(int)name_at[i]];
117
+ }
118
+
119
+ wrapper->last_field_name_at = NULL;
120
+ wrapper->last_field_name_length = 0;
121
+ }
122
+
123
+ HASH_CAT(wrapper->env, wrapper->last_field_name, at, length);
124
+
125
+ return 0;
126
+ }
127
+
128
+ int on_headers_complete(http_parser *parser) {
129
+ GET_WRAPPER(wrapper, parser);
130
+
131
+ if (wrapper->on_headers_complete != Qnil) {
132
+ rb_funcall(wrapper->on_headers_complete, sCall, 1, wrapper->env);
133
+ }
134
+
135
+ return 0;
136
+ }
137
+
138
+ int on_body(http_parser *parser, const char *at, size_t length) {
139
+ GET_WRAPPER(wrapper, parser);
140
+
141
+ if (wrapper->on_body != Qnil) {
142
+ rb_funcall(wrapper->on_body, sCall, 1, rb_str_new(at, length));
143
+ }
144
+
145
+ return 0;
146
+ }
147
+
148
+ int on_message_complete(http_parser *parser) {
149
+ GET_WRAPPER(wrapper, parser);
150
+
151
+ if (wrapper->on_message_complete != Qnil) {
152
+ rb_funcall(wrapper->on_message_complete, sCall, 1, wrapper->env);
153
+ }
154
+
155
+ return 0;
156
+ }
157
+
158
+
159
+ /** Usual ruby C stuff **/
160
+
161
+ void Parser_free(void *data) {
162
+ if(data) {
163
+ ParserWrapper *wrapper = (ParserWrapper *) data;
164
+ if (wrapper->env != Qnil)
165
+ rb_gc_unregister_address(&wrapper->env);
166
+ free(data);
167
+ }
168
+ }
169
+
170
+ VALUE Parser_alloc(VALUE klass) {
171
+ ParserWrapper *wrapper = ALLOC_N(ParserWrapper, 1);
172
+ http_parser_init(&wrapper->parser);
173
+
174
+ wrapper->env = Qnil;
175
+ wrapper->on_message_complete = Qnil;
176
+ wrapper->on_headers_complete = Qnil;
177
+ wrapper->on_body = Qnil;
178
+
179
+ wrapper->last_field_name = Qnil;
180
+ wrapper->last_field_name_at = NULL;
181
+ wrapper->last_field_name_length = 0;
182
+
183
+ // Init callbacks
184
+ wrapper->parser.on_message_begin = on_message_begin;
185
+ wrapper->parser.on_path = on_path;
186
+ wrapper->parser.on_query_string = on_query_string;
187
+ wrapper->parser.on_url = on_url;
188
+ wrapper->parser.on_fragment = on_fragment;
189
+ wrapper->parser.on_header_field = on_header_field;
190
+ wrapper->parser.on_header_value = on_header_value;
191
+ wrapper->parser.on_headers_complete = on_headers_complete;
192
+ wrapper->parser.on_body = on_body;
193
+ wrapper->parser.on_message_complete = on_message_complete;
194
+
195
+ wrapper->parser.data = wrapper;
196
+
197
+ return Data_Wrap_Struct(klass, NULL, Parser_free, wrapper);
198
+ }
199
+
200
+ VALUE Parser_parse_requests(VALUE self, VALUE data) {
201
+ ParserWrapper *wrapper = NULL;
202
+ char *ptr = RSTRING_PTR(data);
203
+ long len = RSTRING_LEN(data);
204
+
205
+ DATA_GET(self, ParserWrapper, wrapper);
206
+
207
+ size_t nparsed = http_parse_requests(&wrapper->parser, ptr, len);
208
+
209
+ if (nparsed != len) {
210
+ rb_raise(eParserError, "Invalid request");
211
+ }
212
+ }
213
+
214
+ VALUE Parser_parse_responses(VALUE self, VALUE data) {
215
+ ParserWrapper *wrapper = NULL;
216
+ char *ptr = RSTRING_PTR(data);
217
+ long len = RSTRING_LEN(data);
218
+
219
+ DATA_GET(self, ParserWrapper, wrapper);
220
+
221
+ size_t nparsed = http_parse_responses(&wrapper->parser, ptr, len);
222
+
223
+ if (nparsed != len) {
224
+ rb_raise(eParserError, "Invalid response");
225
+ }
226
+ }
227
+
228
+ VALUE Parser_set_on_headers_complete(VALUE self, VALUE callback) {
229
+ ParserWrapper *wrapper = NULL;
230
+ DATA_GET(self, ParserWrapper, wrapper);
231
+
232
+ wrapper->on_headers_complete = callback;
233
+ return callback;
234
+ }
235
+
236
+ VALUE Parser_set_on_message_complete(VALUE self, VALUE callback) {
237
+ ParserWrapper *wrapper = NULL;
238
+ DATA_GET(self, ParserWrapper, wrapper);
239
+
240
+ wrapper->on_message_complete = callback;
241
+ return callback;
242
+ }
243
+
244
+ VALUE Parser_set_on_body(VALUE self, VALUE callback) {
245
+ ParserWrapper *wrapper = NULL;
246
+ DATA_GET(self, ParserWrapper, wrapper);
247
+
248
+ wrapper->on_body = callback;
249
+ return callback;
250
+ }
251
+
252
+
253
+ void Init_ruby_http_parser() {
254
+ VALUE mHTTP = rb_define_module_under(rb_define_module("Net"), "HTTP");
255
+ VALUE cParser = rb_define_class_under(mHTTP, "Parser", rb_cObject);
256
+ VALUE cRequestParser = rb_define_class_under(mHTTP, "RequestParser", cParser);
257
+ VALUE cResponseParser = rb_define_class_under(mHTTP, "ResponseParser", cParser);
258
+
259
+ eParserError = rb_define_class_under(mHTTP, "ParseError", rb_eIOError);
260
+ sCall = rb_intern("call");
261
+
262
+ // String constants
263
+ DEF_CONST(sPathInfo, "PATH_INFO");
264
+ DEF_CONST(sQueryString, "QUERY_STRING");
265
+ DEF_CONST(sURL, "REQUEST_URI");
266
+ DEF_CONST(sFragment, "FRAGMENT");
267
+
268
+ rb_define_alloc_func(cParser, Parser_alloc);
269
+ rb_define_method(cParser, "on_message_complete=", Parser_set_on_message_complete, 1);
270
+ rb_define_method(cParser, "on_headers_complete=", Parser_set_on_headers_complete, 1);
271
+ rb_define_method(cParser, "on_body=", Parser_set_on_body, 1);
272
+
273
+ rb_define_method(cRequestParser, "<<", Parser_parse_requests, 1);
274
+ rb_define_method(cResponseParser, "<<", Parser_parse_responses, 1);
275
+ }
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + "/../../ruby_http_parser"
@@ -0,0 +1,13 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "ruby_http_parser"
3
+ s.version = "0.0.1"
4
+ s.summary = "Ruby bindings to http://github.com/ry/http-parser"
5
+
6
+ s.author = "Marc-Andre Cournoyer"
7
+ s.email = "macournoyer@gmail.com"
8
+ s.files = Dir["**/*"]
9
+ s.homepage = "http://github.com/macournoyer/ruby_http_parser"
10
+
11
+ s.require_paths = ["lib"]
12
+ s.extensions = ["ext/ruby_http_parser/extconf.rb"]
13
+ end
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__) + "/spec_helper"
2
+
3
+ describe Net::HTTP::RequestParser do
4
+ before do
5
+ @parser = Net::HTTP::RequestParser.new
6
+ end
7
+
8
+ it "should parse GET" do
9
+ env = nil
10
+ body = ""
11
+
12
+ @parser.on_headers_complete = proc { |e| env = e }
13
+ @parser.on_body = proc { |chunk| body << chunk }
14
+
15
+ @parser << "GET /test?ok=1 HTTP/1.1\r\n" +
16
+ "User-Agent: curl/7.18.0\r\n" +
17
+ "Host: 0.0.0.0:5000\r\n" +
18
+ "Accept: */*\r\n" +
19
+ "Content-Length: 5\r\n" +
20
+ "\r\n" +
21
+ "World"
22
+
23
+ env["PATH_INFO"].should == "/test"
24
+ env["HTTP_HOST"].should == "0.0.0.0:5000"
25
+ body.should == "World"
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ require "rubygems"
2
+ require "spec"
3
+ require File.dirname(__FILE__) + "/../lib/net/http/parser"
@@ -0,0 +1,157 @@
1
+
2
+ SHELL = /bin/sh
3
+
4
+ #### Start of system configuration section. ####
5
+
6
+ srcdir = /Users/ma/projects/ruby_http_parser/ext/ruby_http_parser
7
+ topdir = /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin10.0
8
+ hdrdir = $(topdir)
9
+ VPATH = $(srcdir):$(topdir):$(hdrdir)
10
+ exec_prefix = $(prefix)
11
+ prefix = $(DESTDIR)/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr
12
+ sharedstatedir = $(prefix)/com
13
+ mandir = $(DESTDIR)/usr/share/man
14
+ psdir = $(docdir)
15
+ oldincludedir = $(DESTDIR)/usr/include
16
+ localedir = $(datarootdir)/locale
17
+ bindir = $(exec_prefix)/bin
18
+ libexecdir = $(exec_prefix)/libexec
19
+ sitedir = $(DESTDIR)/Library/Ruby/Site
20
+ htmldir = $(docdir)
21
+ vendorarchdir = $(vendorlibdir)/$(sitearch)
22
+ includedir = $(prefix)/include
23
+ infodir = $(DESTDIR)/usr/share/info
24
+ vendorlibdir = $(vendordir)/$(ruby_version)
25
+ sysconfdir = $(prefix)/etc
26
+ libdir = $(exec_prefix)/lib
27
+ sbindir = $(exec_prefix)/sbin
28
+ rubylibdir = $(libdir)/ruby/$(ruby_version)
29
+ docdir = $(datarootdir)/doc/$(PACKAGE)
30
+ dvidir = $(docdir)
31
+ vendordir = $(libdir)/ruby/vendor_ruby
32
+ datarootdir = $(prefix)/share
33
+ pdfdir = $(docdir)
34
+ archdir = $(rubylibdir)/$(arch)
35
+ sitearchdir = $(sitelibdir)/$(sitearch)
36
+ datadir = $(datarootdir)
37
+ localstatedir = $(prefix)/var
38
+ sitelibdir = $(sitedir)/$(ruby_version)
39
+
40
+ CC = gcc
41
+ LIBRUBY = $(LIBRUBY_SO)
42
+ LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
43
+ LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
44
+ LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)
45
+
46
+ RUBY_EXTCONF_H =
47
+ CFLAGS = -fno-common -arch i386 -arch x86_64 -g -Os -pipe -fno-common -DENABLE_DTRACE -fno-common -pipe -fno-common $(cflags) -I/Users/ma/projects/ruby_http_parser/ext/http-parser
48
+ INCFLAGS = -I. -I$(topdir) -I$(hdrdir) -I$(srcdir)
49
+ DEFS =
50
+ CPPFLAGS = -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE $(DEFS) $(cppflags)
51
+ CXXFLAGS = $(CFLAGS)
52
+ ldflags = -L. -arch i386 -arch x86_64
53
+ dldflags =
54
+ archflag =
55
+ DLDFLAGS = $(ldflags) $(dldflags) $(archflag)
56
+ LDSHARED = cc -arch i386 -arch x86_64 -pipe -bundle -undefined dynamic_lookup
57
+ AR = ar
58
+ EXEEXT =
59
+
60
+ RUBY_INSTALL_NAME = ruby
61
+ RUBY_SO_NAME = ruby
62
+ arch = universal-darwin10.0
63
+ sitearch = universal-darwin10.0
64
+ ruby_version = 1.8
65
+ ruby = /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
66
+ RUBY = $(ruby)
67
+ RM = rm -f
68
+ MAKEDIRS = mkdir -p
69
+ INSTALL = /usr/bin/install -c
70
+ INSTALL_PROG = $(INSTALL) -m 0755
71
+ INSTALL_DATA = $(INSTALL) -m 644
72
+ COPY = cp
73
+
74
+ #### End of system configuration section. ####
75
+
76
+ preload =
77
+
78
+ libpath = . $(libdir)
79
+ LIBPATH = -L. -L$(libdir)
80
+ DEFFILE =
81
+
82
+ CLEANFILES = mkmf.log
83
+ DISTCLEANFILES =
84
+
85
+ extout =
86
+ extout_prefix =
87
+ target_prefix =
88
+ LOCAL_LIBS =
89
+ LIBS = $(LIBRUBYARG_SHARED) -lpthread -ldl
90
+ SRCS = ruby_http_parser.c
91
+ OBJS = ruby_http_parser.o
92
+ TARGET = ruby_http_parser
93
+ DLLIB = $(TARGET).bundle
94
+ EXTSTATIC =
95
+ STATIC_LIB =
96
+
97
+ BINDIR = $(bindir)
98
+ RUBYCOMMONDIR = $(sitedir)$(target_prefix)
99
+ RUBYLIBDIR = $(sitelibdir)$(target_prefix)
100
+ RUBYARCHDIR = $(sitearchdir)$(target_prefix)
101
+
102
+ TARGET_SO = $(DLLIB)
103
+ CLEANLIBS = $(TARGET).bundle $(TARGET).il? $(TARGET).tds $(TARGET).map
104
+ CLEANOBJS = *.o *.a *.s[ol] *.pdb *.exp *.bak
105
+
106
+ all: $(DLLIB)
107
+ static: $(STATIC_LIB)
108
+
109
+ clean:
110
+ @-$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES)
111
+
112
+ distclean: clean
113
+ @-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
114
+ @-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
115
+
116
+ realclean: distclean
117
+ install: install-so install-rb
118
+
119
+ install-so: $(RUBYARCHDIR)
120
+ install-so: $(RUBYARCHDIR)/$(DLLIB)
121
+ $(RUBYARCHDIR)/$(DLLIB): $(DLLIB)
122
+ $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
123
+ install-rb: pre-install-rb install-rb-default
124
+ install-rb-default: pre-install-rb-default
125
+ pre-install-rb: Makefile
126
+ pre-install-rb-default: Makefile
127
+ $(RUBYARCHDIR):
128
+ $(MAKEDIRS) $@
129
+
130
+ site-install: site-install-so site-install-rb
131
+ site-install-so: install-so
132
+ site-install-rb: install-rb
133
+
134
+ .SUFFIXES: .c .m .cc .cxx .cpp .C .o
135
+
136
+ .cc.o:
137
+ $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
138
+
139
+ .cxx.o:
140
+ $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
141
+
142
+ .cpp.o:
143
+ $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
144
+
145
+ .C.o:
146
+ $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
147
+
148
+ .c.o:
149
+ $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) -c $<
150
+
151
+ $(DLLIB): $(OBJS)
152
+ @-$(RM) $@
153
+ $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
154
+
155
+
156
+
157
+ $(OBJS): ruby.h defines.h