3 (c) 2008 Steven Barth <steven@midlink.org>
4 (c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
10 http://www.apache.org/licenses/LICENSE-2.0
15 local ipairs, pairs = ipairs, pairs
16 local tostring, tonumber = tostring, tonumber
17 local pcall, assert = pcall, assert
19 local os = require "os"
20 local io = require "io"
21 local uci = require "luci.model.uci"
22 local util = require "luci.util"
23 local ltn12 = require "luci.ltn12"
24 local proto = require "luci.http.protocol"
25 local string = require "string"
26 local date = require "luci.http.protocol.date"
28 module "luci.ttpd.server"
35 IOResource = util.class()
37 function IOResource.__init__(self, fd, offset, len)
38 self.fd, self.offset, self.len = fd, offset, len
44 function VHost.__init__(self, handler)
45 self.handler = handler
49 function VHost.process(self, request, sourcein, sinkerr, ...)
50 local handler = self.handler
52 local uri = request.env.REQUEST_URI:match("^([^?]*)")
55 request.env.SCRIPT_NAME = ""
58 request.env.PATH_INFO = uri
60 for k, dhandler in pairs(self.dhandler) do
61 if k == uri or k.."/" == uri:sub(1, #k+1) then
63 request.env.SCRIPT_NAME = k
64 request.env.PATH_INFO = uri:sub(#k+1)
70 return handler:process(request, sourcein, sinkerr, ...)
74 function VHost.get_default_handler(self)
78 function VHost.set_default_handler(self, handler)
79 self.handler = handler
82 function VHost.get_handlers(self)
86 function VHost.set_handler(self, match, handler)
87 self.dhandler[match] = handler
94 function Server.__init__(self, host)
95 self.uci = uci.cursor()
103 function Server.get_default_vhost(self)
107 function Server.set_default_vhost(self, vhost)
111 function Server.get_vhosts(self)
115 function Server.set_vhost(self, name, vhost)
116 self.vhosts[name] = vhost
119 function Server.flush(self)
120 if #self.wbuf > 0 then
121 self._write(self.wbuf)
126 function Server.read(self, len)
127 while #self.rbuf < len do
128 self.rbuf = self.rbuf .. self._read(len - #self.rbuf)
131 local chunk = self.rbuf:sub(1, len)
132 self.rbuf = self.rbuf:sub(len + 1)
136 function Server.limitsource(self, limit)
143 local read = (limit > BUFSIZE) and BUFSIZE or limit
145 return self:read(read)
150 -- Adapted from Luaposix
151 function Server.receiveheaders(self)
152 local line, name, value, err
155 line, err = self:readline()
156 if err then return nil, err end
157 -- headers go until a blank line is found
159 -- get field-name and value
160 _, _, name, value = line:find("^(.-):%s*(.*)")
161 if not (name and value) then return nil, "malformed reponse headers" end
163 -- get next line (value might be folded)
164 line, err = self:readline()
165 if err then return nil, err end
166 -- unfold any folded values
167 while line:find("^%s") do
168 value = value .. line
169 line = self:readline()
170 if err then return nil, err end
172 -- save pair in table
173 if headers[name] then headers[name] = headers[name] .. ", " .. value
174 else headers[name] = value end
179 function Server.readchunk(self)
180 -- get chunk size, skip extention
181 local line, err = self:readline()
182 if err then return nil, err end
183 local size = tonumber(line:gsub(";.*", ""), 16)
184 if not size then return nil, "invalid chunk size" end
185 -- was it the last chunk?
187 -- if not, get chunk and skip terminating CRLF
188 local chunk, err, part = self:read(size)
189 if chunk then self:readline() end
192 -- if it was, read trailers into headers table
193 headers, err = self:receiveheaders()
194 if not headers then return nil, err end
198 function Server.readline(self)
199 if #self.rbuf < 1 then
200 self.rbuf = self._read(BUFSIZE)
204 local le = self.rbuf:find("\r\n", nil, true)
206 if le == 1 then -- EoH
207 self.rbuf = self.rbuf:sub(le + 2)
210 local line = self.rbuf:sub(1, le - 1)
211 self.rbuf = self.rbuf:sub(le + 2)
215 if #self.rbuf >= BUFSIZE then
216 return nil, "Invalid Request"
218 self.rbuf = self.rbuf .. self._read(BUFSIZE-#self.rbuf)
223 function Server.sink(self)
224 return function(chunk, err)
228 local stat, err = pcall(self.write, self, chunk)
240 function Server.chunksink(self)
241 return function(chunk, err)
242 local stat, err = pcall(self.writechunk, self, chunk)
251 function Server.writechunk(self, chunk, err)
253 if not chunk then return self._write("0\r\n\r\n") end
254 local size = string.format("%X\r\n", #chunk)
255 return self._write(size .. chunk .. "\r\n")
258 function Server.write(self, chunk)
260 local missing = BUFSIZE - #self.wbuf
261 self.wbuf = self.wbuf .. chunk:sub(1, missing)
262 chunk = chunk:sub(missing + 1)
263 if #self.wbuf == BUFSIZE then
264 assert(self._write(self.wbuf))
270 function Server.close(self)
275 function Server.sendfile(self, fd, offset, len)
277 self._sendfile(fd, offset, len)
281 function Server.error(self, code, msg)
282 hcode = tostring(code)
284 self:write( "HTTP/1.0 " .. hcode .. " " ..
285 proto.statusmsg[code] .. "\r\n" )
286 self:write( "Connection: close\r\n" )
287 self:write( "Content-Type: text/plain\r\n\r\n" )
290 self:write( "HTTP-Error " .. code .. ": " .. msg .. "\r\n" )
295 function Server.process(self, functions)
296 util.update(self, functions)
298 local sourcein = ltn12.source.empty()
299 local sourcehdr = function() return self:readline() or "" end
300 local sinkerr = ltn12.sink.file( io.stderr )
301 local sinkout = self:sink()
304 local stat, message, err
308 stat, message, err = pcall(proto.parse_message_header, sourcehdr)
310 -- remote socket closed
311 if not stat and message == 0 then
316 if not stat and message == 11 then
322 if not stat or not message then
328 if message.http_version == 1.1 then
329 close = (message.env.HTTP_CONNECTION == "close")
331 close = not message.env.HTTP_CONNECTION or message.env.HTTP_CONNECTION == "close"
333 -- Uncomment this to disable keep-alive
334 close = not (self.uci:get("lucittpd", "lucittpd", "keepalive") == "1")
336 if message.request_method == "get" or message.request_method == "head" then
339 elseif message.request_method == "post" then
340 -- If we have a HTTP/1.1 client and an Expect: 100-continue header then
341 -- respond with HTTP 100 Continue message
342 if message.http_version == 1.1 and message.headers['Expect'] and
343 message.headers['Expect'] == '100-continue'
345 self:write("HTTP/1.1 100 Continue\r\n\r\n")
348 if message.headers['Transfer-Encoding'] and
349 message.headers['Transfer-Encoding'] ~= "identity" then
350 sourcein = function() return self:readchunk() end
351 elseif message.env.CONTENT_LENGTH then
352 sourcein = self:limitsource(
353 tonumber(message.env.CONTENT_LENGTH)
356 self:error( 411, proto.statusmsg[411] )
360 self:error( 405, proto.statusmsg[405] )
366 local host = self.vhosts[message.env.HTTP_HOST] or self.host
368 self:error( 500, "Unable to find matching host" )
372 local response, sourceout = host:process(
373 message, sourcein, sinkerr,
377 self:error( 500, "Error processing handler" )
380 -- Post process response
382 if util.instanceof(sourceout, IOResource) then
383 if not response.headers["Content-Length"] then
384 response.headers["Content-Length"] = sourceout.len
387 if not response.headers["Content-Length"] then
388 if message.http_version == 1.1 then
389 response.headers["Transfer-Encoding"] = "chunked"
390 sinkout = self:chunksink()
395 elseif message.request_method ~= "head" then
396 response.headers["Content-Length"] = 0
400 response.headers["Connection"] = "close"
403 response.headers["Date"] = date.to_http(os.time())
406 message.env.SERVER_PROTOCOL .. " " ..
407 tostring(response.status) .. " " ..
408 proto.statusmsg[response.status] .. "\r\n"
410 header = header .. "Server: LuCIttpd/" .. tostring(VERSION) .. "\r\n"
413 for k,v in pairs(response.headers) do
414 header = header .. k .. ": " .. v .. "\r\n"
418 local stat, err = pcall(function()
419 self:write(header .. "\r\n")
422 if util.instanceof(sourceout, IOResource) then
423 self:sendfile(sourceout.fd, sourceout.offset, sourceout.len)
425 ltn12.pump.all(sourceout, sinkout)
435 -- Remote end closed the socket, so do we
437 io.stderr:write("Error sending data: " .. err .. "\n")