modules/freifunk: set ipv4 broadcast to 255.255.255.255 by default
[project/luci.git] / libs / httpd / luasrc / httpd / server.lua
1 --[[
2
3 HTTP server implementation for LuCI - helper class
4 (c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
5 (c) 2008 Steven Barth <steven@midlink.org>
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11         http://www.apache.org/licenses/LICENSE-2.0
12
13 $Id$
14
15 ]]--
16
17 module("luci.httpd.server", package.seeall)
18 require("socket")
19 require("socket.http")
20 require("luci.util")
21
22 READ_BUFSIZE = 1024
23 VERSION = 0.2
24
25
26 VHost = luci.util.class()
27
28 function VHost.__init__(self, handler)
29         self.handler = handler
30         self.dhandler = {}
31 end
32
33 function VHost.process(self, request, sourcein, sinkerr, ...)
34         local handler = self.handler
35
36         local uri = request.env.REQUEST_URI:match("^([^?]*)")
37
38         -- SCRIPT_NAME
39         request.env.SCRIPT_NAME = ""
40
41         -- Call URI part
42         request.env.PATH_INFO = uri
43
44         for k, dhandler in pairs(self.dhandler) do
45                 if k == uri or k.."/" == uri:sub(1, #k+1) then
46                         handler = dhandler
47                         request.env.SCRIPT_NAME = k
48                         request.env.PATH_INFO   = uri:sub(#k+1)
49                         break;
50                 end
51         end
52
53         if handler then
54                 return handler:process(request, sourcein, sinkerr, ...)
55         end
56 end
57
58
59 function VHost.set_default_handler(self, handler)
60         self.handler = handler
61 end
62
63
64 function VHost.set_handler(self, match, handler)
65         self.dhandler[match] = handler
66 end
67
68
69
70 Server = luci.util.class()
71
72 function Server.__init__(self, host)
73         self.host = host
74         self.vhosts = {}
75 end
76
77 function Server.set_default_vhost(self, vhost)
78         self.host = vhost
79 end
80
81 -- Sets a vhost
82 function Server.set_vhost(self, name, vhost)
83         self.vhosts[name] = vhost
84 end
85
86 function Server.create_daemon_handlers(self)
87         return function(...) return self:process(...) end,
88                 function(...) return self:error_overload(...) end
89 end
90
91
92 function Server.error(self, socket, code, msg)
93         hcode = tostring(code)
94         
95         socket:send( "HTTP/1.0 " .. hcode .. " " ..
96          luci.http.protocol.statusmsg[code] .. "\r\n" )
97         socket:send( "Connection: close\r\n" )
98         socket:send( "Content-Type: text/plain\r\n\r\n" )
99
100         if msg then
101                 socket:send( "HTTP-Error " .. code .. ": " .. msg .. "\r\n" )
102         end
103 end
104
105 function Server.error_overload(self, socket)
106         self:error(socket, 503, "Too many simultaneous connections")
107 end
108
109
110 function Server.process( self, client )
111
112         -- Setup sockets and sources
113         local thread = {
114                 receive = function(self, ...) return luci.httpd.corecv(client, ...) end,
115                 send = function(self, ...) return luci.httpd.cosend(client, ...) end,
116                 close = function(self, ...) return client:close(...) end,
117                 getfd = function(self, ...) return client:getfd(...) end,
118                 dirty = function(self, ...) return client:dirty(...) end
119         }
120         
121         client:settimeout( 0 )
122         
123         local sourcein  = ltn12.source.empty()
124         local sourcehdr = luci.http.protocol.header_source( thread )
125         local sinkerr   = ltn12.sink.file( io.stderr )
126         
127         local close = false
128         
129         local reading = { client }
130
131         local message, err
132         
133         repeat
134                 -- parse headers
135                 message, err = luci.http.protocol.parse_message_header( sourcehdr )
136
137                 if not message then
138                         self:error( thread, 400, err )
139                         break
140                 end     
141                 
142                 -- keep-alive
143                 if message.http_version == 1.1 then
144                         close = (message.env.HTTP_CONNECTION == "close")
145                 else
146                         close = not message.env.HTTP_CONNECTION or message.env.HTTP_CONNECTION == "close"
147                 end
148         
149                 if message.request_method == "get" or message.request_method == "head" then
150                         -- Be happy
151                         
152                 elseif message.request_method == "post" then
153                         -- If we have a HTTP/1.1 client and an Expect: 100-continue header then
154                         -- respond with HTTP 100 Continue message
155                         if message.http_version == 1.1 and message.headers['Expect'] and
156                                 message.headers['Expect'] == '100-continue'
157                         then
158                                 thread:send("HTTP/1.1 100 Continue\r\n\r\n")
159                         end
160                         
161                         if message.headers['Transfer-Encoding'] and
162                          message.headers['Transfer-Encoding'] ~= "identity" then
163                                 sourcein = socket.source("http-chunked", thread)
164                         elseif message.env.CONTENT_LENGTH then
165                                 sourcein = socket.source("by-length", thread,
166                                  tonumber(message.env.CONTENT_LENGTH))
167                         else
168                                 self:error( thread, 411, luci.http.protocol.statusmsg[411] )
169                                 break;
170                         end
171
172                         -- FIXME: Close for POST requests
173                         close = true
174                 else
175                         self:error( thread, 405, luci.http.protocol.statusmsg[405] )
176                         break;
177                         
178                 end
179
180
181                 local host = self.vhosts[message.env.HTTP_HOST] or self.host
182                 if not host then
183                         self:error( thread, 500, "Unable to find matching host" )
184                         break;
185                 end
186                 
187                 local response, sourceout = host:process(
188                         message, sourcein, sinkerr,
189                         client, io.stderr 
190                 )
191                 if not response then
192                         self:error( thread, 500, "Error processing handler" )
193                 end
194                 
195                 -- Post process response
196                 local sinkmode = close and "close-when-done" or "keep-open"
197                 
198                 if sourceout then
199                         if not response.headers["Content-Length"] then
200                                 if message.http_version == 1.1 then
201                                         response.headers["Transfer-Encoding"] = "chunked"
202                                         sinkmode = "http-chunked"
203                                 else
204                                         close = true
205                                         sinkmode = "close-when-done"
206                                 end
207                         end
208                 end
209                 
210                 if close then
211                         response.headers["Connection"] = "close"
212                 end
213                 
214                 
215                 local sinkout = socket.sink(sinkmode, thread)
216                 
217                 local header =
218                         message.env.SERVER_PROTOCOL .. " " ..
219                         tostring(response.status) .. " " ..
220                         luci.http.protocol.statusmsg[response.status] .. "\r\n"
221
222                 header = header .. "Server: LuCI HTTPd/" .. tostring(VERSION) .. "\r\n"
223
224                 
225                 for k,v in pairs(response.headers) do
226                         header = header .. k .. ": " .. v .. "\r\n"
227                 end
228                 
229                 thread:send(header .. "\r\n")
230
231                 if sourceout then
232                         local eof = false
233                         repeat
234                                 coroutine.yield()
235                                 eof = not ltn12.pump.step(sourceout, sinkout)
236                         until eof
237                 end
238         until close
239         
240         client:close()
241 end