libevent 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ #ifndef RUBY_19
2
+ #ifndef STRING_PTR
3
+ #define STRING_PTR(v) (RSTRING(v)->ptr)
4
+ #endif
5
+ #ifndef RFLOAT_VALUE
6
+ #define RFLOAT_VALUE(v) (RFLOAT(v)->value)
7
+ #endif
8
+ #ifndef RARRAY_LEN
9
+ #define RARRAY_LEN(v) (RARRAY(v)->len)
10
+ #endif
11
+ #ifndef RARRAY_PTR
12
+ #define RARRAY_PTR(v) (RARRAY(v)->ptr)
13
+ #endif
14
+ #endif
@@ -0,0 +1,107 @@
1
+ #include "ext.h"
2
+
3
+ static VALUE t_allocate(VALUE klass);
4
+
5
+ static void t_free(Libevent_Signal *signal);
6
+
7
+ static VALUE t_initialize(VALUE self, VALUE base, VALUE name, VALUE handler);
8
+
9
+ static VALUE t_destroy(VALUE self);
10
+
11
+ static void t_handler(evutil_socket_t signal_number, short events, void *context);
12
+
13
+ void Init_libevent_signal() {
14
+ cLibevent_Signal = rb_define_class_under(mLibevent, "Signal", rb_cObject);
15
+
16
+ rb_define_alloc_func(cLibevent_Signal, t_allocate);
17
+
18
+ rb_define_method(cLibevent_Signal, "initialize", t_initialize, 3);
19
+ rb_define_method(cLibevent_Signal, "destroy", t_destroy, 0);
20
+ }
21
+
22
+ /*
23
+ * Allocate memory
24
+ */
25
+ static VALUE t_allocate(VALUE klass) {
26
+ Libevent_Signal *signal;
27
+
28
+ signal = ALLOC(Libevent_Signal);
29
+ return Data_Wrap_Struct(klass, 0, t_free, signal);
30
+ }
31
+
32
+ /*
33
+ * Free memory
34
+ */
35
+ static void t_free(Libevent_Signal *signal) {
36
+ if ( signal->ev_event ) {
37
+ event_free(signal->ev_event);
38
+ }
39
+
40
+ xfree(signal);
41
+ }
42
+
43
+ /*
44
+ * Create and add signal to specified event base with handler block
45
+ *
46
+ * @note method allocates memory for <b>struct event </b>
47
+ * that will be freed when object will be freed by ruby' GC
48
+ *
49
+ * @param [Base] base event base instance
50
+ * @param [String] name a name of signal
51
+ * @param [Object] handler object that perform signal handling. Any object that responds to :call method
52
+ *
53
+ */
54
+ static VALUE t_initialize(VALUE self, VALUE base, VALUE name, VALUE handler) {
55
+ Libevent_Signal *le_signal;
56
+ Libevent_Base *le_base;
57
+ VALUE signal_list;
58
+ VALUE signal_number;
59
+
60
+ Data_Get_Struct(self, Libevent_Signal, le_signal);
61
+ Data_Get_Struct(base, Libevent_Base, le_base);
62
+
63
+ // check name
64
+ signal_list = rb_funcall( rb_const_get(rb_cObject, rb_intern("Signal")), rb_intern("list"), 0);
65
+ signal_number = rb_hash_aref(signal_list, name);
66
+ if ( signal_number == Qnil )
67
+ rb_raise(rb_eArgError, "unknown signal name given");
68
+ rb_iv_set(self, "@name", name);
69
+
70
+ // check handler
71
+ if ( !rb_respond_to(handler, rb_intern("call")))
72
+ rb_raise(rb_eArgError, "handler does not response to call method");
73
+ rb_iv_set(self, "@handler", handler);
74
+
75
+ // create signal event
76
+ le_signal->ev_event = evsignal_new(le_base->ev_base, FIX2INT(signal_number), t_handler, (void *)handler);
77
+ if ( !le_signal->ev_event )
78
+ rb_fatal("Could not create a signal event");
79
+ if ( event_add(le_signal->ev_event, NULL) < 0 )
80
+ rb_fatal("Could not add a signal event");
81
+
82
+ return self;
83
+ }
84
+
85
+ /*
86
+ * Delete signal from event base
87
+ * @return [true] on success
88
+ * @return [false] on failure
89
+ */
90
+ static VALUE t_destroy(VALUE self) {
91
+ Libevent_Signal *le_signal;
92
+ int status;
93
+
94
+ Data_Get_Struct(self, Libevent_Signal, le_signal);
95
+ status = event_del(le_signal->ev_event);
96
+
97
+ return( status == -1 ? Qfalse : Qtrue);
98
+ }
99
+
100
+ /*
101
+ * C callback function that invokes call method on Ruby object.
102
+ */
103
+ static void t_handler(evutil_socket_t signal_number, short events, void *context) {
104
+ VALUE handler = (VALUE)context;
105
+
106
+ rb_funcall(handler, rb_intern("call"), 0);
107
+ }
data/lib/libevent.rb ADDED
@@ -0,0 +1,9 @@
1
+ require "libevent/version"
2
+
3
+ require "libevent_ext"
4
+
5
+ require "libevent/base"
6
+ require "libevent/signal"
7
+ require "libevent/http"
8
+ require "libevent/http_request"
9
+ require "libevent/builder"
@@ -0,0 +1,18 @@
1
+ module Libevent
2
+ class Base
3
+ # Create new event base
4
+ def initialize
5
+ @signals = []
6
+ end
7
+
8
+ attr_reader :signals
9
+
10
+ # Create new signal with handler as block and add signal to event base
11
+ #
12
+ # @param [String] name of signal
13
+ def trap_signal(name, &block)
14
+ @signals << Signal.new(self, name, block)
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ module Libevent
2
+ class Builder
3
+ def initialize(options = {}, &block)
4
+ @base = Base.new
5
+ instance_eval(&block) if block_given?
6
+ end
7
+
8
+ attr_reader :base
9
+
10
+ # Create new Http instance, bind socket and options yield http object
11
+ # @param [String] host
12
+ # @param [Fixnum] port
13
+ # @return [Http] instance
14
+ def server(host, port, &block)
15
+ http = Http.new(@base)
16
+ http.bind_socket(host, port) or raise RuntimeError, "can't bind socket #{host}:#{port}"
17
+ yield(http) if block_given?
18
+ http
19
+ end
20
+
21
+ # Trap signal using event base
22
+ # @param [String] name a signal name
23
+ def signal(name, &block)
24
+ base.trap_signal(name, &block)
25
+ end
26
+
27
+ # Start event base loop
28
+ def dispatch
29
+ base.dispatch
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,23 @@
1
+ module Libevent
2
+ class Http
3
+
4
+ attr_reader :base
5
+
6
+ # Create virtual http server
7
+ # @param [String] domain a domain for virtual server
8
+ # @return [Http] http instance
9
+ def vhost(domain)
10
+ http = self.class.new(self.base)
11
+ add_virtual_host(domain, http)
12
+ yield(http) if block_given?
13
+ http
14
+ end
15
+
16
+ # Set request handler for current http instance
17
+ # @param block
18
+ def handler(&block)
19
+ set_request_handler(block)
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,4 @@
1
+ module Libevent
2
+ class HttpRequest
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module Libevent
2
+ class Signal
3
+ attr_reader :name
4
+ end
5
+ end
6
+
@@ -0,0 +1,3 @@
1
+ module Libevent
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,97 @@
1
+ require "libevent"
2
+ require "stringio"
3
+
4
+ module Rack
5
+ module Handler
6
+ class Libevent
7
+
8
+ def self.run(app, options)
9
+ server = new(app, options)
10
+ server.start
11
+ end
12
+
13
+ def self.valid_options
14
+ {
15
+ "timeout=TIMEOUT" => "Set the timeout for an HTTP request"
16
+ }
17
+ end
18
+
19
+ def initialize(app, options)
20
+ @app = app
21
+
22
+ options[:Host] or raise ArgumentError, "Host option required"
23
+ options[:Port] or raise ArgumentError, "Port option required"
24
+
25
+ @host = options[:Host]
26
+ @port = options[:Port].to_i
27
+
28
+ @base = ::Libevent::Base.new
29
+ @http = ::Libevent::Http.new(@base)
30
+
31
+ @http.set_timeout(options[:timeout].to_i) if options[:timeout]
32
+ end
33
+
34
+ def start
35
+ @http.bind_socket(@host, @port) or raise RuntimeError, "Can't bind to #{@host}:#{@port}"
36
+ @http.set_request_handler(self.method(:process))
37
+
38
+ @base.trap_signal("INT") { self.stop }
39
+ @base.trap_signal("TERM") { self.stop }
40
+
41
+ @base.dispatch
42
+ end
43
+
44
+ def stop
45
+ @base.exit_loop
46
+ end
47
+
48
+ protected
49
+
50
+ def process(request)
51
+ env = {}
52
+
53
+ env['REQUEST_METHOD'] = request.get_command
54
+ env['SCRIPT_NAME'] = ''
55
+ env['REQUEST_PATH'] = '/'
56
+ env['PATH_INFO'] = request.get_uri_path || '/'
57
+ env['QUERY_STRING'] = request.get_uri_query || ''
58
+ env['SERVER_NAME'] = request.get_host || @host
59
+ env['SERVER_PORT'] = @port.to_s
60
+ env['SERVER_SOFTWARE'] = "libevent/#{::Libevent::VERSION}"
61
+ env['SERVER_PROTOCOL'] = 'HTTP/1.1'
62
+ env['REMOTE_ADDR'] = request.get_remote_host
63
+ env['HTTP_VERSION'] = request.get_http_version
64
+ env['rack.version'] = [1, 1]
65
+ env['rack.url_scheme'] = request.get_uri_scheme || 'http'
66
+ env['rack.input'] = StringIO.new(request.get_body)
67
+ env['rack.errors'] = STDERR
68
+ env['rack.multithread'] = false
69
+ env['rack.multiprocess'] = false
70
+ env['rack.run_once'] = false
71
+
72
+ request.get_input_headers.each do |key, val|
73
+ env_key = ""
74
+ env_key << "HTTP_" unless key =~ /^content(_|-)(type|length)$/i
75
+ env_key << key
76
+ env_key.gsub!('-','_')
77
+ env_key.upcase!
78
+ env[env_key] = val
79
+ end
80
+
81
+ code, headers, body = @app.call(env)
82
+
83
+ begin
84
+ headers.each do |key, values|
85
+ values.split("\n").each { |value| request.add_output_header(key, value) }
86
+ end
87
+ request.send_reply_start(code, nil)
88
+ body.each { |chunk| request.send_reply_chunk(chunk) }
89
+ request.send_reply_end
90
+ ensure
91
+ body.close if body.respond_to?(:close)
92
+ end
93
+ end
94
+ end
95
+
96
+ end
97
+ end
data/libevent.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "libevent/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "libevent"
7
+ s.version = Libevent::VERSION
8
+ s.authors = ["Andriy Yanko"]
9
+ s.email = ["andriy.yanko@gmail.com"]
10
+ s.homepage = "https://github.com/ayanko/libevent"
11
+ s.summary = %q{C extension for libevent}
12
+ s.description = %q{C extension for libevent}
13
+
14
+ s.rubyforge_project = "libevent"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.extensions = ["ext/libevent_ext/extconf.rb"]
22
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path('../../lib', __FILE__)
4
+
5
+ require "libevent"
6
+ require "rack/handler/libevent"
7
+
8
+ app = lambda { |env|
9
+ body = []
10
+ body << "RACK_ENVIRONMENT:\n"
11
+ env.sort.each { |pair| body << "%-20s : %s\n" % pair }
12
+ body << "RACK_INPUT_START"
13
+ body << env['rack.input'].read
14
+ body << "RACK_INPUT_END\n"
15
+
16
+ headers = {}
17
+ headers['Content-Type'] = 'text/plain'
18
+ headers['Content-Length'] = body.inject(0) { |sum, chunk| sum += chunk.size }.to_s
19
+ headers['Set-Cookie'] = "one=first\nsecond=two"
20
+
21
+ [ 200, headers, body ]
22
+ }
23
+
24
+ Rack::Handler::Libevent.run(app, {
25
+ :Host => "0.0.0.0",
26
+ :Port => 3000,
27
+ :timeout => 60,
28
+ })
data/samples/simple.rb ADDED
@@ -0,0 +1,22 @@
1
+ require "libevent"
2
+
3
+ # create event base
4
+ base = Libevent::Base.new
5
+
6
+ # create http server instance
7
+ http = Libevent::Http.new(base)
8
+
9
+ # bind socket
10
+ http.bind_socket("0.0.0.0", 15015)
11
+
12
+ # set handler
13
+ http.handler do |request|
14
+ request.send_reply(200, {}, ["Hello World\n"])
15
+ end
16
+
17
+ # catch SIGINT
18
+ base.trap_signal("INT") { base.exit_loop }
19
+
20
+ # start libevent loop
21
+ base.dispatch
22
+
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path('../../lib', __FILE__)
4
+
5
+ require "libevent"
6
+
7
+ # curl -v -H 'Host: blog.local' http://localhost:3001/hello
8
+ # curl -v -H 'Host: blog.local' http://localhost:3001/api
9
+ # curl -v -H 'Host: wiki.local' http://localhost:3001
10
+ # curl -v -H 'Host: any.local' http://localhost:3001
11
+
12
+ Libevent::Builder.new do
13
+
14
+ server "0.0.0.0", 3000 do |http|
15
+
16
+ http.handler do |request|
17
+ case request.get_uri_path
18
+ when '/hello'
19
+ request.send_reply 200, { 'Content->Type' => 'text/plain'}, [ "Hello World" ]
20
+ when '/api'
21
+ request.send_reply 200, { 'Content->Type' => 'application/json'}, [ "{\"version\":\"1.0\"}" ]
22
+ else
23
+ request.send_error 404, "Nothing Found"
24
+ end
25
+ end
26
+
27
+ http.vhost "blog.local" do |host|
28
+ host.handler do |request|
29
+ request.send_reply 200, {}, ["It's blog"]
30
+ end
31
+ end
32
+
33
+ http.vhost "wiki.local" do |host|
34
+ host.handler do |request|
35
+ request.send_reply 200, {}, ["It's wiki"]
36
+ end
37
+ end
38
+
39
+ http.vhost "*.local" do |host|
40
+ host.handler do |request|
41
+ request.send_error 404, "Please use blog.local or wiki.local"
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ server "0.0.0.0", 3001 do |http|
48
+ http.handler do |request|
49
+ request.send_reply 200, { 'Content->Type' => 'text/plain'}, [ "Hello World 4000" ]
50
+ end
51
+ end
52
+
53
+ signal("INT") do
54
+ base.exit_loop
55
+ end
56
+
57
+ signal("HUP") do
58
+ Kernel.puts "HUP received ..."
59
+ end
60
+
61
+ dispatch
62
+
63
+ end