Ignore SIGPIPE. (Wow that suddenly makes things usable!)
[ebb.git] / ruby_binding / ebb.rb
blob27065bdd142caee6c044ab5aaf69b15dd6e05880
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.
5 require 'ebb_ext'
7 module Ebb
8   class Server
9     def self.run(app, options={})
10       # port must be an integer
11       server = self.new(app, options)
12       yield server if block_given?
13       server.start
14     end
15     
16     def initialize(app, options={})
17       @host = options[:Host] || '0.0.0.0'
18       @port = (options[:Port] || 4001).to_i
19       @app = app
20     end
21     
22     # Called by the C library on each request.
23     # env is a hash containing all the variables of the request
24     # client is a TCPSocket
25     # XXX: push this code to C?
26     def process_client(client)
27       status, headers, body = @app.call(client.env)
28             
29       client.write("HTTP/1.1 %d %s\r\n" % [status, HTTP_STATUS_CODES[status]])
30       
31       if body.respond_to? :length and status != 304
32         client.write("Connection: close\r\n")
33         headers['Content-Length'] = body.length
34       end
35       
36       headers.each { |k, v| client.write("#{k}: #{v}\r\n") }
37       client.write("\r\n")
38       body.each { |part| client.write(part) }
39     ensure
40       body.close if body and body.respond_to? :close
41       client.close
42     end
43     
44     # TODO:
45     # problem here. this should be a nonblocking call that starts the server
46     # in a seperate thread. We don't want to sit in libev's loop and ignore
47     # all the ruby related things going on.
48     # Better to start a new thread (do i do this in ebb_ext.c?)
49     def start
50       trap('INT')  { puts "got INT"; stop }
51       trap('TERM') { puts "got TERM"; stop }
52       
53       start_listening
54       while process_connections
55         unless @waiting_clients.empty?
56           client = @waiting_clients.shift
57           process_client(client)
58         end
59       end
60     end
61   end
62   
63   HTTP_STATUS_CODES = {  
64     100  => 'Continue', 
65     101  => 'Switching Protocols', 
66     200  => 'OK', 
67     201  => 'Created', 
68     202  => 'Accepted', 
69     203  => 'Non-Authoritative Information', 
70     204  => 'No Content', 
71     205  => 'Reset Content', 
72     206  => 'Partial Content', 
73     300  => 'Multiple Choices', 
74     301  => 'Moved Permanently', 
75     302  => 'Moved Temporarily', 
76     303  => 'See Other', 
77     304  => 'Not Modified', 
78     305  => 'Use Proxy', 
79     400  => 'Bad Request', 
80     401  => 'Unauthorized', 
81     402  => 'Payment Required', 
82     403  => 'Forbidden', 
83     404  => 'Not Found', 
84     405  => 'Method Not Allowed', 
85     406  => 'Not Acceptable', 
86     407  => 'Proxy Authentication Required', 
87     408  => 'Request Time-out', 
88     409  => 'Conflict', 
89     410  => 'Gone', 
90     411  => 'Length Required', 
91     412  => 'Precondition Failed', 
92     413  => 'Request Entity Too Large', 
93     414  => 'Request-URI Too Large', 
94     415  => 'Unsupported Media Type', 
95     500  => 'Internal Server Error', 
96     501  => 'Not Implemented', 
97     502  => 'Bad Gateway', 
98     503  => 'Service Unavailable', 
99     504  => 'Gateway Time-out', 
100     505  => 'HTTP Version not supported'
101   }