libevent 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ lib/*.so
6
+ tmp/*
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "rake-compiler"
data/README.md ADDED
@@ -0,0 +1,146 @@
1
+ ## Libevent
2
+
3
+ C extension to [libevent](http://libevent.org/) library.
4
+
5
+ ## Description
6
+
7
+ The nice feature of libevent is it already contains build in HTTP server (evhttp).
8
+
9
+ Currently libevent extension implements mostly http server.
10
+
11
+ ## Dependencies
12
+
13
+ * libevent v2
14
+
15
+ ## Documentation
16
+
17
+ Please read [libevent rubydoc](http://rubydoc.info/github/ayanko/libevent/frames)
18
+
19
+ ## Installation
20
+
21
+ gem install libevent
22
+
23
+ ## Using Libevent HTTP server
24
+
25
+ Check `samples` directory
26
+
27
+ ### From scratch
28
+
29
+ Simple server
30
+
31
+ require "libevent"
32
+
33
+ # create event base
34
+ base = Libevent::Base.new
35
+
36
+ # create http server instance
37
+ http = Libevent::Http.new(base)
38
+
39
+ # bind socket
40
+ http.bind_socket("0.0.0.0", 15015)
41
+
42
+ # set handler
43
+ http.handler do |request|
44
+ request.send_reply(200, {}, ["Hello World\n"])
45
+ end
46
+
47
+ # catch SIGINT
48
+ base.trap_signal("INT") { base.exit_loop }
49
+
50
+ # start libevent loop
51
+ base.dispatch
52
+
53
+ Check with curl
54
+
55
+ $ curl -v http://localhost:15015
56
+ > GET / HTTP/1.1
57
+ > User-Agent: curl/7.22.0 (x86_64-unknown-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.0e zlib/1.2.5 libssh2/1.3.0
58
+ > Host: localhost:15015
59
+ > Accept: */*
60
+ >
61
+ < HTTP/1.1 200 OK
62
+ < Transfer-Encoding: chunked
63
+ < Date: Fri, 18 Nov 2011 19:09:04 GMT
64
+ < Content-Type: text/html; charset=ISO-8859-1
65
+ <
66
+ Hello World
67
+
68
+ ### Server with virtual hosts
69
+
70
+ require "libevent"
71
+
72
+ Libevent::Builder.new do
73
+
74
+ server "0.0.0.0", 3000 do |http|
75
+
76
+ http.handler do |request|
77
+ case request.get_uri_path
78
+ when '/hello'
79
+ request.send_reply 200, { 'Content->Type' => 'text/plain'}, [ "Hello World" ]
80
+ when '/api'
81
+ request.send_reply 200, { 'Content->Type' => 'application/json'}, [ "{\"version\":\"1.0\"}" ]
82
+ else
83
+ request.send_error 404, "Nothing Found"
84
+ end
85
+ end
86
+
87
+ http.vhost "blog.local" do |host|
88
+ host.handler do |request|
89
+ request.send_reply 200, {}, ["It's blog"]
90
+ end
91
+ end
92
+
93
+ http.vhost "wiki.local" do |host|
94
+ host.handler do |request|
95
+ request.send_reply 200, {}, ["It's wiki"]
96
+ end
97
+ end
98
+
99
+ http.vhost "*.local" do |host|
100
+ host.handler do |request|
101
+ request.send_error 404, "Please use blog.local or wiki.local"
102
+ end
103
+ end
104
+
105
+ end
106
+
107
+ server "0.0.0.0", 3001 do |http|
108
+ http.handler do |request|
109
+ request.send_reply 200, { 'Content->Type' => 'text/plain'}, [ "Hello World 3001" ]
110
+ end
111
+ end
112
+
113
+ signal("INT") do
114
+ base.exit_loop
115
+ end
116
+
117
+ signal("HUP") do
118
+ Kernel.puts "HUP received ..."
119
+ end
120
+
121
+ dispatch
122
+
123
+ end
124
+
125
+ ### Serve Rails application
126
+
127
+ Add to `Gemfile`
128
+
129
+ gem "libevent", :require => false
130
+
131
+ Update gems
132
+
133
+ $ bundle install
134
+
135
+ Run application
136
+
137
+ $ script/rails s Libevent
138
+
139
+ Or via rackup
140
+
141
+ $ bundle exec rackup -s Libevent -p 3000
142
+
143
+ ### Serve Rack application
144
+
145
+ Check rack handler `rack/handler/libevent.rb`
146
+
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/extensiontask'
3
+
4
+ Rake::ExtensionTask.new("libevent_ext")
@@ -0,0 +1,94 @@
1
+ #include "ext.h"
2
+
3
+ static VALUE t_allocate(VALUE klass);
4
+
5
+ static void t_free(Libevent_Base *base);
6
+
7
+ static VALUE t_dispatch(VALUE self);
8
+
9
+ static VALUE t_exit_loop(VALUE self);
10
+
11
+ static VALUE t_break_loop(VALUE self);
12
+
13
+ void Init_libevent_base() {
14
+ cLibevent_Base = rb_define_class_under(mLibevent, "Base", rb_cObject);
15
+
16
+ rb_define_alloc_func(cLibevent_Base, t_allocate);
17
+
18
+ rb_define_method(cLibevent_Base, "dispatch", t_dispatch, 0);
19
+ rb_define_method(cLibevent_Base, "exit_loop", t_exit_loop, 0);
20
+ rb_define_method(cLibevent_Base, "break_loop", t_break_loop, 0);
21
+ }
22
+
23
+ /*
24
+ * Allocate memmory
25
+ */
26
+ static VALUE t_allocate(VALUE klass) {
27
+ Libevent_Base *base;
28
+
29
+ base = ALLOC(Libevent_Base);
30
+ base->ev_base = event_base_new();
31
+
32
+ if ( !base->ev_base ) {
33
+ rb_fatal("Couldn't get an event base");
34
+ }
35
+
36
+ return Data_Wrap_Struct(klass, 0, t_free, base);
37
+ }
38
+
39
+ /*
40
+ * Free memmory
41
+ */
42
+ static void t_free(Libevent_Base *base) {
43
+ event_base_free(base->ev_base);
44
+ }
45
+
46
+ /*
47
+ * Event dispatching loop.
48
+ *
49
+ * This loop will run the event base until either there are no more added events,
50
+ * or until something calls Libevent::Base#break_loop or Base#exit_loop.
51
+ * @see #break_loop
52
+ * @see #exit_loop
53
+ */
54
+ static VALUE t_dispatch(VALUE self) {
55
+ Libevent_Base *base;
56
+ int status;
57
+
58
+ Data_Get_Struct(self, Libevent_Base, base);
59
+ status = event_base_dispatch(base->ev_base);
60
+
61
+ return INT2FIX(status);
62
+ }
63
+
64
+ /*
65
+ * Exit the event loop after the specified time
66
+ *
67
+ * @todo specified time is not implemented
68
+ */
69
+ static VALUE t_exit_loop(VALUE self) {
70
+ Libevent_Base *base;
71
+ int status;
72
+
73
+ Data_Get_Struct(self, Libevent_Base, base);
74
+ status = event_base_loopexit(base->ev_base, NULL);
75
+
76
+ return (status == -1 ? Qfalse : Qtrue);
77
+ }
78
+ /*
79
+ * Abort the active event_base loop immediately.
80
+ *
81
+ * It will abort the loop after the next event is completed;
82
+ * event_base_loopbreak() is typically invoked from this event's callback.
83
+ * This behavior is analogous to the "break;" statement.
84
+ * Subsequent invocations of event_loop() will proceed normally.
85
+ */
86
+ static VALUE t_break_loop(VALUE self) {
87
+ Libevent_Base *base;
88
+ int status;
89
+
90
+ Data_Get_Struct(self, Libevent_Base, base);
91
+ status = event_base_loopbreak(base->ev_base);
92
+
93
+ return (status == -1 ? Qfalse : Qtrue);
94
+ }
@@ -0,0 +1,10 @@
1
+ #include "ext.h"
2
+
3
+ void Init_libevent_ext() {
4
+ mLibevent = rb_define_module("Libevent");
5
+
6
+ Init_libevent_base();
7
+ Init_libevent_signal();
8
+ Init_libevent_http();
9
+ Init_libevent_http_request();
10
+ }
@@ -0,0 +1,40 @@
1
+ #ifndef LIBEVENT_EXT_H
2
+ #define LIBEVENT_EXT_H
3
+
4
+ #include "ruby.h"
5
+ #include "ruby18_compat.h"
6
+
7
+ #include <event.h>
8
+ #include <evhttp.h>
9
+
10
+ VALUE mLibevent;
11
+ VALUE cLibevent_Base;
12
+ VALUE cLibevent_Signal;
13
+ VALUE cLibevent_Http;
14
+ VALUE cLibevent_HttpRequest;
15
+
16
+ typedef struct Libevent_Base {
17
+ struct event_base *ev_base;
18
+ } Libevent_Base;
19
+
20
+ typedef struct Libevent_Signal {
21
+ struct event *ev_event;
22
+ } Libevent_Signal;
23
+
24
+ typedef struct Libevent_Http {
25
+ struct event_base *ev_base;
26
+ struct evhttp *ev_http;
27
+ struct evhttp *ev_http_parent;
28
+ } Libevent_Http;
29
+
30
+ typedef struct Libevent_HttpRequest {
31
+ struct evhttp_request *ev_request;
32
+ struct evbuffer *ev_buffer;
33
+ } Libevent_HttpRequest;
34
+
35
+ void Init_libevent_base();
36
+ void Init_libevent_signal();
37
+ void Init_libevent_http();
38
+ void Init_libevent_http_request();
39
+
40
+ #endif
@@ -0,0 +1,7 @@
1
+ require "mkmf"
2
+
3
+ $CFLAGS << ' -Wall '
4
+
5
+ $LDFLAGS << ' ' << `pkg-config --libs libevent`
6
+
7
+ create_makefile('libevent_ext')
@@ -0,0 +1,184 @@
1
+ #include "ext.h"
2
+
3
+ static VALUE t_allocate(VALUE klass);
4
+
5
+ static void t_free(Libevent_Http *http);
6
+
7
+ static VALUE t_initialize(VALUE self, VALUE object);
8
+
9
+ static VALUE t_bind_socket(VALUE self, VALUE address, VALUE port);
10
+
11
+ static VALUE t_set_request_handler(VALUE self, VALUE handler);
12
+
13
+ static VALUE t_set_timeout(VALUE self, VALUE timeout);
14
+
15
+ static void t_request_handler(struct evhttp_request *ev_request, void *context);
16
+
17
+ static VALUE t_add_virtual_host(VALUE self, VALUE domain, VALUE vhttp);
18
+
19
+ void Init_libevent_http() {
20
+ cLibevent_Http = rb_define_class_under(mLibevent, "Http", rb_cObject);
21
+
22
+ rb_define_alloc_func(cLibevent_Http, t_allocate);
23
+
24
+ rb_define_method(cLibevent_Http, "initialize", t_initialize, 1);
25
+ rb_define_method(cLibevent_Http, "bind_socket", t_bind_socket, 2);
26
+ rb_define_method(cLibevent_Http, "set_request_handler", t_set_request_handler, 1);
27
+ rb_define_method(cLibevent_Http, "set_timeout", t_set_timeout, 1);
28
+ rb_define_method(cLibevent_Http, "add_virtual_host", t_add_virtual_host, 2);
29
+ }
30
+
31
+ /*
32
+ * Allocate memory
33
+ */
34
+ static VALUE t_allocate(VALUE klass) {
35
+ Libevent_Http *http = ALLOC(Libevent_Http);
36
+
37
+ http->ev_base = NULL;
38
+ http->ev_http = NULL;
39
+ http->ev_http_parent = NULL;
40
+
41
+ return Data_Wrap_Struct(klass, 0, t_free, http);
42
+ }
43
+
44
+ /*
45
+ * Free memory
46
+ */
47
+ static void t_free(Libevent_Http *http) {
48
+ if ( http->ev_http ) {
49
+ // main http frees all associated vhosts
50
+ if ( http->ev_http_parent == NULL )
51
+ evhttp_free(http->ev_http);
52
+ }
53
+
54
+ xfree(http);
55
+ }
56
+
57
+ /*
58
+ * Initialize http instance and allocate evhttp structure
59
+ *
60
+ * @param [Base] object
61
+ * @yield [self]
62
+ */
63
+ static VALUE t_initialize(VALUE self, VALUE object) {
64
+ Libevent_Http *http;
65
+ Libevent_Base *base;
66
+
67
+ Data_Get_Struct(self, Libevent_Http, http);
68
+ Data_Get_Struct(object, Libevent_Base, base);
69
+
70
+ http->ev_base = base->ev_base;
71
+ http->ev_http = evhttp_new(http->ev_base);
72
+
73
+ if (!http->ev_http) {
74
+ rb_fatal("Couldn't create evhttp");
75
+ }
76
+
77
+ rb_iv_set(self, "@base", object);
78
+
79
+ if (rb_block_given_p())
80
+ rb_yield(self);
81
+
82
+ return self;
83
+ }
84
+
85
+ /*
86
+ * Binds an HTTP instance on the specified address and port.
87
+ * Can be called multiple times to bind the same http server to multiple different ports.
88
+ * @param [String] address IP address
89
+ * @param [Fixnum] port port to bind
90
+ * @return [true] on success
91
+ * @return [false] on failure
92
+ */
93
+ static VALUE t_bind_socket(VALUE self, VALUE address, VALUE port) {
94
+ Libevent_Http *http;
95
+ int status;
96
+
97
+ Data_Get_Struct(self, Libevent_Http, http);
98
+ Check_Type(address, T_STRING);
99
+ Check_Type(port, T_FIXNUM);
100
+ status = evhttp_bind_socket(http->ev_http, RSTRING_PTR(address), FIX2INT(port));
101
+
102
+ return ( status == -1 ? Qfalse : Qtrue );
103
+ }
104
+
105
+ /*
106
+ * Set a callback for all requests that are not caught by specific callbacks.
107
+ * @note
108
+ * handler should response to :call method.
109
+ *
110
+ * Libevent::HttpRequest instance will be passed to handler as first argument
111
+ *
112
+ * @param [Object] handler object that response to :call
113
+ * @return [nil]
114
+ */
115
+ static VALUE t_set_request_handler(VALUE self, VALUE handler) {
116
+ Libevent_Http *http;
117
+
118
+ Data_Get_Struct(self, Libevent_Http, http);
119
+
120
+ if ( !rb_respond_to(handler, rb_intern("call")))
121
+ rb_raise(rb_eArgError, "handler does not response to call method");
122
+
123
+ rb_iv_set(self, "@request_handler", handler);
124
+ evhttp_set_gencb(http->ev_http, t_request_handler, (void *)handler);
125
+
126
+ return Qnil;
127
+ }
128
+
129
+ /*
130
+ * C callback function that create HttpRequest instance and call Ruby handler object with it.
131
+ */
132
+ static void t_request_handler(struct evhttp_request *ev_request, void* context) {
133
+ Libevent_HttpRequest *le_http_request;
134
+ VALUE http_request;
135
+ VALUE handler = (VALUE)context;
136
+
137
+ http_request = rb_obj_alloc(cLibevent_HttpRequest);
138
+ Data_Get_Struct(http_request, Libevent_HttpRequest, le_http_request);
139
+ le_http_request->ev_request = ev_request;
140
+ rb_obj_call_init(http_request, 0, 0);
141
+
142
+ rb_funcall(handler, rb_intern("call"), 1, http_request);
143
+ }
144
+
145
+ /*
146
+ * Set the timeout for an HTTP request.
147
+ * @param [Fixnum] timeout he timeout, in seconds
148
+ * return nil
149
+ */
150
+ static VALUE t_set_timeout(VALUE self, VALUE timeout) {
151
+ Libevent_Http *http;
152
+
153
+ Data_Get_Struct(self, Libevent_Http, http);
154
+ evhttp_set_timeout(http->ev_http, NUM2INT(timeout));
155
+
156
+ return Qnil;
157
+ }
158
+
159
+ /*
160
+ * Adds a virtual host to the http server.
161
+ * It is possible to have hierarchical vhosts.
162
+ * @param [String] domain the glob pattern against which the hostname is matched. The match is case insensitive and follows otherwise regular shell matching.
163
+ * @param [Http] vhttp the virtual host to add the regular http server
164
+ * @return [true false]
165
+ * @example
166
+ * A vhost with the pattern *.example.com may have other vhosts with patterns foo.example.com and bar.example.com associated with it.
167
+ * @note
168
+ * A virtual host is a newly initialized evhttp object that has request handler
169
+ * It most not have any listing sockets associated with it.
170
+ */
171
+ static VALUE t_add_virtual_host(VALUE self, VALUE domain, VALUE vhttp) {
172
+ Libevent_Http *le_http;
173
+ Libevent_Http *le_vhttp;
174
+ int status;
175
+
176
+ Data_Get_Struct(self, Libevent_Http, le_http);
177
+ Data_Get_Struct(vhttp, Libevent_Http, le_vhttp);
178
+ Check_Type(domain, T_STRING);
179
+ le_vhttp->ev_http_parent = le_http->ev_http;
180
+ status = evhttp_add_virtual_host(le_http->ev_http, RSTRING_PTR(domain), le_vhttp->ev_http);
181
+
182
+ return ( status == -1 ? Qfalse : Qtrue );
183
+ }
184
+