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; |