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

Side by Side Diff: third_party/twisted_8_1/twisted/web/static.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
« no previous file with comments | « third_party/twisted_8_1/twisted/web/soap.py ('k') | third_party/twisted_8_1/twisted/web/sux.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # -*- test-case-name: twisted.web.test.test_web -*-
2 # Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5
6 """I deal with static resources.
7 """
8
9 from __future__ import nested_scopes
10
11 # System Imports
12 import os, stat, string
13 import cStringIO
14 import traceback
15 import warnings
16 import types
17 StringIO = cStringIO
18 del cStringIO
19 import urllib
20
21 # Sibling Imports
22 from twisted.web import server
23 from twisted.web import error
24 from twisted.web import resource
25 from twisted.web.util import redirectTo
26
27 # Twisted Imports
28 from twisted.web import http
29 from twisted.python import threadable, log, components, failure, filepath
30 from twisted.internet import abstract, interfaces, defer
31 from twisted.spread import pb
32 from twisted.persisted import styles
33 from twisted.python.util import InsensitiveDict
34 from twisted.python.runtime import platformType
35
36
37 dangerousPathError = error.NoResource("Invalid request URL.")
38
39 def isDangerous(path):
40 return path == '..' or '/' in path or os.sep in path
41
42
43 class Data(resource.Resource):
44 """
45 This is a static, in-memory resource.
46 """
47
48 def __init__(self, data, type):
49 resource.Resource.__init__(self)
50 self.data = data
51 self.type = type
52
53 def render(self, request):
54 request.setHeader("content-type", self.type)
55 request.setHeader("content-length", str(len(self.data)))
56 if request.method == "HEAD":
57 return ''
58 return self.data
59
60 def addSlash(request):
61 qs = ''
62 qindex = string.find(request.uri, '?')
63 if qindex != -1:
64 qs = request.uri[qindex:]
65
66 return "http%s://%s%s/%s" % (
67 request.isSecure() and 's' or '',
68 request.getHeader("host"),
69 (string.split(request.uri,'?')[0]),
70 qs)
71
72 class Redirect(resource.Resource):
73 def __init__(self, request):
74 resource.Resource.__init__(self)
75 self.url = addSlash(request)
76
77 def render(self, request):
78 return redirectTo(self.url, request)
79
80
81 class Registry(components.Componentized, styles.Versioned):
82 """
83 I am a Componentized object that will be made available to internal Twisted
84 file-based dynamic web content such as .rpy and .epy scripts.
85 """
86
87 def __init__(self):
88 components.Componentized.__init__(self)
89 self._pathCache = {}
90
91 persistenceVersion = 1
92
93 def upgradeToVersion1(self):
94 self._pathCache = {}
95
96 def cachePath(self, path, rsrc):
97 self._pathCache[path] = rsrc
98
99 def getCachedPath(self, path):
100 return self._pathCache.get(path)
101
102
103 def loadMimeTypes(mimetype_locations=['/etc/mime.types']):
104 """
105 Multiple file locations containing mime-types can be passed as a list.
106 The files will be sourced in that order, overriding mime-types from the
107 files sourced beforehand, but only if a new entry explicitly overrides
108 the current entry.
109 """
110 import mimetypes
111 # Grab Python's built-in mimetypes dictionary.
112 contentTypes = mimetypes.types_map
113 # Update Python's semi-erroneous dictionary with a few of the
114 # usual suspects.
115 contentTypes.update(
116 {
117 '.conf': 'text/plain',
118 '.diff': 'text/plain',
119 '.exe': 'application/x-executable',
120 '.flac': 'audio/x-flac',
121 '.java': 'text/plain',
122 '.ogg': 'application/ogg',
123 '.oz': 'text/x-oz',
124 '.swf': 'application/x-shockwave-flash',
125 '.tgz': 'application/x-gtar',
126 '.wml': 'text/vnd.wap.wml',
127 '.xul': 'application/vnd.mozilla.xul+xml',
128 '.py': 'text/plain',
129 '.patch': 'text/plain',
130 }
131 )
132 # Users can override these mime-types by loading them out configuration
133 # files (this defaults to ['/etc/mime.types']).
134 for location in mimetype_locations:
135 if os.path.exists(location):
136 more = mimetypes.read_mime_types(location)
137 if more is not None:
138 contentTypes.update(more)
139
140 return contentTypes
141
142 def getTypeAndEncoding(filename, types, encodings, defaultType):
143 p, ext = os.path.splitext(filename)
144 ext = ext.lower()
145 if encodings.has_key(ext):
146 enc = encodings[ext]
147 ext = os.path.splitext(p)[1].lower()
148 else:
149 enc = None
150 type = types.get(ext, defaultType)
151 return type, enc
152
153 class File(resource.Resource, styles.Versioned, filepath.FilePath):
154 """
155 File is a resource that represents a plain non-interpreted file
156 (although it can look for an extension like .rpy or .cgi and hand the
157 file to a processor for interpretation if you wish). Its constructor
158 takes a file path.
159
160 Alternatively, you can give a directory path to the constructor. In this
161 case the resource will represent that directory, and its children will
162 be files underneath that directory. This provides access to an entire
163 filesystem tree with a single Resource.
164
165 If you map the URL 'http://server/FILE' to a resource created as
166 File('/tmp'), then http://server/FILE/ will return an HTML-formatted
167 listing of the /tmp/ directory, and http://server/FILE/foo/bar.html will
168 return the contents of /tmp/foo/bar.html .
169
170 @cvar childNotFound: L{Resource} used to render 404 Not Found error pages.
171 """
172
173 contentTypes = loadMimeTypes()
174
175 contentEncodings = {
176 ".gz" : "gzip",
177 ".bz2": "bzip2"
178 }
179
180 processors = {}
181
182 indexNames = ["index", "index.html", "index.htm", "index.trp", "index.rpy"]
183
184 type = None
185
186 ### Versioning
187
188 persistenceVersion = 6
189
190 def upgradeToVersion6(self):
191 self.ignoredExts = []
192 if self.allowExt:
193 self.ignoreExt("*")
194 del self.allowExt
195
196 def upgradeToVersion5(self):
197 if not isinstance(self.registry, Registry):
198 self.registry = Registry()
199
200 def upgradeToVersion4(self):
201 if not hasattr(self, 'registry'):
202 self.registry = {}
203
204 def upgradeToVersion3(self):
205 if not hasattr(self, 'allowExt'):
206 self.allowExt = 0
207
208 def upgradeToVersion2(self):
209 self.defaultType = "text/html"
210
211 def upgradeToVersion1(self):
212 if hasattr(self, 'indexName'):
213 self.indexNames = [self.indexName]
214 del self.indexName
215
216 def __init__(self, path, defaultType="text/html", ignoredExts=(), registry=N one, allowExt=0):
217 """Create a file with the given path.
218 """
219 resource.Resource.__init__(self)
220 filepath.FilePath.__init__(self, path)
221 # Remove the dots from the path to split
222 self.defaultType = defaultType
223 if ignoredExts in (0, 1) or allowExt:
224 warnings.warn("ignoredExts should receive a list, not a boolean")
225 if ignoredExts or allowExt:
226 self.ignoredExts = ['*']
227 else:
228 self.ignoredExts = []
229 else:
230 self.ignoredExts = list(ignoredExts)
231 self.registry = registry or Registry()
232
233 def ignoreExt(self, ext):
234 """Ignore the given extension.
235
236 Serve file.ext if file is requested
237 """
238 self.ignoredExts.append(ext)
239
240 childNotFound = error.NoResource("File not found.")
241
242 def directoryListing(self):
243 from twisted.web.woven import dirlist
244 return dirlist.DirectoryLister(self.path,
245 self.listNames(),
246 self.contentTypes,
247 self.contentEncodings,
248 self.defaultType)
249
250 def getChild(self, path, request):
251 """See twisted.web.Resource.getChild.
252 """
253 self.restat()
254
255 if not self.isdir():
256 return self.childNotFound
257
258 if path:
259 fpath = self.child(path)
260 else:
261 fpath = self.childSearchPreauth(*self.indexNames)
262 if fpath is None:
263 return self.directoryListing()
264
265 if not fpath.exists():
266 fpath = fpath.siblingExtensionSearch(*self.ignoredExts)
267 if fpath is None:
268 return self.childNotFound
269
270 if platformType == "win32":
271 # don't want .RPY to be different than .rpy, since that would allow
272 # source disclosure.
273 processor = InsensitiveDict(self.processors).get(fpath.splitext()[1] )
274 else:
275 processor = self.processors.get(fpath.splitext()[1])
276 if processor:
277 return resource.IResource(processor(fpath.path, self.registry))
278 return self.createSimilarFile(fpath.path)
279
280 # methods to allow subclasses to e.g. decrypt files on the fly:
281 def openForReading(self):
282 """Open a file and return it."""
283 return self.open()
284
285 def getFileSize(self):
286 """Return file size."""
287 return self.getsize()
288
289
290 def render(self, request):
291 """You know what you doing."""
292 self.restat()
293
294 if self.type is None:
295 self.type, self.encoding = getTypeAndEncoding(self.basename(),
296 self.contentTypes,
297 self.contentEncodings,
298 self.defaultType)
299
300 if not self.exists():
301 return self.childNotFound.render(request)
302
303 if self.isdir():
304 return self.redirect(request)
305
306 #for content-length
307 fsize = size = self.getFileSize()
308
309 # request.setHeader('accept-ranges','bytes')
310
311 if self.type:
312 request.setHeader('content-type', self.type)
313 if self.encoding:
314 request.setHeader('content-encoding', self.encoding)
315
316 try:
317 f = self.openForReading()
318 except IOError, e:
319 import errno
320 if e[0] == errno.EACCES:
321 return error.ForbiddenResource().render(request)
322 else:
323 raise
324
325 if request.setLastModified(self.getmtime()) is http.CACHED:
326 return ''
327
328 # Commented out because it's totally broken. --jknight 11/29/04
329 # try:
330 # range = request.getHeader('range')
331 #
332 # if range is not None:
333 # # This is a request for partial data...
334 # bytesrange = string.split(range, '=')
335 # assert bytesrange[0] == 'bytes',\
336 # "Syntactically invalid http range header!"
337 # start, end = string.split(bytesrange[1],'-')
338 # if start:
339 # f.seek(int(start))
340 # if end:
341 # end = int(end)
342 # size = end
343 # else:
344 # end = size
345 # request.setResponseCode(http.PARTIAL_CONTENT)
346 # request.setHeader('content-range',"bytes %s-%s/%s " % (
347 # str(start), str(end), str(size)))
348 # #content-length should be the actual size of the stuff we're
349 # #sending, not the full size of the on-server entity.
350 # fsize = end - int(start)
351 #
352 # request.setHeader('content-length', str(fsize))
353 # except:
354 # traceback.print_exc(file=log.logfile)
355
356 request.setHeader('content-length', str(fsize))
357 if request.method == 'HEAD':
358 return ''
359
360 # return data
361 FileTransfer(f, size, request)
362 # and make sure the connection doesn't get closed
363 return server.NOT_DONE_YET
364
365 def redirect(self, request):
366 return redirectTo(addSlash(request), request)
367
368 def listNames(self):
369 if not self.isdir():
370 return []
371 directory = self.listdir()
372 directory.sort()
373 return directory
374
375 def listEntities(self):
376 return map(lambda fileName, self=self: self.createSimilarFile(os.path.jo in(self.path, fileName)), self.listNames())
377
378 def createPickleChild(self, name, child):
379 if not os.path.isdir(self.path):
380 resource.Resource.putChild(self, name, child)
381 # xxx use a file-extension-to-save-function dictionary instead
382 if type(child) == type(""):
383 fl = open(os.path.join(self.path, name), 'wb')
384 fl.write(child)
385 else:
386 if '.' not in name:
387 name = name + '.trp'
388 fl = open(os.path.join(self.path, name), 'wb')
389 from pickle import Pickler
390 pk = Pickler(fl)
391 pk.dump(child)
392 fl.close()
393
394 def createSimilarFile(self, path):
395 f = self.__class__(path, self.defaultType, self.ignoredExts, self.regist ry)
396 # refactoring by steps, here - constructor should almost certainly take these
397 f.processors = self.processors
398 f.indexNames = self.indexNames[:]
399 f.childNotFound = self.childNotFound
400 return f
401
402 class FileTransfer(pb.Viewable):
403 """
404 A class to represent the transfer of a file over the network.
405 """
406 request = None
407
408 def __init__(self, file, size, request):
409 self.file = file
410 self.size = size
411 self.request = request
412 self.written = self.file.tell()
413 request.registerProducer(self, 0)
414
415 def resumeProducing(self):
416 if not self.request:
417 return
418 data = self.file.read(min(abstract.FileDescriptor.bufferSize, self.size - self.written))
419 if data:
420 self.written += len(data)
421 # this .write will spin the reactor, calling .doWrite and then
422 # .resumeProducing again, so be prepared for a re-entrant call
423 self.request.write(data)
424 if self.request and self.file.tell() == self.size:
425 self.request.unregisterProducer()
426 self.request.finish()
427 self.request = None
428
429 def pauseProducing(self):
430 pass
431
432 def stopProducing(self):
433 self.file.close()
434 self.request = None
435
436 # Remotely relay producer interface.
437
438 def view_resumeProducing(self, issuer):
439 self.resumeProducing()
440
441 def view_pauseProducing(self, issuer):
442 self.pauseProducing()
443
444 def view_stopProducing(self, issuer):
445 self.stopProducing()
446
447
448 synchronized = ['resumeProducing', 'stopProducing']
449
450 threadable.synchronize(FileTransfer)
451
452 """I contain AsIsProcessor, which serves files 'As Is'
453 Inspired by Apache's mod_asis
454 """
455
456 class ASISProcessor(resource.Resource):
457
458 def __init__(self, path, registry=None):
459 resource.Resource.__init__(self)
460 self.path = path
461 self.registry = registry or Registry()
462
463 def render(self, request):
464 request.startedWriting = 1
465 res = File(self.path, registry=self.registry)
466 return res.render(request)
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/web/soap.py ('k') | third_party/twisted_8_1/twisted/web/sux.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698