libevent 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.
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
+