Software /
code /
prosody
Comparison
net/http/server.lua @ 4631:fc5d3b053454
net.http.{server|codes|parser}: Initial commit.
author | Waqas Hussain <waqas20@gmail.com> |
---|---|
date | Sun, 08 Apr 2012 04:09:33 +0500 |
child | 4633:92e1a538f8b0 |
comparison
equal
deleted
inserted
replaced
4630:9502c0224caf | 4631:fc5d3b053454 |
---|---|
1 | |
2 local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat; | |
3 local parser_new = require "net.http.parser".new; | |
4 local events = require "util.events".new(); | |
5 local addserver = require "net.server".addserver; | |
6 local log = require "util.logger".init("http.server"); | |
7 local os_date = os.date; | |
8 local pairs = pairs; | |
9 local s_upper = string.upper; | |
10 local setmetatable = setmetatable; | |
11 local xpcall = xpcall; | |
12 local debug = debug; | |
13 local tostring = tostring; | |
14 local codes = require "net.http.codes"; | |
15 local _G = _G; | |
16 | |
17 local _M = {}; | |
18 | |
19 local sessions = {}; | |
20 local handlers = {}; | |
21 | |
22 local listener = {}; | |
23 | |
24 local handle_request; | |
25 local _1, _2, _3; | |
26 local function _handle_request() return handle_request(_1, _2, _3); end | |
27 local function _traceback_handler(err) log("error", "Traceback[http]: %s: %s", tostring(err), debug.traceback()); end | |
28 | |
29 function listener.onconnect(conn) | |
30 local secure = conn:ssl() and true or nil; | |
31 local pending = {}; | |
32 local waiting = false; | |
33 local function process_next(last_response) | |
34 --if waiting then log("debug", "can't process_next, waiting"); return; end | |
35 if sessions[conn] and #pending > 0 then | |
36 local request = t_remove(pending); | |
37 --log("debug", "process_next: %s", request.path); | |
38 waiting = true; | |
39 --handle_request(conn, request, process_next); | |
40 _1, _2, _3 = conn, request, process_next; | |
41 if not xpcall(_handle_request, _traceback_handler) then | |
42 conn:write("HTTP/1.0 503 Internal Server Error\r\n\r\nAn error occured during the processing of this request."); | |
43 conn:close(); | |
44 end | |
45 else | |
46 --log("debug", "ready for more"); | |
47 waiting = false; | |
48 end | |
49 end | |
50 local function success_cb(request) | |
51 --log("debug", "success_cb: %s", request.path); | |
52 request.secure = secure; | |
53 t_insert(pending, request); | |
54 if not waiting then | |
55 process_next(); | |
56 end | |
57 end | |
58 local function error_cb(err) | |
59 log("debug", "error_cb: %s", err or "<nil>"); | |
60 -- FIXME don't close immediately, wait until we process current stuff | |
61 -- FIXME if err, send off a bad-request response | |
62 sessions[conn] = nil; | |
63 conn:close(); | |
64 end | |
65 sessions[conn] = parser_new(success_cb, error_cb); | |
66 end | |
67 | |
68 function listener.ondisconnect(conn) | |
69 sessions[conn] = nil; | |
70 end | |
71 | |
72 function listener.onincoming(conn, data) | |
73 sessions[conn]:feed(data); | |
74 end | |
75 | |
76 local headerfix = setmetatable({}, { | |
77 __index = function(t, k) | |
78 local v = "\r\n"..k:gsub("_", "-"):gsub("%f[%w].", s_upper)..": "; | |
79 t[k] = v; | |
80 return v; | |
81 end | |
82 }); | |
83 | |
84 function _M.hijack_response(response, listener) | |
85 error("TODO"); | |
86 end | |
87 function handle_request(conn, request, finish_cb) | |
88 --log("debug", "handler: %s", request.path); | |
89 local headers = {}; | |
90 for k,v in pairs(request.headers) do headers[k:gsub("-", "_")] = v; end | |
91 request.headers = headers; | |
92 request.conn = conn; | |
93 | |
94 local date_header = os_date('!%a, %d %b %Y %H:%M:%S GMT'); -- FIXME use | |
95 local conn_header = request.headers.connection; | |
96 local keep_alive = conn_header == "Keep-Alive" or (request.httpversion == "1.1" and conn_header ~= "close"); | |
97 | |
98 local response = { | |
99 request = request; | |
100 status_code = 200; | |
101 headers = { date = date_header, connection = (keep_alive and "Keep-Alive" or "close") }; | |
102 conn = conn; | |
103 send = _M.send_response; | |
104 finish_cb = finish_cb; | |
105 }; | |
106 | |
107 if not request.headers.host then | |
108 response.status_code = 400; | |
109 response.headers.content_type = "text/html"; | |
110 response:send("<html><head>400 Bad Request</head><body>400 Bad Request: No Host header.</body></html>"); | |
111 else | |
112 -- TODO call handler | |
113 --response.headers.content_type = "text/plain"; | |
114 --response:send("host="..(request.headers.host or "").."\npath="..request.path.."\n"..(request.body or "")); | |
115 local host = request.headers.host; | |
116 if host then | |
117 host = host:match("[^:]*"):lower(); | |
118 local event = request.method.." "..host..request.path:match("[^?]*"); | |
119 local payload = { request = request, response = response }; | |
120 --[[repeat | |
121 if events.fire_event(event, payload) ~= nil then return; end | |
122 event = (event:sub(-1) == "/") and event:sub(1, -1) or event:gsub("[^/]*$", ""); | |
123 if event:sub(-1) == "/" then | |
124 event = event:sub(1, -1); | |
125 else | |
126 event = event:gsub("[^/]*$", ""); | |
127 end | |
128 until not event:find("/", 1, true);]] | |
129 --log("debug", "Event: %s", event); | |
130 if events.fire_event(event, payload) ~= nil then return; end | |
131 -- TODO try adding/stripping / at the end, but this needs to work via an HTTP redirect | |
132 end | |
133 | |
134 -- if handler not called, fallback to legacy httpserver handlers | |
135 _M.legacy_handler(request, response); | |
136 end | |
137 end | |
138 function _M.send_response(response, body) | |
139 local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]); | |
140 local headers = response.headers; | |
141 body = body or ""; | |
142 headers.content_length = #body; | |
143 | |
144 local output = { status_line }; | |
145 for k,v in pairs(headers) do | |
146 t_insert(output, headerfix[k]..v); | |
147 end | |
148 t_insert(output, "\r\n\r\n"); | |
149 t_insert(output, body); | |
150 | |
151 response.conn:write(t_concat(output)); | |
152 if headers.connection == "Keep-Alive" then | |
153 response:finish_cb(); | |
154 else | |
155 response.conn:close(); | |
156 end | |
157 end | |
158 function _M.legacy_handler(request, response) | |
159 log("debug", "Invoking legacy handler"); | |
160 local base = request.path:match("^/([^/?]+)"); | |
161 local legacy_server = _G.httpserver and _G.httpserver.new.http_servers[5280]; | |
162 local handler = legacy_server and legacy_server.handlers[base]; | |
163 if not handler then handler = _G.httpserver and _G.httpserver.set_default_handler.default_handler; end | |
164 if handler then | |
165 -- add legacy properties to request object | |
166 request.url = { path = request.path }; | |
167 request.handler = response.conn; | |
168 request.id = tostring{}:match("%x+$"); | |
169 local headers = {}; | |
170 for k,v in pairs(request.headers) do | |
171 headers[k:gsub("_", "-")] = v; | |
172 end | |
173 request.headers = headers; | |
174 function request:send(resp) | |
175 if self.destroyed then return; end | |
176 if resp.body or resp.headers then | |
177 if resp.headers then | |
178 for k,v in pairs(resp.headers) do response.headers[k] = v; end | |
179 end | |
180 response:send(resp.body) | |
181 else | |
182 response:send(resp) | |
183 end | |
184 self.sent = true; | |
185 self:destroy(); | |
186 end | |
187 function request:destroy() | |
188 if self.destroyed then return; end | |
189 if not self.sent then return self:send(""); end | |
190 self.destroyed = true; | |
191 if self.on_destroy then | |
192 log("debug", "Request has destroy callback"); | |
193 self:on_destroy(); | |
194 else | |
195 log("debug", "Request has no destroy callback"); | |
196 end | |
197 end | |
198 local r = handler(request.method, request.body, request); | |
199 if r ~= true then | |
200 request:send(r); | |
201 end | |
202 else | |
203 log("debug", "No handler found"); | |
204 response.status_code = 404; | |
205 response.headers.content_type = "text/html"; | |
206 response:send("<html><head>404 Not Found</head><body>404 Not Found: No such page.</body></html>"); | |
207 end | |
208 end | |
209 | |
210 function _M.add_handler(event, handler, priority) | |
211 events.add_handler(event, handler, priority); | |
212 end | |
213 function _M.remove_handler(event, handler) | |
214 events.remove_handler(event, handler); | |
215 end | |
216 | |
217 function _M.listen_on(port, interface, ssl) | |
218 addserver(interface or "*", port, listener, "*a", ssl); | |
219 end | |
220 | |
221 _M.listener = listener; | |
222 _M.codes = codes; | |
223 return _M; |