~ubuntu-branches/ubuntu/utopic/xen/utopic

« back to all changes in this revision

Viewing changes to tools/python/xen/web/httpserver.py

  • Committer: Bazaar Package Importer
  • Author(s): Bastian Blank
  • Date: 2010-05-06 15:47:38 UTC
  • mto: (1.3.1) (15.1.1 sid) (4.1.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20100506154738-agoz0rlafrh1fnq7
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.
 
5
#
 
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.
 
10
#
 
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
#============================================================================
 
18
 
 
19
import threading
 
20
 
 
21
import string
 
22
import socket
 
23
import types
 
24
from urllib import quote, unquote
 
25
import os
 
26
import os.path
 
27
import fcntl
 
28
 
 
29
from xen.xend import sxp
 
30
from xen.xend.Args import ArgError
 
31
from xen.xend.XendError import XendError
 
32
 
 
33
import http
 
34
import unix
 
35
from resource import Resource, ErrorPage
 
36
from SrvDir import SrvDir
 
37
 
 
38
class ThreadRequest:
 
39
    """A request to complete processing using a thread.
 
40
    """
 
41
    
 
42
    def __init__(self, processor, req, fn, args, kwds):
 
43
        self.processor = processor
 
44
        self.req = req
 
45
        self.fn = fn
 
46
        self.args = args
 
47
        self.kwds = kwds
 
48
        
 
49
    def run(self):
 
50
        self.processor.setInThread()
 
51
        thread = threading.Thread(target=self.main)
 
52
        thread.setDaemon(True)
 
53
        thread.start()
 
54
 
 
55
    def call(self):
 
56
        try:
 
57
            self.fn(*self.args, **self.kwds)
 
58
        except SystemExit:
 
59
            raise
 
60
        except Exception, ex:
 
61
            self.req.resultErr(ex)
 
62
        self.req.finish()
 
63
 
 
64
    def main(self):
 
65
        self.call()
 
66
        self.processor.process()
 
67
       
 
68
 
 
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
 
72
    a ThreadRequest.
 
73
    """
 
74
 
 
75
    done = False
 
76
 
 
77
    inThread = False
 
78
 
 
79
    def __init__(self, server, sock, addr):
 
80
        self.server = server
 
81
        self.sock = sock
 
82
        self.srd = sock.makefile('rb')
 
83
        self.srw = sock.makefile('wb')
 
84
        self.srvaddr = server.getServerAddr()
 
85
 
 
86
    def isInThread(self):
 
87
        return self.inThread
 
88
 
 
89
    def setInThread(self):
 
90
        self.inThread = True
 
91
 
 
92
    def getServer(self):
 
93
        return self.server
 
94
 
 
95
    def getRequest(self):
 
96
        return HttpServerRequest(self, self.srvaddr, self.srd, self.srw)
 
97
 
 
98
    def close(self):
 
99
        try:
 
100
            self.sock.close()
 
101
        except:
 
102
            pass
 
103
 
 
104
    def finish(self):
 
105
        self.done = True
 
106
        self.close()
 
107
 
 
108
    def process(self):
 
109
        while not self.done:
 
110
            req = self.getRequest()
 
111
            res = req.process()
 
112
            if isinstance(res, ThreadRequest):
 
113
                if self.isInThread():
 
114
                    res.call()
 
115
                else:
 
116
                    res.run()
 
117
                    break
 
118
            else:
 
119
                req.finish()
 
120
                                        
 
121
class HttpServerRequest(http.HttpRequest):
 
122
    """A single request to an http server.
 
123
    """
 
124
 
 
125
    def __init__(self, processor, addr, srd, srw):
 
126
        self.processor = processor
 
127
        self.prepath = ''
 
128
        http.HttpRequest.__init__(self, addr, srd, srw)
 
129
 
 
130
    def getServer(self):
 
131
        return self.processor.getServer()
 
132
 
 
133
    def process(self):
 
134
        """Process the request. If the return value is a ThreadRequest
 
135
        it is evaluated in a thread.
 
136
        """
 
137
        try:
 
138
            self.prepath = []
 
139
            self.postpath = map(unquote, string.split(self.request_path[1:], '/'))
 
140
            resource = self.getResource()
 
141
            return self.render(resource)
 
142
        except SystemExit:
 
143
            raise
 
144
        except Exception, ex:
 
145
            self.processError(ex)
 
146
 
 
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')
 
151
 
 
152
    def finish(self):
 
153
        self.sendResponse()
 
154
        if self.close_connection:
 
155
            self.processor.finish()
 
156
 
 
157
    def prePathURL(self):
 
158
        url_host = self.getRequestHostname()
 
159
        port = self.getPort()
 
160
        if self.isSecure():
 
161
            url_proto = "https"
 
162
            default_port = 443
 
163
        else:
 
164
            url_proto = "http"
 
165
            default_port = 80
 
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))
 
170
 
 
171
    def getResource(self):
 
172
        return self.getServer().getResource(self)
 
173
 
 
174
    def render(self, resource):
 
175
        val = None
 
176
        if resource is None:
 
177
            self.sendError(http.NOT_FOUND)
 
178
        else:
 
179
            try:
 
180
                while True:
 
181
                    val = resource.render(self)
 
182
                    if not isinstance(val, Resource):
 
183
                        break
 
184
                val = self.result(val)
 
185
            except SystemExit:
 
186
                raise
 
187
            except Exception, ex:
 
188
                self.resultErr(ex)
 
189
        return val
 
190
 
 
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.
 
195
        """
 
196
        return ThreadRequest(self.processor, self, _fn, _args, _kwds)
 
197
            
 
198
    def result(self, val):
 
199
        if isinstance(val, Exception):
 
200
            return self.resultErr(val)
 
201
        else:
 
202
            return self.resultVal(val)
 
203
 
 
204
    def resultVal(self, val):
 
205
        """Callback to complete the request.
 
206
 
 
207
        @param val: the value
 
208
        """
 
209
        if val is None:
 
210
            return val
 
211
        elif isinstance(val, ThreadRequest):
 
212
            return val
 
213
        elif self.useSxp():
 
214
            self.setHeader("Content-Type", sxp.mime_type)
 
215
            sxp.show(val, out=self)
 
216
        else:
 
217
            self.write('<html><head></head><body>')
 
218
            self.printPath()
 
219
            if isinstance(val, types.ListType):
 
220
                self.write('<code><pre>')
 
221
                PrettyPrint.prettyprint(val, out=self)
 
222
                self.write('</pre></code>')
 
223
            else:
 
224
                self.write(str(val))
 
225
            self.write('</body></html>')
 
226
        return None
 
227
 
 
228
    def resultErr(self, err):
 
229
        """Error callback to complete a request.
 
230
 
 
231
        @param err: the error
 
232
        """
 
233
        if not isinstance(err, (ArgError, sxp.ParseError, XendError)):
 
234
            raise
 
235
        #log.exception("op=%s: %s", op, str(err))
 
236
        if self.useSxp():
 
237
            self.setHeader("Content-Type", sxp.mime_type)
 
238
            sxp.show(['xend.err', str(err)], out=self)
 
239
        else:
 
240
            self.setHeader("Content-Type", "text/plain")
 
241
            self.write('Error ')
 
242
            self.write(': ')
 
243
            self.write(str(err))
 
244
        return None
 
245
 
 
246
    def useSxp(self):
 
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.
 
249
 
 
250
        returns 1 for SXP, 0 otherwise
 
251
        """
 
252
        ok = 0
 
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):
 
256
            ok = 1
 
257
        return ok
 
258
 
 
259
    def printPath(self):
 
260
        pathlist = [x for x in self.prepath if x != '' ]
 
261
        s = "/"
 
262
        self.write('<h1><a href="/">/</a>')
 
263
        for x in pathlist:
 
264
            s += x + "/"
 
265
            self.write(' <a href="%s">%s</a>/' % (s, x))
 
266
        self.write("</h1>")
 
267
 
 
268
class HttpServerClient:
 
269
 
 
270
    def __init__(self, server, sock, addr):
 
271
        self.server = server
 
272
        self.sock = sock
 
273
        self.addr = addr
 
274
 
 
275
    def process(self):
 
276
        thread = threading.Thread(target=self.doProcess)
 
277
        thread.setDaemon(True)
 
278
        thread.start()
 
279
 
 
280
    def doProcess(self):
 
281
        try:
 
282
            rp = RequestProcessor(self.server, self.sock, self.addr)
 
283
            rp.process()
 
284
        except SystemExit:
 
285
            raise
 
286
        except Exception, ex:
 
287
            print 'HttpServer>processRequest> exception: ', ex
 
288
            try:
 
289
                self.sock.close()
 
290
            except:
 
291
                pass
 
292
 
 
293
class HttpServer:
 
294
 
 
295
    backlog = 5
 
296
 
 
297
    def __init__(self, root, interface, port=8080):
 
298
        self.root = root
 
299
        self.interface = interface
 
300
        self.port = port
 
301
        # ready indicates when we are ready to begin accept connections
 
302
        # it should be set after a successful bind
 
303
        self.ready = False
 
304
        self.closed = False
 
305
 
 
306
    def run(self):
 
307
        self.bind()
 
308
        self.listen()
 
309
        self.ready = True
 
310
 
 
311
        while not self.closed:
 
312
            (sock, addr) = self.accept()
 
313
            cl = HttpServerClient(self, sock, addr)
 
314
            cl.process()
 
315
 
 
316
    def stop(self):
 
317
        self.close()
 
318
 
 
319
    def bind(self):
 
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))
 
326
 
 
327
    def listen(self):
 
328
        self.socket.listen(self.backlog)
 
329
 
 
330
    def accept(self):
 
331
        return self.socket.accept()
 
332
 
 
333
    def close(self):
 
334
        self.closed = True
 
335
        self.ready = False
 
336
        # shutdown socket explicitly to allow reuse
 
337
        try:
 
338
            self.socket.shutdown(2)
 
339
        except socket.error:
 
340
            pass
 
341
 
 
342
        try:
 
343
            self.socket.close()
 
344
        except socket.error:
 
345
            pass
 
346
 
 
347
    def getServerAddr(self):
 
348
        return (socket.gethostname(), self.port)
 
349
 
 
350
    def getResource(self, req):
 
351
        return self.root.getRequestResource(req)
 
352
 
 
353
    def shutdown(self):
 
354
        self.close()
 
355
 
 
356
 
 
357
class UnixHttpServer(HttpServer):
 
358
 
 
359
    def __init__(self, root, path):
 
360
        HttpServer.__init__(self, root, 'localhost')
 
361
        self.path = path
 
362
        
 
363
    def bind(self):
 
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)