-
Notifications
You must be signed in to change notification settings - Fork 0
/
tube.rb
177 lines (144 loc) · 3.46 KB
/
tube.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
require 'socket'
require 'http/parser'
require 'stringio'
require 'thread'
require 'eventmachine'
class Tube
def initialize(port, app)
@server = TCPServer.new(port)
@app = app
end
def prefork(workers)
workers.times do
fork do
puts "Forked #{Process.pid}"
start
end
end
Process.waitall
end
def start
loop do
socket = @server.accept
Thread.new do
connection = Connection.new(socket, @app)
connection.process
end
end
end
def start_em
EM.run do
EM.start_server "localhost", 3000, EMConnection do |connection|
connection.app = @app
end
end
end
class Connection
def initialize(socket, app)
@socket = socket
@app = app
@parser = Http::Parser.new(self)
end
def process
until @socket.closed? || @socket.eof?
data = @socket.readpartial(1024)
@parser << data
puts data
end
end
def on_message_complete
puts "#{@parser.http_method} #{@parser.request_url}"
puts " " + @parser.headers.inspect
puts
env = {}
@parser.headers.each_pair do |name, value|
name = "HTTP_" + name.upcase.tr("-", "_")
env[name] = value
end
env["PATH_INFO"] = @parser.request_url
env["REQUEST_METHOD"] = @parser.http_method
env["rack.input"] = StringIO.new
send_response env
end
REASONS = {
200 => "OK",
404 => "Not Found"
}
def send_response(env)
status, headers, body = @app.call(env)
reason = REASONS[status]
@socket.write "HTTP/1.1 #{status} #{reason}\r\n"
headers.each_pair do |name, value|
@socket.write "#{name}: #{value}\r\n"
end
@socket.write "\r\n"
body.each do |chunk|
@socket.write chunk
end
body.close if body.respond_to? :close
close
end
def close
@socket.close
end
end #Connection
class EMConnection < EM::Connection
attr_accessor :app
def post_init
@parser = Http::Parser.new(self)
end
def receive_data(data)
@parser << data
end
def on_message_complete
puts "#{@parser.http_method} #{@parser.request_url}"
puts " " + @parser.headers.inspect
puts
env = {}
@parser.headers.each_pair do |name, value|
name = "HTTP_" + name.upcase.tr("-", "_")
env[name] = value
end
env["PATH_INFO"] = @parser.request_url
env["REQUEST_METHOD"] = @parser.http_method
env["rack.input"] = StringIO.new
send_response env
end
REASONS = {
200 => "OK",
404 => "Not Found"
}
def send_response(env)
status, headers, body = @app.call(env)
reason = REASONS[status]
send_data "HTTP/1.1 #{status} #{reason}\r\n"
headers.each_pair do |name, value|
send_data "#{name}: #{value}\r\n"
end
send_data "\r\n"
body.each do |chunk|
send_data chunk
end
body.close if body.respond_to? :close
close_connection_after_writing
end
end #Connection
class Builder
attr_reader :app
def run(app)
@app = app
end
def self.parse_file(file)
content = File.read(file)
builder = self.new
builder.instance_eval(content)
builder.app
end
end #Builder
end
app = Tube::Builder.parse_file("config.ru")
server = Tube.new(3000, app)
puts "Plugging tube into port 3000"
# server.prefork 3
# server.start
server.start_em