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.
@@ -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