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 +6 -0
- data/Gemfile +5 -0
- data/README.md +146 -0
- data/Rakefile +4 -0
- data/ext/libevent_ext/base.c +94 -0
- data/ext/libevent_ext/ext.c +10 -0
- data/ext/libevent_ext/ext.h +40 -0
- data/ext/libevent_ext/extconf.rb +7 -0
- data/ext/libevent_ext/http.c +184 -0
- data/ext/libevent_ext/http_request.c +466 -0
- data/ext/libevent_ext/ruby18_compat.h +14 -0
- data/ext/libevent_ext/signal.c +107 -0
- data/lib/libevent.rb +9 -0
- data/lib/libevent/base.rb +18 -0
- data/lib/libevent/builder.rb +32 -0
- data/lib/libevent/http.rb +23 -0
- data/lib/libevent/http_request.rb +4 -0
- data/lib/libevent/signal.rb +6 -0
- data/lib/libevent/version.rb +3 -0
- data/lib/rack/handler/libevent.rb +97 -0
- data/libevent.gemspec +22 -0
- data/samples/rack_request_inspect +28 -0
- data/samples/simple.rb +22 -0
- data/samples/virtual_hosts +63 -0
- metadata +70 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
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,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,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,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
|
+
|