* libs/httpd: Added Cache-Control header to LuCI
[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 }
116
117 client:settimeout( 0 )
118
119 local sourcein = ltn12.source.empty()
120 local sourcehdr = luci.http.protocol.header_source( thread )
121 local sinkerr = ltn12.sink.file( io.stderr )
122
123 local close = false
124
125 local reading = { client }
126
127 local message, err
128
129 repeat
130 -- parse headers
131 message, err = luci.http.protocol.parse_message_header( sourcehdr )
132
133 if not message then
134 self:error( client, 400, err )
135 break
136 end
137
138 -- keep-alive
139 if message.http_version == 1.1 then
140 close = (message.env.HTTP_CONNECTION == "close")
141 else
142 close = not message.env.HTTP_CONNECTION or message.env.HTTP_CONNECTION == "close"
143 end
144
145 if message.request_method == "get" or message.request_method == "head" then
146 -- Be happy
147
148 elseif message.request_method == "post" then
149 -- If we have a HTTP/1.1 client and an Expect: 100-continue header then
150 -- respond with HTTP 100 Continue message
151 if message.http_version == 1.1 and message.headers['Expect'] and
152 message.headers['Expect'] == '100-continue'
153 then
154 client:send("HTTP/1.1 100 Continue\r\n\r\n")
155 end
156
157 if message.headers['Transfer-Encoding'] and
158 message.headers['Transfer-Encoding'] ~= "identity" then
159 sourcein = socket.source("http-chunked", thread)
160 elseif message.env.CONTENT_LENGTH then
161 sourcein = socket.source("by-length", thread,
162 tonumber(message.env.CONTENT_LENGTH))
163 else
164 self:error( client, 411, luci.http.protocol.statusmsg[411] )
165 break;
166 end
167
168 else
169 self:error( client, 405, luci.http.protocol.statusmsg[405] )
170 break;
171
172 end
173
174
175 local host = self.vhosts[message.env.HTTP_HOST] or self.host
176 if not host then
177 self:error( client, 500, "Unable to find matching host" )
178 break;
179 end
180
181 local response, sourceout = host:process(
182 message, sourcein, sinkerr,
183 client, io.stderr
184 )
185 if not response then
186 self:error( client, 500, "Error processing handler" )
187 end
188
189 -- Post process response
190 local sinkmode = close and "close-when-done" or "keep-open"
191
192 if sourceout then
193 if not response.headers["Content-Length"] then
194 if message.http_version == 1.1 then
195 response.headers["Transfer-Encoding"] = "chunked"
196 sinkmode = "http-chunked"
197 else
198 close = true
199 sinkmode = "close-when-done"
200 end
201 end
202 end
203
204 if close then
205 response.headers["Connection"] = "close"
206 end
207
208
209 local sinkout = socket.sink(sinkmode, client)
210
211 local header =
212 message.env.SERVER_PROTOCOL .. " " ..
213 tostring(response.status) .. " " ..
214 luci.http.protocol.statusmsg[response.status] .. "\r\n"
215
216 header = header .. "Server: LuCI HTTPd/" .. tostring(VERSION) .. "\r\n"
217
218
219 for k,v in pairs(response.headers) do
220 header = header .. k .. ": " .. v .. "\r\n"
221 end
222
223 client:send(header .. "\r\n")
224
225 if sourceout then
226 local eof = false
227 repeat
228 coroutine.yield()
229 eof = not ltn12.pump.step(sourceout, sinkout)
230 until eof
231 end
232 until close
233
234 client:close()
235 end