Esempio n. 1
0
    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)
Esempio n. 2
0
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()
Esempio n. 3
0
    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
Esempio n. 4
0
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()
Esempio n. 5
0
    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
Esempio n. 6
0
    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)