1
#============================================================================
2
# This library is free software; you can redistribute it and/or
3
# modify it under the terms of version 2.1 of the GNU Lesser General Public
4
# License as published by the Free Software Foundation.
6
# This library is distributed in the hope that it will be useful,
7
# but WITHOUT ANY WARRANTY; without even the implied warranty of
8
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9
# Lesser General Public License for more details.
11
# You should have received a copy of the GNU Lesser General Public
12
# License along with this library; if not, write to the Free Software
13
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14
#============================================================================
15
# Copyright (C) 2005 Mike Wray <mike.wray@hp.com>
16
# Copyright (C) 2006 XenSource Ltd.
17
#============================================================================
24
from urllib import quote, unquote
29
from xen.xend import sxp
30
from xen.xend.Args import ArgError
31
from xen.xend.XendError import XendError
35
from resource import Resource, ErrorPage
36
from SrvDir import SrvDir
39
"""A request to complete processing using a thread.
42
def __init__(self, processor, req, fn, args, kwds):
43
self.processor = processor
50
self.processor.setInThread()
51
thread = threading.Thread(target=self.main)
52
thread.setDaemon(True)
57
self.fn(*self.args, **self.kwds)
61
self.req.resultErr(ex)
66
self.processor.process()
69
class RequestProcessor:
70
"""Processor for requests on a connection to an http server.
71
Requests are executed synchonously unless they ask for a thread by returning
79
def __init__(self, server, sock, addr):
82
self.srd = sock.makefile('rb')
83
self.srw = sock.makefile('wb')
84
self.srvaddr = server.getServerAddr()
89
def setInThread(self):
96
return HttpServerRequest(self, self.srvaddr, self.srd, self.srw)
110
req = self.getRequest()
112
if isinstance(res, ThreadRequest):
113
if self.isInThread():
121
class HttpServerRequest(http.HttpRequest):
122
"""A single request to an http server.
125
def __init__(self, processor, addr, srd, srw):
126
self.processor = processor
128
http.HttpRequest.__init__(self, addr, srd, srw)
131
return self.processor.getServer()
134
"""Process the request. If the return value is a ThreadRequest
135
it is evaluated in a thread.
139
self.postpath = map(unquote, string.split(self.request_path[1:], '/'))
140
resource = self.getResource()
141
return self.render(resource)
144
except Exception, ex:
145
self.processError(ex)
147
def processError(self, ex):
148
import traceback; traceback.print_exc()
149
self.sendError(http.INTERNAL_SERVER_ERROR, msg=str(ex))
150
self.setCloseConnection('close')
154
if self.close_connection:
155
self.processor.finish()
157
def prePathURL(self):
158
url_host = self.getRequestHostname()
159
port = self.getPort()
166
if port != default_port:
167
url_host += (':%d' % port)
168
url_path = quote(string.join(self.prepath, '/'))
169
return ('%s://%s/%s' % (url_proto, url_host, url_path))
171
def getResource(self):
172
return self.getServer().getResource(self)
174
def render(self, resource):
177
self.sendError(http.NOT_FOUND)
181
val = resource.render(self)
182
if not isinstance(val, Resource):
184
val = self.result(val)
187
except Exception, ex:
191
def threadRequest(self, _fn, *_args, **_kwds):
192
"""Create a request to finish request processing in a thread.
193
Use this to create a ThreadRequest to return from rendering a
194
resource if you need a thread to complete processing.
196
return ThreadRequest(self.processor, self, _fn, _args, _kwds)
198
def result(self, val):
199
if isinstance(val, Exception):
200
return self.resultErr(val)
202
return self.resultVal(val)
204
def resultVal(self, val):
205
"""Callback to complete the request.
207
@param val: the value
211
elif isinstance(val, ThreadRequest):
214
self.setHeader("Content-Type", sxp.mime_type)
215
sxp.show(val, out=self)
217
self.write('<html><head></head><body>')
219
if isinstance(val, types.ListType):
220
self.write('<code><pre>')
221
PrettyPrint.prettyprint(val, out=self)
222
self.write('</pre></code>')
225
self.write('</body></html>')
228
def resultErr(self, err):
229
"""Error callback to complete a request.
231
@param err: the error
233
if not isinstance(err, (ArgError, sxp.ParseError, XendError)):
235
#log.exception("op=%s: %s", op, str(err))
237
self.setHeader("Content-Type", sxp.mime_type)
238
sxp.show(['xend.err', str(err)], out=self)
240
self.setHeader("Content-Type", "text/plain")
247
"""Determine whether to send an SXP response to a request.
248
Uses SXP if there is no User-Agent, no Accept, or application/sxp is in Accept.
250
returns 1 for SXP, 0 otherwise
253
user_agent = self.getHeader('User-Agent')
254
accept = self.getHeader('Accept')
255
if (not user_agent) or (not accept) or (accept.find(sxp.mime_type) >= 0):
260
pathlist = [x for x in self.prepath if x != '' ]
262
self.write('<h1><a href="/">/</a>')
265
self.write(' <a href="%s">%s</a>/' % (s, x))
268
class HttpServerClient:
270
def __init__(self, server, sock, addr):
276
thread = threading.Thread(target=self.doProcess)
277
thread.setDaemon(True)
282
rp = RequestProcessor(self.server, self.sock, self.addr)
286
except Exception, ex:
287
print 'HttpServer>processRequest> exception: ', ex
297
def __init__(self, root, interface, port=8080):
299
self.interface = interface
301
# ready indicates when we are ready to begin accept connections
302
# it should be set after a successful bind
311
while not self.closed:
312
(sock, addr) = self.accept()
313
cl = HttpServerClient(self, sock, addr)
320
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
321
flags = fcntl.fcntl(self.socket.fileno(), fcntl.F_GETFD)
322
flags |= fcntl.FD_CLOEXEC
323
fcntl.fcntl(self.socket.fileno(), fcntl.F_SETFD, flags)
324
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
325
self.socket.bind((self.interface, self.port))
328
self.socket.listen(self.backlog)
331
return self.socket.accept()
336
# shutdown socket explicitly to allow reuse
338
self.socket.shutdown(2)
347
def getServerAddr(self):
348
return (socket.gethostname(), self.port)
350
def getResource(self, req):
351
return self.root.getRequestResource(req)
357
class UnixHttpServer(HttpServer):
359
def __init__(self, root, path):
360
HttpServer.__init__(self, root, 'localhost')
364
self.socket = unix.bind(self.path)
365
flags = fcntl.fcntl(self.socket.fileno(), fcntl.F_GETFD)
366
flags |= fcntl.FD_CLOEXEC
367
fcntl.fcntl(self.socket.fileno(), fcntl.F_SETFD, flags)