Cleaned up writing. Began work on Ebb::Input.
[ebb.git] / ruby_binding / ebb.rb
blob944c4c76bfb62bbba4aeffdf5e9a21058d9d9598
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 $: << File.expand_path(File.dirname(__FILE__))
5 require 'ebb_ext'
7 module Ebb
8   class Client
9     attr_reader :env
10     
11     def env
12       @env.update(
13         'rack.input' => Input.new(self)
14       )
15     end
16   end
17   
18   class Input
19     def initialize(client)
20       @client = client
21     end
22     
23     def read(len = 1)
24       @client.read_input(len)
25     end
26     
27     def gets
28       raise NotImplementedError
29     end
30     
31     def each
32       raise NotImplementedError
33     end
34   end
35   
36   class Server
37     def self.run(app, options={})
38       # port must be an integer
39       server = self.new(app, options)
40       yield server if block_given?
41       server.start
42     end
43     
44     def initialize(app, options={})
45       @host = options[:Host] || '0.0.0.0'
46       @port = (options[:Port] || 4001).to_i
47       @app = app
48       init(@host, @port)
49     end
50     
51     # Called by the C library on each request.
52     # env is a hash containing all the variables of the request
53     # client is a TCPSocket
54     def process_client(client)
55       status, headers, body = @app.call(client.env)
56       
57       client.write "HTTP/1.1 %d %s\r\n" % [status, HTTP_STATUS_CODES[status]]
58       
59       if body.respond_to? :length and status != 304
60         client.write "Connection: close\r\n"
61         headers['Content-Length'] = body.length
62       end
63       
64       headers.each { |k, v| client.write "#{k}: #{v}\r\n" }
65       client.write "\r\n"
66       if body.kind_of?(String)
67         client.write body
68       elsif body.kind_of?(StringIO)
69         client.write body.string
70       else
71         # Not many apps use this yet so i'll hold off on streaming
72         # responses until the rest of ebb is more developed
73         # Yes, I know streaming responses are very cool.
74         raise NotImplementedError, "Unsupported body of type #{body.class}"
75       end
76     end
77     
78     def start
79       trap('INT')  { puts "got INT"; stop }
80       trap('TERM') { puts "got TERM"; stop }
81       _start
82       
83       while process_connections
84         unless @waiting_clients.empty?
85           if $debug
86             puts "#{@waiting_clients.length} waiting clients" if @waiting_clients.length > 1
87           end
88           client = @waiting_clients.shift
89           process_client(client)
90           client.start_writing
91         end
92       end
93     end
94   end
95   
96   HTTP_STATUS_CODES = {  
97     100  => 'Continue', 
98     101  => 'Switching Protocols', 
99     200  => 'OK', 
100     201  => 'Created', 
101     202  => 'Accepted', 
102     203  => 'Non-Authoritative Information', 
103     204  => 'No Content', 
104     205  => 'Reset Content', 
105     206  => 'Partial Content', 
106     300  => 'Multiple Choices', 
107     301  => 'Moved Permanently', 
108     302  => 'Moved Temporarily', 
109     303  => 'See Other', 
110     304  => 'Not Modified', 
111     305  => 'Use Proxy', 
112     400  => 'Bad Request', 
113     401  => 'Unauthorized', 
114     402  => 'Payment Required', 
115     403  => 'Forbidden', 
116     404  => 'Not Found', 
117     405  => 'Method Not Allowed', 
118     406  => 'Not Acceptable', 
119     407  => 'Proxy Authentication Required', 
120     408  => 'Request Time-out', 
121     409  => 'Conflict', 
122     410  => 'Gone', 
123     411  => 'Length Required', 
124     412  => 'Precondition Failed', 
125     413  => 'Request Entity Too Large', 
126     414  => 'Request-URI Too Large', 
127     415  => 'Unsupported Media Type', 
128     500  => 'Internal Server Error', 
129     501  => 'Not Implemented', 
130     502  => 'Bad Gateway', 
131     503  => 'Service Unavailable', 
132     504  => 'Gateway Time-out', 
133     505  => 'HTTP Version not supported'
134   }