def _processRequest(self): """ Process the request by sending it to the relevant server. @return: the HTTP response. @rtype: L{Response} """ store = self.storeMap[self.server.details()] j = json.loads(self.data) if self.stream is not None: j["stream"] = self.stream j["streamType"] = self.streamType try: if store.conduit.isStreamAction(j): stream = ProducerStream() class StreamProtocol(Protocol): def connectionMade(self): stream.registerProducer(self.transport, False) def dataReceived(self, data): stream.write(data) def connectionLost(self, reason): stream.finish() result = yield store.conduit.processRequestStream( j, StreamProtocol()) try: ct, name = result except ValueError: code = responsecode.BAD_REQUEST else: headers = {"content-type": MimeType.fromString(ct)} headers["content-disposition"] = MimeDisposition( "attachment", params={"filename": name}) returnValue(Response(responsecode.OK, headers, stream)) else: result = yield store.conduit.processRequest(j) code = responsecode.OK except Exception as e: # Send the exception over to the other side result = { "result": "exception", "class": ".".join(( e.__class__.__module__, e.__class__.__name__, )), "details": str(e), } code = responsecode.BAD_REQUEST response = JSONResponse(code, result) returnValue(response)
class HTTPClientChannelRequest(HTTPParser): parseCloseAsEnd = True outgoing_version = "HTTP/1.1" chunkedOut = False finished = False closeAfter = False def __init__(self, channel, request, closeAfter): HTTPParser.__init__(self, channel) self.request = request self.closeAfter = closeAfter self.transport = self.channel.transport self.responseDefer = Deferred() def submit(self): l = [] request = self.request if request.method == "HEAD": # No incoming data will arrive. self.length = 0 l.append('%s %s %s\r\n' % (request.method, request.uri, self.outgoing_version)) if request.headers is not None: for name, valuelist in request.headers.getAllRawHeaders(): for value in valuelist: l.append("%s: %s\r\n" % (name, value)) if request.stream is not None: if request.stream.length is not None: l.append("%s: %s\r\n" % ('Content-Length', request.stream.length)) else: # Got a stream with no length. Send as chunked and hope, against # the odds, that the server actually supports chunked uploads. l.append("%s: %s\r\n" % ('Transfer-Encoding', 'chunked')) self.chunkedOut = True if self.closeAfter: l.append("%s: %s\r\n" % ('Connection', 'close')) else: l.append("%s: %s\r\n" % ('Connection', 'Keep-Alive')) l.append("\r\n") self.transport.writeSequence(l) d = StreamProducer(request.stream).beginProducing(self) d.addCallback(self._finish).addErrback(self._error) def registerProducer(self, producer, streaming): """ Register a producer. """ self.transport.registerProducer(producer, streaming) def unregisterProducer(self): self.transport.unregisterProducer() def write(self, data): if not data: return elif self.chunkedOut: self.transport.writeSequence(("%X\r\n" % len(data), data, "\r\n")) else: self.transport.write(data) def _finish(self, x): """ We are finished writing data. """ if self.chunkedOut: # write last chunk and closing CRLF self.transport.write("0\r\n\r\n") self.finished = True self.channel.requestWriteFinished(self) del self.transport def _error(self, err): """ Abort parsing, and depending of the status of the request, either fire the C{responseDefer} if no response has been sent yet, or close the stream. """ self.abortParse() if hasattr(self, 'stream') and self.stream is not None: self.stream.finish(err) else: self.responseDefer.errback(err) def _abortWithError(self, errcode, text): """ Abort parsing by forwarding a C{ProtocolError} to C{_error}. """ self._error(ProtocolError(text)) def connectionLost(self, reason): self._error(reason) def gotInitialLine(self, initialLine): parts = initialLine.split(' ', 2) # Parse the initial request line if len(parts) != 3: self._abortWithError(BAD_REQUEST, "Bad response line: %s" % (initialLine,)) return strversion, self.code, message = parts try: protovers = parseVersion(strversion) if protovers[0] != 'http': raise ValueError() except ValueError: self._abortWithError(BAD_REQUEST, "Unknown protocol: %s" % (strversion,)) return self.version = protovers[1:3] # Ensure HTTP 0 or HTTP 1. if self.version[0] != 1: self._abortWithError(HTTP_VERSION_NOT_SUPPORTED, 'Only HTTP 1.x is supported.') return ## FIXME: Actually creates Response, function is badly named! def createRequest(self): self.stream = ProducerStream(self.length) self.response = Response(self.code, self.inHeaders, self.stream) self.stream.registerProducer(self, True) del self.inHeaders ## FIXME: Actually processes Response, function is badly named! def processRequest(self): self.responseDefer.callback(self.response) def handleContentChunk(self, data): self.stream.write(data) def handleContentComplete(self): self.stream.finish()
def createRequest(self): self.stream = ProducerStream(self.length) self.response = Response(self.code, self.inHeaders, self.stream) self.stream.registerProducer(self, True) del self.inHeaders
class HTTPClientChannelRequest(HTTPParser): parseCloseAsEnd = True outgoing_version = "HTTP/1.1" chunkedOut = False finished = False closeAfter = False def __init__(self, channel, request, closeAfter): HTTPParser.__init__(self, channel) self.request = request self.closeAfter = closeAfter self.transport = self.channel.transport self.responseDefer = Deferred() def submit(self): l = [] request = self.request if request.method == "HEAD": # No incoming data will arrive. self.length = 0 l.append('%s %s %s\r\n' % (request.method, request.uri, self.outgoing_version)) if request.headers is not None: for name, valuelist in request.headers.getAllRawHeaders(): for value in valuelist: l.append("%s: %s\r\n" % (name, value)) if request.stream is not None: if request.stream.length is not None: l.append("%s: %s\r\n" % ('Content-Length', request.stream.length)) else: # Got a stream with no length. Send as chunked and hope, against # the odds, that the server actually supports chunked uploads. l.append("%s: %s\r\n" % ('Transfer-Encoding', 'chunked')) self.chunkedOut = True if self.closeAfter: l.append("%s: %s\r\n" % ('Connection', 'close')) else: l.append("%s: %s\r\n" % ('Connection', 'Keep-Alive')) l.append("\r\n") self.transport.writeSequence(l) d = StreamProducer(request.stream).beginProducing(self) d.addCallback(self._finish).addErrback(self._error) def registerProducer(self, producer, streaming): """ Register a producer. """ self.transport.registerProducer(producer, streaming) def unregisterProducer(self): self.transport.unregisterProducer() def write(self, data): if not data: return elif self.chunkedOut: self.transport.writeSequence(("%X\r\n" % len(data), data, "\r\n")) else: self.transport.write(data) def _finish(self, x): """ We are finished writing data. """ if self.chunkedOut: # write last chunk and closing CRLF self.transport.write("0\r\n\r\n") self.finished = True self.channel.requestWriteFinished(self) del self.transport def _error(self, err): """ Abort parsing, and depending of the status of the request, either fire the C{responseDefer} if no response has been sent yet, or close the stream. """ self.abortParse() if hasattr(self, 'stream') and self.stream is not None: self.stream.finish(err) else: self.responseDefer.errback(err) def _abortWithError(self, errcode, text): """ Abort parsing by forwarding a C{ProtocolError} to C{_error}. """ self._error(ProtocolError(text)) def connectionLost(self, reason): self._error(reason) def gotInitialLine(self, initialLine): parts = initialLine.split(' ', 2) # Parse the initial request line if len(parts) != 3: self._abortWithError(BAD_REQUEST, "Bad response line: %s" % (initialLine, )) return strversion, self.code, message = parts try: protovers = parseVersion(strversion) if protovers[0] != 'http': raise ValueError() except ValueError: self._abortWithError(BAD_REQUEST, "Unknown protocol: %s" % (strversion, )) return self.version = protovers[1:3] # Ensure HTTP 0 or HTTP 1. if self.version[0] != 1: self._abortWithError(HTTP_VERSION_NOT_SUPPORTED, 'Only HTTP 1.x is supported.') return ## FIXME: Actually creates Response, function is badly named! def createRequest(self): self.stream = ProducerStream(self.length) self.response = Response(self.code, self.inHeaders, self.stream) self.stream.registerProducer(self, True) del self.inHeaders ## FIXME: Actually processes Response, function is badly named! def processRequest(self): self.responseDefer.callback(self.response) def handleContentChunk(self, data): self.stream.write(data) def handleContentComplete(self): self.stream.finish()
def http_POST(self, request): """ The server-to-server POST method. """ # Check shared secret if not self.store.directoryService().serversDB().getThisServer( ).checkSharedSecret(request.headers): self.log.error("Invalid shared secret header in cross-pod request") raise HTTPError( StatusResponse(responsecode.FORBIDDEN, "Not authorized to make this request")) # Look for XPOD header xpod = request.headers.getRawHeaders("XPOD") contentType = request.headers.getHeader("content-type") if xpod is not None: # Attachments are sent in the request body with the JSON data in a header. We # decode the header and add the request.stream as an attribute of the JSON object. xpod = xpod[0] try: j = json.loads(base64.b64decode(xpod)) except (TypeError, ValueError) as e: self.log.error("Invalid JSON header in request: {ex}\n{xpod}", ex=e, xpod=xpod) raise HTTPError( StatusResponse( responsecode.BAD_REQUEST, "Invalid JSON header in request: {}\n{}".format( e, xpod))) j["stream"] = request.stream j["streamType"] = contentType else: # Check content first if "{}/{}".format(contentType.mediaType, contentType.mediaSubtype) != "application/json": self.log.error("MIME type {mime} not allowed in request", mime=contentType) raise HTTPError( StatusResponse( responsecode.BAD_REQUEST, "MIME type {} not allowed in request".format( contentType))) body = (yield allDataFromStream(request.stream)) try: j = json.loads(body) except ValueError as e: self.log.error("Invalid JSON data in request: {ex}\n{body}", ex=e, body=body) raise HTTPError( StatusResponse( responsecode.BAD_REQUEST, "Invalid JSON data in request: {}\n{}".format(e, body))) # Log extended item if not hasattr(request, "extendedLogItems"): request.extendedLogItems = {} request.extendedLogItems[ "xpod"] = j["action"] if "action" in j else "unknown" # Look for a streaming action which needs special handling if self.store.conduit.isStreamAction(j): # Get the conduit to process the data stream try: stream = ProducerStream() class StreamProtocol(Protocol): def connectionMade(self): stream.registerProducer(self.transport, False) def dataReceived(self, data): stream.write(data) def connectionLost(self, reason): stream.finish() result = yield self.store.conduit.processRequestStream( j, StreamProtocol()) try: ct, name = result except ValueError: code = responsecode.BAD_REQUEST else: headers = {"content-type": MimeType.fromString(ct)} headers["content-disposition"] = MimeDisposition( "attachment", params={"filename": name}) returnValue(Response(responsecode.OK, headers, stream)) except Exception as e: # Send the exception over to the other side result = { "result": "exception", "class": ".".join(( e.__class__.__module__, e.__class__.__name__, )), "details": str(e), } code = responsecode.BAD_REQUEST else: # Get the conduit to process the data try: result = yield self.store.conduit.processRequest(j) code = responsecode.OK if result[ "result"] == "ok" else responsecode.BAD_REQUEST except Exception as e: # Send the exception over to the other side result = { "result": "exception", "class": ".".join(( e.__class__.__module__, e.__class__.__name__, )), "details": str(e), } code = responsecode.BAD_REQUEST response = JSONResponse(code, result) returnValue(response)