* libs/httpd: Prepared HTTPD dispatching model
[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
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
9
10         http://www.apache.org/licenses/LICENSE-2.0
11
12 $Id$
13
14 ]]--
15
16 module("luci.httpd.server", package.seeall)
17 require("luci.util")
18
19 READ_BUFSIZE = 1024
20
21 VHost = luci.util.class()
22
23 function VHost.__init__(self, handler)
24         self.handler = handler
25         self.dhandler = {}
26 end
27
28 function VHost.process(self, ...)
29         -- TODO: Dispatch handler
30 end
31
32 function VHost.sethandler(self, handler, match)
33         if match then
34                 self.dhandler[match] = handler
35         else
36                 self.handler = handler
37         end
38 end
39
40
41
42 Server = luci.util.class()
43
44 function Server.__init__(self, ip, port, base)
45         self.socket = socket.bind(ip, port)
46         self.socket:settimeout(0, "t")
47         self.clhandler = client_handler
48         self.errhandler = error503
49         self.host = nil
50         self.vhosts = {}
51         
52         -- Clone another server
53         if base then
54                 getmetatable(self).__index = base 
55         end
56 end
57
58 -- Sets a vhost
59 function Server.setvhost(self, vhost, name)
60         if name then
61                 self.vhosts[name] = vhost
62         else
63                 self.host = vhost
64         end
65 end
66
67
68 function Server.error400(self, client, msg)
69         client:send( "HTTP/1.0 400 Bad request\r\n" )
70         client:send( "Content-Type: text/plain\r\n\r\n" )
71
72         if msg then
73                 client:send( msg .. "\r\n" )
74         end
75
76         client:close()
77 end
78
79 function Server.error503(self, client)
80         client:send( "HTTP/1.0 503 Server unavailable\r\n" )
81         client:send( "Content-Type: text/plain\r\n\r\n" )
82         client:send( "There are too many clients connected, try again later\r\n" )
83 end
84
85 function Server.process(self, ...)
86         -- TODO: Dispatch vhost
87 end
88
89
90 function Server.client_handler(self, client)
91
92         client:settimeout( 0 )
93
94         -- Create LTN12 block source
95         local block_source = function()
96
97                 coroutine.yield()
98
99                 local chunk, err, part = client:receive( READ_BUFSIZE )
100
101                 if chunk == nil and err == "timeout" then
102                         return part
103                 elseif chunk ~= nil then
104                         return chunk
105                 else
106                         return nil, err
107                 end
108
109         end
110
111         -- Create LTN12 line source
112         local line_source = ltn12.source.simplify( function()
113
114                 coroutine.yield()
115
116                 local chunk, err, part = client:receive("*l")
117
118                 -- Line too long
119                 if chunk == nil and err ~= "timeout" then
120
121                         return nil, part
122                                 and "Line exceeds maximum allowed length["..part.."]"
123                                 or  "Unexpected EOF"
124
125                 -- Line ok
126                 else
127
128                         -- Strip trailing CR
129                         chunk = chunk:gsub("\r$","")
130
131                         -- We got end of headers, switch to dummy source
132                         if #chunk == 0 then
133                                 return "", function()
134                                         return nil
135                                 end
136                         else
137                                 return chunk, nil
138                         end
139                 end
140         end )
141
142         coroutine.yield(client)
143
144
145         -- parse message
146         local message, err = luci.http.protocol.parse_message_header( line_source )
147
148         if message then
149
150                 -- If we have a HTTP/1.1 client and an Expect: 100-continue header then
151                 -- respond with HTTP 100 Continue message
152                 if message.http_version == 1.1 and message.headers['Expect'] and
153                         message.headers['Expect'] == '100-continue'
154                 then
155                         client:send("HTTP/1.1 100 Continue\r\n\r\n")
156                 end
157
158
159                 local s, e = luci.http.protocol.parse_message_body( block_source, message )
160
161                 -- XXX: debug
162                 luci.util.dumptable( message )
163
164                 if not s and e then
165                         self:error400( client, e )
166                 end
167         else
168                 self:error400( client, err )
169         end
170
171         -- send response
172         self:error400( client, "Dummy response" )
173 end