def testContentType(self): table = ( ("text/html;charset=iso-8859-4", http_headers.MimeType('text', 'html', (('charset', 'iso-8859-4'), ))), ("text/html", http_headers.MimeType('text', 'html')), ) self.runRoundtripTest("Content-Type", table)
def test_dataState(self): """ Test the internal state of the Data object """ self.assert_(hasattr(self.data, "created_time")) self.assertEquals(self.data.data, self.text) self.assertEquals(self.data.type, http_headers.MimeType("text", "plain")) self.assertEquals(self.data.contentType(), http_headers.MimeType("text", "plain"))
def uploadFile(self, fieldname, filename, mimetype, content, resrc=None, host='foo', path='/'): if not resrc: resrc = self.root ctype = http_headers.MimeType('multipart', 'form-data', (('boundary', '---weeboundary'), )) return self.getResponseFor(resrc, '/', headers={ 'host': 'foo', 'content-type': ctype }, length=len(content), method='POST', content="""-----weeboundary\r Content-Disposition: form-data; name="%s"; filename="%s"\r Content-Type: %s\r \r %s\r -----weeboundary--\r """ % (fieldname, filename, mimetype, content))
def test_multipartMaxSize(self): """ Check that the C{maxSize} parameter makes the parsing raise an exception if the data is too big. """ ctype = http_headers.MimeType('multipart', 'form-data', (('boundary', '---weeboundary'), )) content = """-----weeboundary\r Content-Disposition: form-data; name="FileNameOne"; filename="myfilename"\r Content-Type: text/html\r \r my great content wooo and even more and more\r -----weeboundary--\r """ root = resource.Resource() request = SimpleRequest(server.Site(root), "GET", "/", http_headers.Headers({'content-type': ctype}), content) def cb(res): self.assertEquals(res.response.description, "Maximum length of 10 bytes exceeded.") return self.assertFailure(server.parsePOSTData(request, maxSize=10), http.HTTPError).addCallback(cb)
def __init__(self, code, description, title=None): """ @param code: a response code in L{responsecode.RESPONSES}. @param description: a string description. @param title: the message title. If not specified or C{None}, defaults to C{responsecode.RESPONSES[code]}. """ if title is None: title = cgi.escape(responsecode.RESPONSES[code]) output = "".join(( "<html>", "<head>", "<title>%s</title>" % (title,), "</head>", "<body>", "<h1>%s</h1>" % (title,), "<p>%s</p>" % (cgi.escape(description),), "</body>", "</html>", )) if type(output) == unicode: output = output.encode("utf-8") mime_params = {"charset": "utf-8"} else: mime_params = {} super(StatusResponse, self).__init__(code=code, stream=output) self.headers.setHeader("content-type", http_headers.MimeType("text", "html", mime_params)) self.description = description
def render(self, request): title = "Directory listing for %s" % urllib.unquote(request.path) s = """<html><head><title>%s</title><style> th, .even td, .odd td { padding-right: 0.5em; font-family: monospace} .even-dir { background-color: #efe0ef } .even { background-color: #eee } .odd-dir {background-color: #f0d0ef } .odd { background-color: #dedede } .icon { text-align: center } .listing { margin-left: auto; margin-right: auto; width: 50%%; padding: 0.1em; } body { border: 0; padding: 0; margin: 0; background-color: #efefef;} h1 {padding: 0.1em; background-color: #777; color: white; border-bottom: thin white dashed;} </style></head><body><div class="directory-listing"><h1>%s</h1>""" % (title, title) s += "<table>" s += "<tr><th>Filename</th><th>Size</th><th>Last Modified</th><th>File Type</th></tr>" even = False for row in self.data_listing(request, None): s += '<tr class="%s">' % (even and 'even' or 'odd', ) s += '<td><a href="%(link)s">%(linktext)s</a></td><td align="right">%(size)s</td><td>%(lastmod)s</td><td>%(type)s</td></tr>' % row even = not even s += "</table></div></body></html>" response = http.Response(200, {}, s) response.headers.setHeader("content-type", http_headers.MimeType('text', 'html')) return response
def defaultErrorHandler(request, response): if response.stream is not None: # Already got an error message return response if response.code < 300: # We only do error messages return response message = ERROR_MESSAGES.get(response.code, None) if message is None: # No message specified for that code return response message = message % { 'uri': _escape(request.uri), 'location': _escape(response.headers.getHeader('location')), 'method': _escape(request.method) } title = RESPONSES.get(response.code, "") body = ("<html><head><title>%d %s</title></head>" "<body><h1>%s</h1>%s</body></html>") % (response.code, title, title, message) response.headers.setHeader("content-type", http_headers.MimeType('text', 'html')) response.stream = stream.MemoryStream(body) return response
def test_multipart(self): """ Test parsing data in multipart format: it should fill the C{files} attribute. """ ctype = http_headers.MimeType('multipart', 'form-data', (('boundary', '---weeboundary'), )) content = """-----weeboundary\r Content-Disposition: form-data; name="FileNameOne"; filename="myfilename"\r Content-Type: text/html\r \r my great content wooo\r -----weeboundary--\r """ root = resource.Resource() request = SimpleRequest(server.Site(root), "GET", "/", http_headers.Headers({'content-type': ctype}), content) def cb(ign): self.assertEquals(request.args, {}) self.assertEquals(request.files.keys(), ['FileNameOne']) self.assertEquals( request.files.values()[0][0][:2], ('myfilename', http_headers.MimeType('text', 'html', {}))) f = request.files.values()[0][0][2] self.assertEquals(f.read(), "my great content wooo") return server.parsePOSTData(request).addCallback(cb)
def cb(ign): self.assertEquals(request.args, {}) self.assertEquals(request.files.keys(), ['FileNameOne']) self.assertEquals( request.files.values()[0][0][:2], ('myfilename', http_headers.MimeType('text', 'html', {}))) f = request.files.values()[0][0][2] self.assertEquals(f.read(), "my great content wooo")
def render(self, request): # For GET/HEAD: Return an error message s = ( "<html><head><title>XML-RPC responder</title></head>" "<body><h1>XML-RPC responder</h1>POST your XML-RPC here.</body></html>" ) return http.Response( responsecode.OK, {'content-type': http_headers.MimeType('text', 'html')}, s)
def _cbRender(self, result, request): if not isinstance(result, Fault): result = (result, ) try: s = xmlrpclib.dumps(result, methodresponse=1) except: f = Fault(self.FAILURE, "can't serialize output") s = xmlrpclib.dumps(f, methodresponse=1) return http.Response( responsecode.OK, {'content-type': http_headers.MimeType('text', 'xml')}, s)
def test_wrongContentType(self): """ Check that a content-type not handled raise a C{http.HTTPError}. """ ctype = http_headers.MimeType('application', 'foobar') content = "key=value&multiple=two+words&multiple=more%20words" root = resource.Resource() request = SimpleRequest(server.Site(root), "GET", "/", http_headers.Headers({'content-type': ctype}), content) return self.assertFailure(server.parsePOSTData(request), http.HTTPError)
def renderHTTP_exception(self, req, reason): log.msg("Exception rendering:", isErr=1) log.err(reason) body = ( "<html><head><title>Internal Server Error</title></head>" "<body><h1>Internal Server Error</h1>An error occurred rendering the requested page. More information is available in the server log.</body></html>" ) return http.Response( responsecode.INTERNAL_SERVER_ERROR, {'content-type': http_headers.MimeType('text', 'html')}, body)
def testEquality(self): """Test that various uses of the constructer are equal """ kwargMime = http_headers.MimeType('text', 'plain', key='value', param=None) dictMime = http_headers.MimeType('text', 'plain', { 'param': None, 'key': 'value' }) tupleMime = http_headers.MimeType('text', 'plain', (('param', None), ('key', 'value'))) stringMime = http_headers.MimeType.fromString( 'text/plain;key=value;param') self.assertEquals(kwargMime, dictMime) self.assertEquals(dictMime, tupleMime) self.assertEquals(kwargMime, tupleMime) self.assertEquals(kwargMime, stringMime)
def doTrace(request): request = iweb.IRequest(request) txt = "%s %s HTTP/%d.%d\r\n" % (request.method, request.uri, request.clientproto[0], request.clientproto[1]) l = [] for name, valuelist in request.headers.getAllRawHeaders(): for value in valuelist: l.append("%s: %s\r\n" % (name, value)) txt += ''.join(l) return http.Response( responsecode.OK, {'content-type': http_headers.MimeType('message', 'http')}, txt)
def _processingReallyFailed(self, reason, origReason): log.msg("Exception rendering error page:", isErr=1) log.err(reason) log.msg("Original exception:", isErr=1) log.err(origReason) body = ( "<html><head><title>Internal Server Error</title></head>" "<body><h1>Internal Server Error</h1>An error occurred rendering the requested page. Additionally, an error occured rendering the error page.</body></html>" ) response = http.Response( responsecode.INTERNAL_SERVER_ERROR, {'content-type': http_headers.MimeType('text', 'html')}, body) self.writeResponse(response)
def test_render(self): """ Test that the result from Data.render is acceptable, including the response code, the content-type header, and the actual response body itself. """ response = iweb.IResponse(self.data.render(None)) self.assertEqual(response.code, 200) self.assert_(response.headers.hasHeader("content-type")) self.assertEqual(response.headers.getHeader("content-type"), http_headers.MimeType("text", "plain")) def checkStream(data): self.assertEquals(str(data), self.text) return stream.readStream( iweb.IResponse(self.data.render(None)).stream, checkStream)
def _readHeaders(stream): """Read the MIME headers. Assumes we've just finished reading in the boundary string.""" ctype = fieldname = filename = None headers = [] # Now read headers while 1: line = stream.readline(size=1024) if isinstance(line, defer.Deferred): line = defer.waitForDeferred(line) yield line line = line.getResult() #print "GOT", line if not line.endswith('\r\n'): if line == "": raise MimeFormatError("Unexpected end of stream.") else: raise MimeFormatError("Header line too long") line = line[:-2] # strip \r\n if line == "": break # End of headers parts = line.split(':', 1) if len(parts) != 2: raise MimeFormatError("Header did not have a :") name, value = parts name = name.lower() headers.append((name, value)) if name == "content-type": ctype = http_headers.parseContentType( http_headers.tokenize((value, ), foldCase=False)) elif name == "content-disposition": fieldname, filename = parseContentDispositionFormData(value) if ctype is None: ctype == http_headers.MimeType('application', 'octet-stream') if fieldname is None: raise MimeFormatError('Content-disposition invalid or omitted.') # End of headers, return (field name, content-type, filename) yield fieldname, filename, ctype return
def test_otherErrors(self): """ Test that errors durign parsing other than C{MimeFormatError} are propagated. """ ctype = http_headers.MimeType('multipart', 'form-data', (('boundary', '---weeboundary'), )) # XXX: maybe this is not a good example # parseContentDispositionFormData could handle this problem content = """-----weeboundary\r Content-Disposition: form-data; name="FileNameOne"; filename="myfilename and invalid data \r -----weeboundary--\r """ root = resource.Resource() request = SimpleRequest(server.Site(root), "GET", "/", http_headers.Headers({'content-type': ctype}), content) return self.assertFailure(server.parsePOSTData(request), ValueError)
def test_multipartWithNoBoundary(self): """ If the boundary type is not specified, parsing should fail with a C{http.HTTPError}. """ ctype = http_headers.MimeType('multipart', 'form-data') content = """-----weeboundary\r Content-Disposition: form-data; name="FileNameOne"; filename="myfilename"\r Content-Type: text/html\r \r my great content wooo\r -----weeboundary--\r """ root = resource.Resource() request = SimpleRequest(server.Site(root), "GET", "/", http_headers.Headers({'content-type': ctype}), content) return self.assertFailure(server.parsePOSTData(request), http.HTTPError)
def test_urlencoded(self): """ Test parsing data in urlencoded format: it should end in the C{args} attribute. """ ctype = http_headers.MimeType('application', 'x-www-form-urlencoded') content = "key=value&multiple=two+words&multiple=more%20words" root = resource.Resource() request = SimpleRequest(server.Site(root), "GET", "/", http_headers.Headers({'content-type': ctype}), content) def cb(ign): self.assertEquals(request.files, {}) self.assertEquals(request.args, { 'multiple': ['two words', 'more words'], 'key': ['value'] }) return server.parsePOSTData(request).addCallback(cb)
def test_mimeParsingError(self): """ A malformed content should result in a C{http.HTTPError}. The tested content has an invalid closing boundary. """ ctype = http_headers.MimeType('multipart', 'form-data', (('boundary', '---weeboundary'), )) content = """-----weeboundary\r Content-Disposition: form-data; name="FileNameOne"; filename="myfilename"\r Content-Type: text/html\r \r my great content wooo\r -----weeoundary--\r """ root = resource.Resource() request = SimpleRequest(server.Site(root), "GET", "/", http_headers.Headers({'content-type': ctype}), content) return self.assertFailure(server.parsePOSTData(request), http.HTTPError)
def test_maxFields(self): """ Check that the C{maxSize} parameter makes the parsing raise an exception if the data contains too many fields. """ ctype = http_headers.MimeType('multipart', 'form-data', (('boundary', '---xyz'), )) content = """-----xyz\r Content-Disposition: form-data; name="foo"\r \r Foo Bar\r -----xyz\r Content-Disposition: form-data; name="foo"\r \r Baz\r -----xyz\r Content-Disposition: form-data; name="file"; filename="filename"\r Content-Type: text/html\r \r blah\r -----xyz\r Content-Disposition: form-data; name="file"; filename="filename"\r Content-Type: text/plain\r \r bleh\r -----xyz--\r """ root = resource.Resource() request = SimpleRequest(server.Site(root), "GET", "/", http_headers.Headers({'content-type': ctype}), content) def cb(res): self.assertEquals(res.response.description, "Maximum number of fields 3 exceeded") return self.assertFailure(server.parsePOSTData(request, maxFields=3), http.HTTPError).addCallback(cb)
class FileSaver(resource.PostableResource): allowedTypes = (http_headers.MimeType('text', 'plain'), http_headers.MimeType('text', 'html'), http_headers.MimeType('text', 'css')) def __init__(self, destination, expectedFields=[], allowedTypes=None, maxBytes=1000000, permissions=0644): self.destination = destination self.allowedTypes = allowedTypes or self.allowedTypes self.maxBytes = maxBytes self.expectedFields = expectedFields self.permissions = permissions def makeUniqueName(self, filename): """Called when a unique filename is needed. filename is the name of the file as given by the client. Returns the fully qualified path of the file to create. The file must not yet exist. """ return tempfile.mktemp(suffix=os.path.splitext(filename)[1], dir=self.destination) def isSafeToWrite(self, filename, mimetype, filestream): """Returns True if it's "safe" to write this file, otherwise it raises an exception. """ if filestream.length > self.maxBytes: raise IOError("%s: File exceeds maximum length (%d > %d)" % (filename, filestream.length, self.maxBytes)) if mimetype not in self.allowedTypes: raise IOError("%s: File type not allowed %s" % (filename, mimetype)) return True def writeFile(self, filename, mimetype, fileobject): """Does the I/O dirty work after it calls isSafeToWrite to make sure it's safe to write this file. """ filestream = stream.FileStream(fileobject) if self.isSafeToWrite(filename, mimetype, filestream): outname = self.makeUniqueName(filename) flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr( os, "O_BINARY", 0) fileobject = os.fdopen(os.open(outname, flags, self.permissions), 'wb', 0) stream.readIntoFile(filestream, fileobject) return outname def render(self, req): content = ["<html><body>"] if req.files: for fieldName in req.files: if fieldName in self.expectedFields: for finfo in req.files[fieldName]: try: outname = self.writeFile(*finfo) content.append("Saved file %s<br />" % outname) except IOError, err: content.append(str(err) + "<br />") else: content.append("%s is not a valid field" % fieldName) else:
def testAccept(self): table = ( ("audio/*;q=0.2, audio/basic", { http_headers.MimeType('audio', '*'): 0.2, http_headers.MimeType('audio', 'basic'): 1.0 }), ("text/plain;q=0.5, text/html, text/x-dvi;q=0.8, text/x-c", { http_headers.MimeType('text', 'plain'): 0.5, http_headers.MimeType('text', 'html'): 1.0, http_headers.MimeType('text', 'x-dvi'): 0.8, http_headers.MimeType('text', 'x-c'): 1.0 }), ("text/*, text/html, text/html;level=1, */*", { http_headers.MimeType('text', '*'): 1.0, http_headers.MimeType('text', 'html'): 1.0, http_headers.MimeType('text', 'html', (('level', '1'), )): 1.0, http_headers.MimeType('*', '*'): 1.0 }), ("text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5", { http_headers.MimeType('text', '*'): 0.3, http_headers.MimeType('text', 'html'): 0.7, http_headers.MimeType('text', 'html', (('level', '1'), )): 1.0, http_headers.MimeType('text', 'html', (('level', '2'), )): 0.4, http_headers.MimeType('*', '*'): 0.5 }), ) self.runRoundtripTest("Accept", table)
def rangefilter(request, oldresponse): if oldresponse.stream is None: return oldresponse size = oldresponse.stream.length if size is None: # Does not deal with indeterminate length outputs return oldresponse oldresponse.headers.setHeader('accept-ranges', ('bytes', )) rangespec = request.headers.getHeader('range') # If we've got a range header and the If-Range header check passes, and # the range type is bytes, do a partial response. if (rangespec is not None and http.checkIfRange(request, oldresponse) and rangespec[0] == 'bytes'): # If it's a single range, return a simple response if len(rangespec[1]) == 1: try: start, end = canonicalizeRange(rangespec[1][0], size) except UnsatisfiableRangeRequest: return makeUnsatisfiable(request, oldresponse) response = http.Response(responsecode.PARTIAL_CONTENT, oldresponse.headers) response.headers.setHeader('content-range', ('bytes', start, end, size)) content, after = makeSegment(oldresponse.stream, 0, start, end) after.close() response.stream = content return response else: # Return a multipart/byteranges response lastOffset = -1 offsetList = [] for arange in rangespec[1]: try: start, end = canonicalizeRange(arange, size) except UnsatisfiableRangeRequest: continue if start <= lastOffset: # Stupid client asking for out-of-order or overlapping ranges, PUNT! return oldresponse offsetList.append((start, end)) lastOffset = end if not offsetList: return makeUnsatisfiable(request, oldresponse) content_type = oldresponse.headers.getRawHeaders('content-type') boundary = "%x%x" % (int(time.time() * 1000000), os.getpid()) response = http.Response(responsecode.PARTIAL_CONTENT, oldresponse.headers) response.headers.setHeader( 'content-type', http_headers.MimeType('multipart', 'byteranges', [('boundary', boundary)])) response.stream = out = stream.CompoundStream() lastOffset = 0 origStream = oldresponse.stream headerString = "\r\n--%s" % boundary if len(content_type) == 1: headerString += '\r\nContent-Type: %s' % content_type[0] headerString += "\r\nContent-Range: %s\r\n\r\n" for start, end in offsetList: out.addStream(headerString % http_headers.generateContentRange( ('bytes', start, end, size))) content, origStream = makeSegment(origStream, lastOffset, start, end) lastOffset = end + 1 out.addStream(content) origStream.close() out.addStream("\r\n--%s--\r\n" % boundary) return response else: return oldresponse