3 HTTP server implementation for LuCI - helper class
4 (c) 2008 Freifunk Leipzig / 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
16 module("luci.httpd.server", package.seeall)
18 require("socket.http")
24 VHost = luci.util.class()
26 function VHost.__init__(self, handler)
27 self.handler = handler
31 function VHost.process(self, request, sourcein, sinkerr, ...)
32 local handler = self.handler
34 local uri = request.env.REQUEST_URI:match("^([^?]*)")
37 request.env.SCRIPT_NAME = ""
40 request.env.PATH_INFO = uri
42 for k, dhandler in pairs(self.dhandler) do
43 if k == uri or k.."/" == uri:sub(1, #k+1) then
45 request.env.SCRIPT_NAME = k
46 request.env.PATH_INFO = uri:sub(#k+1)
52 return handler:process(request, sourcein, sinkerr, ...)
57 function VHost.set_default_handler(self, handler)
58 self.handler = handler
62 function VHost.set_handler(self, match, handler)
63 self.dhandler[match] = handler
68 Server = luci.util.class()
70 function Server.__init__(self, host)
75 function Server.set_default_vhost(self, vhost)
80 function Server.set_vhost(self, name, vhost)
81 self.vhosts[name] = vhost
84 function Server.create_daemon_handlers(self)
85 return function(...) return self:process(...) end,
86 function(...) return self:error_overload(...) end
90 function Server.error(self, socket, code, msg)
91 hcode = tostring(code)
93 socket:send( "HTTP/1.0 " .. hcode .. " " ..
94 luci.http.protocol.statusmsg[code] .. "\r\n" )
95 socket:send( "Connection: close\r\n" )
96 socket:send( "Content-Type: text/plain\r\n\r\n" )
99 socket:send( "HTTP-Error " .. code .. ": " .. msg .. "\r\n" )
103 function Server.error_overload(self, socket)
104 self:error(socket, 503, "Too many simultaneous connections")
108 function Server.process( self, thread )
110 -- Setup sockets and sources
111 local client = thread.socket
113 client:settimeout( 0 )
115 local sourcein = ltn12.source.empty()
116 local sourcehdr = luci.http.protocol.header_source( thread )
117 local sinkerr = ltn12.sink.file( io.stderr )
121 local reading = { client }
127 message, err = luci.http.protocol.parse_message_header( sourcehdr )
130 self:error( client, 400, err )
135 if message.http_version == 1.1 then
136 close = (message.env.HTTP_CONNECTION == "close")
138 close = not message.env.HTTP_CONNECTION or message.env.HTTP_CONNECTION == "close"
141 if message.request_method == "get" or message.request_method == "head" then
144 elseif message.request_method == "post" then
145 -- If we have a HTTP/1.1 client and an Expect: 100-continue header then
146 -- respond with HTTP 100 Continue message
147 if message.http_version == 1.1 and message.headers['Expect'] and
148 message.headers['Expect'] == '100-continue'
150 client:send("HTTP/1.1 100 Continue\r\n\r\n")
153 if message.headers['Transfer-Encoding'] and
154 message.headers['Transfer-Encoding'] ~= "identity" then
155 sourcein = socket.source("http-chunked", thread)
156 elseif message.env.CONTENT_LENGTH then
157 sourcein = socket.source("by-length", thread,
158 tonumber(message.env.CONTENT_LENGTH))
160 self:error( client, 411, luci.http.protocol.statusmsg[411] )
165 self:error( client, 405, luci.http.protocol.statusmsg[405] )
171 local host = self.vhosts[message.env.HTTP_HOST] or self.host
173 self:error( client, 500, "Unable to find matching host" )
177 local response, sourceout = host:process(
178 message, sourcein, sinkerr,
182 self:error( client, 500, "Error processing handler" )
185 -- Post process response
186 local sinkmode = close and "close-when-done" or "keep-open"
189 if not response.headers["Content-Length"] then
190 if message.http_version == 1.1 then
191 response.headers["Transfer-Encoding"] = "chunked"
192 sinkmode = "http-chunked"
195 sinkmode = "close-when-done"
201 response.headers["Connection"] = "close"
205 local sinkout = socket.sink(sinkmode, client)
208 message.env.SERVER_PROTOCOL .. " " ..
209 tostring(response.status) .. " " ..
210 luci.http.protocol.statusmsg[response.status] .. "\r\n"
213 for k,v in pairs(response.headers) do
214 header = header .. k .. ": " .. v .. "\r\n"
217 client:send(header .. "\r\n")
223 eof = not ltn12.pump.step(sourceout, sinkout)