ebb 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/libev/ev_win32.c ADDED
@@ -0,0 +1,117 @@
1
+ /*
2
+ * libev win32 compatibility cruft
3
+ *
4
+ * Copyright (c) 2007 Marc Alexander Lehmann <libev@schmorp.de>
5
+ * All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions are
9
+ * met:
10
+ *
11
+ * * Redistributions of source code must retain the above copyright
12
+ * notice, this list of conditions and the following disclaimer.
13
+ *
14
+ * * Redistributions in binary form must reproduce the above
15
+ * copyright notice, this list of conditions and the following
16
+ * disclaimer in the documentation and/or other materials provided
17
+ * with the distribution.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+ */
31
+
32
+ #ifdef _WIN32
33
+
34
+ #include <sys/timeb.h>
35
+
36
+ /* note: the comment below could not be substantiated, but what would I care */
37
+ /* MSDN says this is required to handle SIGFPE */
38
+ volatile double SIGFPE_REQ = 0.0f;
39
+
40
+ /* oh, the humanity! */
41
+ static int
42
+ ev_pipe (int filedes [2])
43
+ {
44
+ struct sockaddr_in addr = { 0 };
45
+ int addr_size = sizeof (addr);
46
+ SOCKET listener;
47
+ SOCKET sock [2] = { -1, -1 };
48
+
49
+ if ((listener = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
50
+ return -1;
51
+
52
+ addr.sin_family = AF_INET;
53
+ addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
54
+ addr.sin_port = 0;
55
+
56
+ if (bind (listener, (struct sockaddr *)&addr, addr_size))
57
+ goto fail;
58
+
59
+ if (getsockname(listener, (struct sockaddr *)&addr, &addr_size))
60
+ goto fail;
61
+
62
+ if (listen (listener, 1))
63
+ goto fail;
64
+
65
+ if ((sock [0] = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
66
+ goto fail;
67
+
68
+ if (connect (sock[0], (struct sockaddr *)&addr, addr_size))
69
+ goto fail;
70
+
71
+ if ((sock[1] = accept (listener, 0, 0)) < 0)
72
+ goto fail;
73
+
74
+ closesocket (listener);
75
+
76
+ #if EV_SELECT_IS_WINSOCKET
77
+ filedes [0] = _open_osfhandle (sock [0], 0);
78
+ filedes [1] = _open_osfhandle (sock [1], 0);
79
+ #else
80
+ /* when select isn't winsocket, we also expect socket, connect, accept etc.
81
+ * to work on fds */
82
+ filedes [0] = sock [0];
83
+ filedes [1] = sock [1];
84
+ #endif
85
+
86
+ return 0;
87
+
88
+ fail:
89
+ closesocket (listener);
90
+
91
+ if (sock [0] != INVALID_SOCKET) closesocket (sock [0]);
92
+ if (sock [1] != INVALID_SOCKET) closesocket (sock [1]);
93
+
94
+ return -1;
95
+ }
96
+
97
+ #undef pipe
98
+ #define pipe(filedes) ev_pipe (filedes)
99
+
100
+ static int
101
+ ev_gettimeofday (struct timeval *tv, struct timezone *tz)
102
+ {
103
+ struct _timeb tb;
104
+
105
+ _ftime (&tb);
106
+
107
+ tv->tv_sec = (long)tb.time;
108
+ tv->tv_usec = ((long)tb.millitm) * 1000;
109
+
110
+ return 0;
111
+ }
112
+
113
+ #undef gettimeofday
114
+ #define gettimeofday(tv,tz) ev_gettimeofday (tv, tz)
115
+
116
+ #endif
117
+
data/libev/ev_wrap.h ADDED
@@ -0,0 +1,132 @@
1
+ /* DO NOT EDIT, automatically generated by update_ev_wrap */
2
+ #ifndef EV_WRAP_H
3
+ #define EV_WRAP_H
4
+ #define now_floor ((loop)->now_floor)
5
+ #define mn_now ((loop)->mn_now)
6
+ #define rtmn_diff ((loop)->rtmn_diff)
7
+ #define io_blocktime ((loop)->io_blocktime)
8
+ #define timeout_blocktime ((loop)->timeout_blocktime)
9
+ #define backend ((loop)->backend)
10
+ #define activecnt ((loop)->activecnt)
11
+ #define loop_count ((loop)->loop_count)
12
+ #define backend_fd ((loop)->backend_fd)
13
+ #define backend_fudge ((loop)->backend_fudge)
14
+ #define backend_modify ((loop)->backend_modify)
15
+ #define backend_poll ((loop)->backend_poll)
16
+ #define curpid ((loop)->curpid)
17
+ #define postfork ((loop)->postfork)
18
+ #define vec_ri ((loop)->vec_ri)
19
+ #define vec_ro ((loop)->vec_ro)
20
+ #define vec_wi ((loop)->vec_wi)
21
+ #define vec_wo ((loop)->vec_wo)
22
+ #define vec_max ((loop)->vec_max)
23
+ #define polls ((loop)->polls)
24
+ #define pollmax ((loop)->pollmax)
25
+ #define pollcnt ((loop)->pollcnt)
26
+ #define pollidxs ((loop)->pollidxs)
27
+ #define pollidxmax ((loop)->pollidxmax)
28
+ #define epoll_events ((loop)->epoll_events)
29
+ #define epoll_eventmax ((loop)->epoll_eventmax)
30
+ #define kqueue_changes ((loop)->kqueue_changes)
31
+ #define kqueue_changemax ((loop)->kqueue_changemax)
32
+ #define kqueue_changecnt ((loop)->kqueue_changecnt)
33
+ #define kqueue_events ((loop)->kqueue_events)
34
+ #define kqueue_eventmax ((loop)->kqueue_eventmax)
35
+ #define port_events ((loop)->port_events)
36
+ #define port_eventmax ((loop)->port_eventmax)
37
+ #define anfds ((loop)->anfds)
38
+ #define anfdmax ((loop)->anfdmax)
39
+ #define pendings ((loop)->pendings)
40
+ #define pendingmax ((loop)->pendingmax)
41
+ #define pendingcnt ((loop)->pendingcnt)
42
+ #define fdchanges ((loop)->fdchanges)
43
+ #define fdchangemax ((loop)->fdchangemax)
44
+ #define fdchangecnt ((loop)->fdchangecnt)
45
+ #define timers ((loop)->timers)
46
+ #define timermax ((loop)->timermax)
47
+ #define timercnt ((loop)->timercnt)
48
+ #define periodics ((loop)->periodics)
49
+ #define periodicmax ((loop)->periodicmax)
50
+ #define periodiccnt ((loop)->periodiccnt)
51
+ #define idles ((loop)->idles)
52
+ #define idlemax ((loop)->idlemax)
53
+ #define idlecnt ((loop)->idlecnt)
54
+ #define idleall ((loop)->idleall)
55
+ #define prepares ((loop)->prepares)
56
+ #define preparemax ((loop)->preparemax)
57
+ #define preparecnt ((loop)->preparecnt)
58
+ #define checks ((loop)->checks)
59
+ #define checkmax ((loop)->checkmax)
60
+ #define checkcnt ((loop)->checkcnt)
61
+ #define forks ((loop)->forks)
62
+ #define forkmax ((loop)->forkmax)
63
+ #define forkcnt ((loop)->forkcnt)
64
+ #define fs_fd ((loop)->fs_fd)
65
+ #define fs_w ((loop)->fs_w)
66
+ #define fs_hash ((loop)->fs_hash)
67
+ #else
68
+ #undef EV_WRAP_H
69
+ #undef now_floor
70
+ #undef mn_now
71
+ #undef rtmn_diff
72
+ #undef io_blocktime
73
+ #undef timeout_blocktime
74
+ #undef backend
75
+ #undef activecnt
76
+ #undef loop_count
77
+ #undef backend_fd
78
+ #undef backend_fudge
79
+ #undef backend_modify
80
+ #undef backend_poll
81
+ #undef curpid
82
+ #undef postfork
83
+ #undef vec_ri
84
+ #undef vec_ro
85
+ #undef vec_wi
86
+ #undef vec_wo
87
+ #undef vec_max
88
+ #undef polls
89
+ #undef pollmax
90
+ #undef pollcnt
91
+ #undef pollidxs
92
+ #undef pollidxmax
93
+ #undef epoll_events
94
+ #undef epoll_eventmax
95
+ #undef kqueue_changes
96
+ #undef kqueue_changemax
97
+ #undef kqueue_changecnt
98
+ #undef kqueue_events
99
+ #undef kqueue_eventmax
100
+ #undef port_events
101
+ #undef port_eventmax
102
+ #undef anfds
103
+ #undef anfdmax
104
+ #undef pendings
105
+ #undef pendingmax
106
+ #undef pendingcnt
107
+ #undef fdchanges
108
+ #undef fdchangemax
109
+ #undef fdchangecnt
110
+ #undef timers
111
+ #undef timermax
112
+ #undef timercnt
113
+ #undef periodics
114
+ #undef periodicmax
115
+ #undef periodiccnt
116
+ #undef idles
117
+ #undef idlemax
118
+ #undef idlecnt
119
+ #undef idleall
120
+ #undef prepares
121
+ #undef preparemax
122
+ #undef preparecnt
123
+ #undef checks
124
+ #undef checkmax
125
+ #undef checkcnt
126
+ #undef forks
127
+ #undef forkmax
128
+ #undef forkcnt
129
+ #undef fs_fd
130
+ #undef fs_w
131
+ #undef fs_hash
132
+ #endif
@@ -0,0 +1,134 @@
1
+ # Copyright (c) 2007 Marc-André Cournoyer
2
+ require 'etc'
3
+
4
+ module Kernel
5
+ unless respond_to? :daemonize # Already part of Ruby 1.9, yeah!
6
+ # Turns the current script into a daemon process that detaches from the console.
7
+ # It can be shut down with a TERM signal. Taken from ActiveSupport.
8
+ def daemonize
9
+ exit if fork # Parent exits, child continues.
10
+ Process.setsid # Become session leader.
11
+ exit if fork # Zap session leader. See [1].
12
+ Dir.chdir "/" # Release old working directory.
13
+ File.umask 0000 # Ensure sensible umask. Adjust as needed.
14
+ STDIN.reopen "/dev/null" # Free file descriptors and
15
+ STDOUT.reopen "/dev/null", "a" # point them somewhere sensible.
16
+ STDERR.reopen STDOUT # STDOUT/ERR should better go to a logfile.
17
+ trap("TERM") { exit }
18
+ end
19
+ end
20
+ end
21
+
22
+ module Process
23
+ # Returns +true+ the process identied by +pid+ is running.
24
+ def running?(pid)
25
+ Process.getpgid(pid) != -1
26
+ rescue Errno::ESRCH
27
+ false
28
+ end
29
+ module_function :running?
30
+ end
31
+
32
+ # Module included in classes that can be turned into a daemon.
33
+ # Handle stuff like:
34
+ # * storing the PID in a file
35
+ # * redirecting output to the log file
36
+ # * changing processs privileges
37
+ # * killing the process gracefully
38
+ module Daemonizable
39
+ attr_accessor :pid_file, :log_file
40
+
41
+ def self.included(base)
42
+ base.extend ClassMethods
43
+ end
44
+
45
+ def daemonizable_init(options)
46
+ pid_file = options[:pid_file]
47
+ log_file = options[:log_file]
48
+ if options[:daemonize]
49
+ change_privilege options[:user], options[:group] if options[:user] && options[:group]
50
+ daemonize
51
+ end
52
+ end
53
+
54
+ def pid
55
+ File.exist?(pid_file) ? open(pid_file).read : nil
56
+ end
57
+
58
+ # Turns the current script into a daemon process that detaches from the console.
59
+ def daemonize
60
+ raise ArgumentError, 'You must specify a pid_file to deamonize' unless @pid_file
61
+
62
+ pwd = Dir.pwd # Current directory is changed during daemonization, so store it
63
+ super # Calls Kernel#daemonize
64
+ Dir.chdir pwd
65
+
66
+ trap('HUP', 'IGNORE') # Don't die upon logout
67
+
68
+ # Redirect output to the logfile
69
+ [STDOUT, STDERR].each { |f| f.reopen @log_file, 'a' } if @log_file
70
+
71
+ write_pid_file
72
+ at_exit do
73
+ log ">> Exiting!"
74
+ remove_pid_file
75
+ end
76
+ end
77
+
78
+ # Change privileges of the process
79
+ # to the specified user and group.
80
+ def change_privilege(user, group=user)
81
+ log ">> Changing process privilege to #{user}:#{group}"
82
+
83
+ uid, gid = Process.euid, Process.egid
84
+ target_uid = Etc.getpwnam(user).uid
85
+ target_gid = Etc.getgrnam(group).gid
86
+
87
+ if uid != target_uid || gid != target_gid
88
+ # Change process ownership
89
+ Process.initgroups(user, target_gid)
90
+ Process::GID.change_privilege(target_gid)
91
+ Process::UID.change_privilege(target_uid)
92
+ end
93
+ rescue Errno::EPERM => e
94
+ log "Couldn't change user and group to #{user}:#{group}: #{e}"
95
+ end
96
+
97
+ module ClassMethods
98
+ # Kill the process which PID is stored in +pid_file+.
99
+ def kill(pid_file, timeout=60)
100
+ if pid = open(pid_file).read
101
+ pid = pid.to_i
102
+ print "Sending INT signal to process #{pid} ... "
103
+ begin
104
+ Process.kill('INT', pid)
105
+ Timeout.timeout(timeout) do
106
+ sleep 0.1 while Process.running?(pid)
107
+ end
108
+ rescue Timeout::Error
109
+ print "timeout, Sending KILL signal ... "
110
+ Process.kill('KILL', pid)
111
+ end
112
+ puts "stopped!"
113
+ else
114
+ puts "Can't stop process, no PID found in #{@pid_file}"
115
+ end
116
+ rescue Errno::ESRCH # No such process
117
+ puts "process not found!"
118
+ ensure
119
+ File.delete(pid_file) rescue nil
120
+ end
121
+ end
122
+
123
+ private
124
+ def remove_pid_file
125
+ File.delete(@pid_file) if @pid_file && File.exists?(@pid_file)
126
+ end
127
+
128
+ def write_pid_file
129
+ log ">> Writing PID to #{@pid_file}"
130
+ FileUtils.mkdir_p File.dirname(@pid_file)
131
+ open(@pid_file,"w") { |f| f.write(Process.pid) }
132
+ File.chmod(0644, @pid_file)
133
+ end
134
+ end
data/ruby_lib/ebb.rb ADDED
@@ -0,0 +1,179 @@
1
+ # A ruby binding to the ebb web server
2
+ # Copyright (c) 2007 Ry Dahl <ry.d4hl@gmail.com>
3
+ # This software is released under the "MIT License". See README file for details.
4
+ module Ebb
5
+ LIBDIR = File.dirname(__FILE__)
6
+ VERSION = File.read(LIBDIR + "/../VERSION").gsub(/\s/,'')
7
+ end
8
+
9
+ require Ebb::LIBDIR + '/../src/ebb_ext'
10
+ require Ebb::LIBDIR + '/daemonizable'
11
+
12
+ module Ebb
13
+ class Client
14
+ BASE_ENV = {
15
+ 'SCRIPT_NAME' => '',
16
+ 'SERVER_SOFTWARE' => "Ebb #{Ebb::VERSION}",
17
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
18
+ 'GATEWAY_INTERFACE' => 'CGI/1.2',
19
+ 'rack.version' => [0, 1],
20
+ 'rack.errors' => STDERR,
21
+ 'rack.url_scheme' => 'http',
22
+ 'rack.multithread' => false,
23
+ 'rack.multiprocess' => false,
24
+ 'rack.run_once' => false
25
+ }.freeze
26
+
27
+ def env
28
+ @env ||= begin
29
+ env = FFI::client_env(self).update(BASE_ENV)
30
+ env['rack.input'] = RequestBody.new(self)
31
+ env
32
+ end
33
+ end
34
+
35
+ def finished
36
+ FFI::client_finished(self)
37
+ end
38
+
39
+ def write(data)
40
+ FFI::client_write(self, data)
41
+ end
42
+ end
43
+
44
+ class RequestBody
45
+ def initialize(client)
46
+ @client = client
47
+ end
48
+
49
+ def read(len)
50
+ FFI::client_read_input(@client, len)
51
+ end
52
+
53
+ def gets
54
+ raise NotImplementedError
55
+ end
56
+
57
+ def each
58
+ raise NotImplementedError
59
+ end
60
+ end
61
+
62
+ class Server
63
+ include Daemonizable
64
+ def self.run(app, options={})
65
+ # port must be an integer
66
+ server = self.new(app, options)
67
+ yield server if block_given?
68
+ server.start
69
+ end
70
+
71
+ def initialize(app, options={})
72
+ @socket = options[:socket]
73
+ @port = (options[:port] || 4001).to_i
74
+ @timeout = options[:timeout]
75
+ @app = app
76
+
77
+ daemonizable_init(options)
78
+ FFI::server_initialize(self)
79
+ end
80
+
81
+ def start
82
+ trap('INT') { @running = false }
83
+
84
+ if @socket
85
+ raise NotImplemented
86
+ FFI::server_listen_on_socket(self, @socket) or raise "Problem listening on socket #{@socket}"
87
+ else
88
+ FFI::server_listen_on_port(self, @port) or raise "Problem listening on port #{@port}"
89
+ end
90
+ @waiting_clients = []
91
+
92
+ puts "Ebb listening at http://0.0.0.0:#{@port}/"
93
+
94
+ @running = true
95
+ while FFI::server_process_connections(self) and @running
96
+ unless @waiting_clients.empty?
97
+ if $DEBUG and @waiting_clients.length > 1
98
+ puts "#{@waiting_clients.length} waiting clients"
99
+ end
100
+ client = @waiting_clients.shift
101
+ process_client(client)
102
+ end
103
+ end
104
+ puts "Ebb unlistening"
105
+ FFI::server_unlisten(self)
106
+ end
107
+
108
+ def process_client(client)
109
+ #puts "Request: #{client.env.inspect}\n"
110
+ begin
111
+ status, headers, body = @app.call(client.env)
112
+ rescue
113
+ raise if $DEBUG
114
+ status = 500
115
+ headers = {'Content-Type' => 'text/plain'}
116
+ body = "Internal Server Error\n"
117
+ end
118
+
119
+ client.write "HTTP/1.1 %d %s\r\n" % [status, HTTP_STATUS_CODES[status]]
120
+
121
+ if body.respond_to? :length and status != 304
122
+ client.write "Connection: close\r\n"
123
+ headers['Content-Length'] = body.length
124
+ end
125
+
126
+ headers.each { |k, v| client.write "#{k}: #{v}\r\n" }
127
+ client.write "\r\n"
128
+
129
+ # Not many apps use streaming yet so i'll hold off on that feature
130
+ # until the rest of ebb is more developed.
131
+ if body.kind_of?(String)
132
+ client.write body
133
+ else
134
+ body.each { |p| client.write p }
135
+ end
136
+ client.finished
137
+ end
138
+ end
139
+
140
+ HTTP_STATUS_CODES = {
141
+ 100 => 'Continue',
142
+ 101 => 'Switching Protocols',
143
+ 200 => 'OK',
144
+ 201 => 'Created',
145
+ 202 => 'Accepted',
146
+ 203 => 'Non-Authoritative Information',
147
+ 204 => 'No Content',
148
+ 205 => 'Reset Content',
149
+ 206 => 'Partial Content',
150
+ 300 => 'Multiple Choices',
151
+ 301 => 'Moved Permanently',
152
+ 302 => 'Moved Temporarily',
153
+ 303 => 'See Other',
154
+ 304 => 'Not Modified',
155
+ 305 => 'Use Proxy',
156
+ 400 => 'Bad Request',
157
+ 401 => 'Unauthorized',
158
+ 402 => 'Payment Required',
159
+ 403 => 'Forbidden',
160
+ 404 => 'Not Found',
161
+ 405 => 'Method Not Allowed',
162
+ 406 => 'Not Acceptable',
163
+ 407 => 'Proxy Authentication Required',
164
+ 408 => 'Request Time-out',
165
+ 409 => 'Conflict',
166
+ 410 => 'Gone',
167
+ 411 => 'Length Required',
168
+ 412 => 'Precondition Failed',
169
+ 413 => 'Request Entity Too Large',
170
+ 414 => 'Request-URI Too Large',
171
+ 415 => 'Unsupported Media Type',
172
+ 500 => 'Internal Server Error',
173
+ 501 => 'Not Implemented',
174
+ 502 => 'Bad Gateway',
175
+ 503 => 'Service Unavailable',
176
+ 504 => 'Gateway Time-out',
177
+ 505 => 'HTTP Version not supported'
178
+ }.freeze
179
+ end