Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(117)

Side by Side Diff: third_party/twisted_8_1/twisted/web/server.py

Issue 12261012: Remove third_party/twisted_8_1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # -*- test-case-name: twisted.web.test.test_web -*-
2
3 # Copyright (c) 2001-2004 Twisted Matrix Laboratories.
4 # See LICENSE for details.
5
6
7 """This is a web-server which integrates with the twisted.internet
8 infrastructure.
9 """
10
11 # System Imports
12
13 import string
14 import types
15 import operator
16 import copy
17 import time
18 import os
19 from urllib import quote
20 try:
21 from twisted.protocols._c_urlarg import unquote
22 except ImportError:
23 from urllib import unquote
24
25 #some useful constants
26 NOT_DONE_YET = 1
27
28 # Twisted Imports
29 from twisted.spread import pb
30 from twisted.internet import defer, address, task
31 from twisted.web import http
32 from twisted.python import log, reflect, failure, components
33 from twisted import copyright
34
35 # Sibling Imports
36 import error, resource
37 from twisted.web import util as webutil
38
39
40 # backwards compatability
41 date_time_string = http.datetimeToString
42 string_date_time = http.stringToDatetime
43
44 # Support for other methods may be implemented on a per-resource basis.
45 supportedMethods = ('GET', 'HEAD', 'POST')
46
47
48 class UnsupportedMethod(Exception):
49 """Raised by a resource when faced with a strange request method.
50
51 RFC 2616 (HTTP 1.1) gives us two choices when faced with this situtation:
52 If the type of request is known to us, but not allowed for the requested
53 resource, respond with NOT_ALLOWED. Otherwise, if the request is something
54 we don't know how to deal with in any case, respond with NOT_IMPLEMENTED.
55
56 When this exception is raised by a Resource's render method, the server
57 will make the appropriate response.
58
59 This exception's first argument MUST be a sequence of the methods the
60 resource *does* support.
61 """
62
63 allowedMethods = ()
64
65 def __init__(self, allowedMethods, *args):
66 Exception.__init__(self, allowedMethods, *args)
67 self.allowedMethods = allowedMethods
68
69 if not operator.isSequenceType(allowedMethods):
70 why = "but my first argument is not a sequence."
71 s = ("First argument must be a sequence of"
72 " supported methods, %s" % (why,))
73 raise TypeError, s
74
75 def _addressToTuple(addr):
76 if isinstance(addr, address.IPv4Address):
77 return ('INET', addr.host, addr.port)
78 elif isinstance(addr, address.UNIXAddress):
79 return ('UNIX', addr.name)
80 else:
81 return tuple(addr)
82
83 class Request(pb.Copyable, http.Request, components.Componentized):
84
85 site = None
86 appRootURL = None
87 __pychecker__ = 'unusednames=issuer'
88
89 def __init__(self, *args, **kw):
90 http.Request.__init__(self, *args, **kw)
91 components.Componentized.__init__(self)
92 self.notifications = []
93
94 def getStateToCopyFor(self, issuer):
95 x = self.__dict__.copy()
96 del x['transport']
97 # XXX refactor this attribute out; it's from protocol
98 # del x['server']
99 del x['channel']
100 del x['content']
101 del x['site']
102 self.content.seek(0, 0)
103 x['content_data'] = self.content.read()
104 x['remote'] = pb.ViewPoint(issuer, self)
105
106 # Address objects aren't jellyable
107 x['host'] = _addressToTuple(x['host'])
108 x['client'] = _addressToTuple(x['client'])
109
110 return x
111
112 # HTML generation helpers
113
114 def sibLink(self, name):
115 "Return the text that links to a sibling of the requested resource."
116 if self.postpath:
117 return (len(self.postpath)*"../") + name
118 else:
119 return name
120
121 def childLink(self, name):
122 "Return the text that links to a child of the requested resource."
123 lpp = len(self.postpath)
124 if lpp > 1:
125 return ((lpp-1)*"../") + name
126 elif lpp == 1:
127 return name
128 else: # lpp == 0
129 if len(self.prepath) and self.prepath[-1]:
130 return self.prepath[-1] + '/' + name
131 else:
132 return name
133
134 def process(self):
135 "Process a request."
136
137 # get site from channel
138 self.site = self.channel.site
139
140 # set various default headers
141 self.setHeader('server', version)
142 self.setHeader('date', http.datetimeToString())
143 self.setHeader('content-type', "text/html")
144
145 # Resource Identification
146 self.prepath = []
147 self.postpath = map(unquote, string.split(self.path[1:], '/'))
148 try:
149 resrc = self.site.getResourceFor(self)
150 self.render(resrc)
151 except:
152 self.processingFailed(failure.Failure())
153
154
155 def render(self, resrc):
156 try:
157 body = resrc.render(self)
158 except UnsupportedMethod, e:
159 allowedMethods = e.allowedMethods
160 if (self.method == "HEAD") and ("GET" in allowedMethods):
161 # We must support HEAD (RFC 2616, 5.1.1). If the
162 # resource doesn't, fake it by giving the resource
163 # a 'GET' request and then return only the headers,
164 # not the body.
165 log.msg("Using GET to fake a HEAD request for %s" %
166 (resrc,))
167 self.method = "GET"
168 body = resrc.render(self)
169
170 if body is NOT_DONE_YET:
171 log.msg("Tried to fake a HEAD request for %s, but "
172 "it got away from me." % resrc)
173 # Oh well, I guess we won't include the content length.
174 else:
175 self.setHeader('content-length', str(len(body)))
176
177 self.write('')
178 self.finish()
179 return
180
181 if self.method in (supportedMethods):
182 # We MUST include an Allow header
183 # (RFC 2616, 10.4.6 and 14.7)
184 self.setHeader('Allow', allowedMethods)
185 s = ('''Your browser approached me (at %(URI)s) with'''
186 ''' the method "%(method)s". I only allow'''
187 ''' the method%(plural)s %(allowed)s here.''' % {
188 'URI': self.uri,
189 'method': self.method,
190 'plural': ((len(allowedMethods) > 1) and 's') or '',
191 'allowed': string.join(allowedMethods, ', ')
192 })
193 epage = error.ErrorPage(http.NOT_ALLOWED,
194 "Method Not Allowed", s)
195 body = epage.render(self)
196 else:
197 epage = error.ErrorPage(http.NOT_IMPLEMENTED, "Huh?",
198 """I don't know how to treat a"""
199 """ %s request."""
200 % (self.method))
201 body = epage.render(self)
202 # end except UnsupportedMethod
203
204 if body == NOT_DONE_YET:
205 return
206 if type(body) is not types.StringType:
207 body = error.ErrorPage(http.INTERNAL_SERVER_ERROR,
208 "Request did not return a string",
209 "Request: "+html.PRE(reflect.safe_repr(self))+"<br />"+
210 "Resource: "+html.PRE(reflect.safe_repr(resrc))+"<br />"+
211 "Value: "+html.PRE(reflect.safe_repr(body))).render(self)
212
213 if self.method == "HEAD":
214 if len(body) > 0:
215 # This is a Bad Thing (RFC 2616, 9.4)
216 log.msg("Warning: HEAD request %s for resource %s is"
217 " returning a message body."
218 " I think I'll eat it."
219 % (self, resrc))
220 self.setHeader('content-length', str(len(body)))
221 self.write('')
222 else:
223 self.setHeader('content-length', str(len(body)))
224 self.write(body)
225 self.finish()
226
227 def processingFailed(self, reason):
228 log.err(reason)
229 if self.site.displayTracebacks:
230 body = ("<html><head><title>web.Server Traceback (most recent call l ast)</title></head>"
231 "<body><b>web.Server Traceback (most recent call last):</b>\ n\n"
232 "%s\n\n</body></html>\n"
233 % webutil.formatFailure(reason))
234 else:
235 body = ("<html><head><title>Processing Failed</title></head><body>"
236 "<b>Processing Failed</b></body></html>")
237
238 self.setResponseCode(http.INTERNAL_SERVER_ERROR)
239 self.setHeader('content-type',"text/html")
240 self.setHeader('content-length', str(len(body)))
241 self.write(body)
242 self.finish()
243 return reason
244
245 def notifyFinish(self):
246 """Notify when finishing the request
247
248 @return: A deferred. The deferred will be triggered when the
249 request is finished -- with a C{None} value if the request
250 finishes successfully or with an error if the request is stopped
251 by the client.
252 """
253 self.notifications.append(defer.Deferred())
254 return self.notifications[-1]
255
256 def connectionLost(self, reason):
257 for d in self.notifications:
258 d.errback(reason)
259 self.notifications = []
260
261 def finish(self):
262 http.Request.finish(self)
263 for d in self.notifications:
264 d.callback(None)
265 self.notifications = []
266
267 def view_write(self, issuer, data):
268 """Remote version of write; same interface.
269 """
270 self.write(data)
271
272 def view_finish(self, issuer):
273 """Remote version of finish; same interface.
274 """
275 self.finish()
276
277 def view_addCookie(self, issuer, k, v, **kwargs):
278 """Remote version of addCookie; same interface.
279 """
280 self.addCookie(k, v, **kwargs)
281
282 def view_setHeader(self, issuer, k, v):
283 """Remote version of setHeader; same interface.
284 """
285 self.setHeader(k, v)
286
287 def view_setLastModified(self, issuer, when):
288 """Remote version of setLastModified; same interface.
289 """
290 self.setLastModified(when)
291
292 def view_setETag(self, issuer, tag):
293 """Remote version of setETag; same interface.
294 """
295 self.setETag(tag)
296
297 def view_setResponseCode(self, issuer, code):
298 """Remote version of setResponseCode; same interface.
299 """
300 self.setResponseCode(code)
301
302 def view_registerProducer(self, issuer, producer, streaming):
303 """Remote version of registerProducer; same interface.
304 (requires a remote producer.)
305 """
306 self.registerProducer(_RemoteProducerWrapper(producer), streaming)
307
308 def view_unregisterProducer(self, issuer):
309 self.unregisterProducer()
310
311 ### these calls remain local
312
313 session = None
314
315 def getSession(self, sessionInterface = None):
316 # Session management
317 if not self.session:
318 cookiename = string.join(['TWISTED_SESSION'] + self.sitepath, "_")
319 sessionCookie = self.getCookie(cookiename)
320 if sessionCookie:
321 try:
322 self.session = self.site.getSession(sessionCookie)
323 except KeyError:
324 pass
325 # if it still hasn't been set, fix it up.
326 if not self.session:
327 self.session = self.site.makeSession()
328 self.addCookie(cookiename, self.session.uid, path='/')
329 self.session.touch()
330 if sessionInterface:
331 return self.session.getComponent(sessionInterface)
332 return self.session
333
334 def _prePathURL(self, prepath):
335 port = self.getHost().port
336 if self.isSecure():
337 default = 443
338 else:
339 default = 80
340 if port == default:
341 hostport = ''
342 else:
343 hostport = ':%d' % port
344 return 'http%s://%s%s/%s' % (
345 self.isSecure() and 's' or '',
346 self.getRequestHostname(),
347 hostport,
348 '/'.join([quote(segment, safe='') for segment in prepath]))
349
350 def prePathURL(self):
351 return self._prePathURL(self.prepath)
352
353 def URLPath(self):
354 from twisted.python import urlpath
355 return urlpath.URLPath.fromRequest(self)
356
357 def rememberRootURL(self):
358 """
359 Remember the currently-processed part of the URL for later
360 recalling.
361 """
362 url = self._prePathURL(self.prepath[:-1])
363 self.appRootURL = url
364
365 def getRootURL(self):
366 """
367 Get a previously-remembered URL.
368 """
369 return self.appRootURL
370
371
372 class _RemoteProducerWrapper:
373 def __init__(self, remote):
374 self.resumeProducing = remote.remoteMethod("resumeProducing")
375 self.pauseProducing = remote.remoteMethod("pauseProducing")
376 self.stopProducing = remote.remoteMethod("stopProducing")
377
378
379 class Session(components.Componentized):
380 """
381 A user's session with a system.
382
383 This utility class contains no functionality, but is used to
384 represent a session.
385
386 @ivar sessionTimeout: timeout of a session, in seconds.
387 @ivar loopFactory: factory for creating L{task.LoopingCall}. Mainly for
388 testing.
389 """
390 sessionTimeout = 900
391 loopFactory = task.LoopingCall
392
393 def __init__(self, site, uid):
394 """
395 Initialize a session with a unique ID for that session.
396 """
397 components.Componentized.__init__(self)
398 self.site = site
399 self.uid = uid
400 self.expireCallbacks = []
401 self.checkExpiredLoop = None
402 self.touch()
403 self.sessionNamespaces = {}
404
405
406 def startCheckingExpiration(self, lifetime):
407 """
408 Start expiration tracking.
409
410 @type lifetime: C{int} or C{float}
411 @param lifetime: The number of seconds this session is allowed to be
412 idle before it expires.
413
414 @return: C{None}
415 """
416 self.checkExpiredLoop = self.loopFactory(self.checkExpired)
417 self.checkExpiredLoop.start(lifetime, now=False)
418
419
420 def notifyOnExpire(self, callback):
421 """
422 Call this callback when the session expires or logs out.
423 """
424 self.expireCallbacks.append(callback)
425
426
427 def expire(self):
428 """
429 Expire/logout of the session.
430 """
431 del self.site.sessions[self.uid]
432 for c in self.expireCallbacks:
433 c()
434 self.expireCallbacks = []
435 if self.checkExpiredLoop is not None:
436 self.checkExpiredLoop.stop()
437 # Break reference cycle.
438 self.checkExpiredLoop = None
439
440
441 def _getTime(self):
442 """
443 Return current time used for session validity.
444 """
445 return time.time()
446
447
448 def touch(self):
449 """
450 Notify session modification.
451 """
452 self.lastModified = self._getTime()
453
454
455 def checkExpired(self):
456 """
457 Is it time for me to expire?
458
459 If I haven't been touched in fifteen minutes, I will call my
460 expire method.
461 """
462 # If I haven't been touched in 15 minutes:
463 if self._getTime() - self.lastModified > self.sessionTimeout:
464 if self.uid in self.site.sessions:
465 self.expire()
466
467
468 version = "TwistedWeb/%s" % copyright.version
469
470
471 class Site(http.HTTPFactory):
472 """
473 A web site: manage log, sessions, and resources.
474
475 @ivar counter: increment value used for generating unique sessions ID.
476 @ivar requestFactory: factory creating requests objects. Default to
477 L{Request}.
478 @ivar displayTracebacks: if set, Twisted internal errors are displayed on
479 rendered pages. Default to C{True}.
480 @ivar sessionFactory: factory for sessions objects. Default to L{Session}.
481 @ivar sessionCheckTime: interval between each check of session expiration.
482 """
483 counter = 0
484 requestFactory = Request
485 displayTracebacks = True
486 sessionFactory = Session
487 sessionCheckTime = 1800
488
489 def __init__(self, resource, logPath=None, timeout=60*60*12):
490 """
491 Initialize.
492 """
493 http.HTTPFactory.__init__(self, logPath=logPath, timeout=timeout)
494 self.sessions = {}
495 self.resource = resource
496
497 def _openLogFile(self, path):
498 from twisted.python import logfile
499 return logfile.LogFile(os.path.basename(path), os.path.dirname(path))
500
501 def __getstate__(self):
502 d = self.__dict__.copy()
503 d['sessions'] = {}
504 return d
505
506 def _mkuid(self):
507 """
508 (internal) Generate an opaque, unique ID for a user's session.
509 """
510 import md5, random
511 self.counter = self.counter + 1
512 return md5.new("%s_%s" % (str(random.random()) , str(self.counter))).hex digest()
513
514 def makeSession(self):
515 """
516 Generate a new Session instance, and store it for future reference.
517 """
518 uid = self._mkuid()
519 session = self.sessions[uid] = self.sessionFactory(self, uid)
520 session.startCheckingExpiration(self.sessionCheckTime)
521 return session
522
523 def getSession(self, uid):
524 """
525 Get a previously generated session, by its unique ID.
526 This raises a KeyError if the session is not found.
527 """
528 return self.sessions[uid]
529
530 def buildProtocol(self, addr):
531 """
532 Generate a channel attached to this site.
533 """
534 channel = http.HTTPFactory.buildProtocol(self, addr)
535 channel.requestFactory = self.requestFactory
536 channel.site = self
537 return channel
538
539 isLeaf = 0
540
541 def render(self, request):
542 """
543 Redirect because a Site is always a directory.
544 """
545 request.redirect(request.prePathURL() + '/')
546 request.finish()
547
548 def getChildWithDefault(self, pathEl, request):
549 """
550 Emulate a resource's getChild method.
551 """
552 request.site = self
553 return self.resource.getChildWithDefault(pathEl, request)
554
555 def getResourceFor(self, request):
556 """
557 Get a resource for a request.
558
559 This iterates through the resource heirarchy, calling
560 getChildWithDefault on each resource it finds for a path element,
561 stopping when it hits an element where isLeaf is true.
562 """
563 request.site = self
564 # Sitepath is used to determine cookie names between distributed
565 # servers and disconnected sites.
566 request.sitepath = copy.copy(request.prepath)
567 return resource.getChildForRequest(self.resource, request)
568
569
570 import html
571
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/web/script.py ('k') | third_party/twisted_8_1/twisted/web/soap.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698