def handle(self):
        try:
            fixed_header_bs = self._readexactly(BoltRequest.bolt_header_size())
            header = BoltRequest.bolt_header_from_stream(fixed_header_bs)
            call_type = header['ptype']
            cmdcode = header['cmdcode']

            class_name = self._readexactly(header['class_len'])
            bs = self._readexactly(header['header_len'])
            sofa_header = SofaHeader.from_bytes(bs)
            body = self._readexactly(header['content_len'])
            if self.server.ready:
                self.server.ready.set()

            request_id = header['request_id']

            if cmdcode == CMDCODE.HEARTBEAT:
                self.wfile.write(HeartbeatResponse.response_to(request_id).to_stream())
                self.wfile.flush()
                return

            if cmdcode == CMDCODE.RESPONSE:
                raise ClientError("wrong cmdcode:[{}]".format(cmdcode))

            if class_name != "com.alipay.sofa.rpc.core.request.SofaRequest".encode():
                raise ClientError("wrong class_name:[{}]".format(class_name))

            service = sofa_header.get('sofa_head_target_service') or sofa_header.get('service')
            if not service:
                self.wfile.write(FailResponse.response_to(request_id, RESPSTATUS.CLIENT_SEND_ERROR).to_stream())
                self.wfile.flush()
                logger.error("Missing service name in sofa header [{}]".format(sofa_header))
                return
            method = sofa_header.get('sofa_head_method_name')
            if not method:
                self.wfile.write(FailResponse.response_to(request_id, RESPSTATUS.CLIENT_SEND_ERROR).to_stream())
                self.wfile.flush()
                logger.error("Missing method name in sofa header [{}]".format(sofa_header))
                return

            spanctx = tracer.extract(opentracing.Format.TEXT_MAP, sofa_header)
            ret = self.handle_request(spanctx, service, method, body)
            self.wfile.write(ret)
            self.wfile.flush()

        except OSError as e:
            if e.errno != ECONNRESET:
                raise

        except Exception as e:
            logger.error(traceback.format_exc())
Beispiel #2
0
    async def _handler_connection(self, reader, writer):
        """
        Full duplex model
        Only recv request here, run a new coro to process and send back response in same connection.
        """
        logger.info("connection created.")
        first_req = True
        while True:
            try:
                try:
                    fixed_header_bs = await reader.readexactly(
                        BoltRequest.bolt_header_size())
                except asyncio.IncompleteReadError:
                    if first_req:
                        # just connected, do nothing. most likely L4 health checks from mosn/upstream
                        await self._close_writer(writer)
                        break
                    # break the loop
                    raise

                first_req = False
                logger.debug(
                    "received bolt header({})".format(fixed_header_bs))
                header = BoltRequest.bolt_header_from_stream(fixed_header_bs)
                call_type = header['ptype']
                cmdcode = header['cmdcode']

                class_name = await reader.readexactly(header['class_len'])
                logger.debug("received classname({})".format(class_name))

                bs = await reader.readexactly(header['header_len'])
                logger.debug("received sofa header({})".format(bs))

                sofa_header = SofaHeader.from_bytes(bs)
                body = await reader.readexactly(header['content_len'])
                logger.debug("received sofa body({})".format(body))

                if cmdcode == CMDCODE.HEARTBEAT:
                    logger.info("received heartbeat, request_id={}".format(
                        header['request_id']))
                    asyncio.ensure_future(
                        self._write_msg(
                            writer,
                            HeartbeatResponse.response_to(
                                header['request_id']).to_stream()))
                    continue

                if cmdcode == CMDCODE.RESPONSE:
                    raise ClientError("wrong cmdcode:[{}]".format(cmdcode))

                if class_name != "com.alipay.sofa.rpc.core.request.SofaRequest".encode(
                ):
                    raise ClientError(
                        "wrong class_name:[{}]".format(class_name))

                logger.debug("dispatching request[{}]".format(
                    header['request_id']))
                asyncio.ensure_future(
                    self._dispatch(call_type,
                                   header['request_id'],
                                   sofa_header,
                                   body,
                                   timeout_ms=header['timeout'],
                                   codec=header['codec'],
                                   writer=writer))
            except ClientError as e:
                logger.error(str(e) + " returning CLIENT_SEND_ERROR")
                await self._write_msg(
                    writer,
                    FailResponse.response_to(
                        header['request_id'],
                        RESPSTATUS.CLIENT_SEND_ERROR).to_stream())
                continue

            except ProtocolError as e:
                logger.error(str(e) + " returning CODEC_EXCEPTION")
                await self._write_msg(
                    writer,
                    FailResponse.response_to(
                        header['request_id'],
                        RESPSTATUS.CODEC_EXCEPTION).to_stream())
                continue

            except EOFError as e:
                logger.warning("Connection closed by remote: {}".format(e))
                try:
                    await writer.drain(
                    )  # clean the buffer, avoid backpressure
                    writer.write(
                        FailResponse.response_to(
                            header['request_id'],
                            RESPSTATUS.CONNECTION_CLOSED).to_stream())
                except:
                    pass
                await self._close_writer(writer)
                break

            except Exception:
                logger.error("Unknow exception, close connection")
                logger.error(traceback.format_exc())
                try:
                    await writer.drain(
                    )  # clean the buffer, avoid backpressure
                    writer.write(
                        FailResponse.response_to(
                            header['request_id'],
                            RESPSTATUS.UNKNOWN).to_stream())
                except:
                    pass
                await self._close_writer(writer)
                break
Beispiel #3
0
    async def _dispatch(self, call_type, request_id, sofa_header, body, *,
                        timeout_ms, codec, writer):
        """send request to handler"""
        service = sofa_header.get(
            'sofa_head_target_service') or sofa_header.get('service')
        if not service:
            await self._write_msg(
                writer,
                FailResponse.response_to(request_id,
                                         RESPSTATUS.CLIENT_SEND_ERROR,
                                         codec=codec).to_stream(),
            )
            logger.error(
                "Missing service name in sofa header [{}]".format(sofa_header))
            return
        method = sofa_header.get('sofa_head_method_name')
        if not method:
            await self._write_msg(
                writer,
                FailResponse.response_to(request_id,
                                         RESPSTATUS.CLIENT_SEND_ERROR,
                                         codec=codec).to_stream())
            logger.error(
                "Missing method name in sofa header [{}]".format(sofa_header))
            return

        spanctx = tracer.extract(opentracing.Format.TEXT_MAP, sofa_header)

        try:
            future = self.handler.handle_request(spanctx, service, method,
                                                 body)
        except NoProcessorError:
            await self._write_msg(
                writer,
                FailResponse.response_to(request_id,
                                         RESPSTATUS.NO_PROCESSOR,
                                         codec=codec).to_stream(),
            )
            return
        except Exception:
            await self._write_msg(
                writer,
                FailResponse.response_to(request_id,
                                         RESPSTATUS.SERVER_EXCEPTION,
                                         codec=codec).to_stream(),
            )
            return
        if PTYPE.ONEWAY == call_type:
            # Just return future
            return future

        try:
            afut = asyncio.wrap_future(future)
            result = await asyncio.wait_for(
                afut, timeout_ms / 1000 if timeout_ms > 0 else None)
        except (asyncio.CancelledError, asyncio.TimeoutError) as e:
            logger.error(
                "Task run cancelled/timeout in {}ms, service:[{}]: error:[{}]".
                format(timeout_ms, service, e))
            await self._write_msg(
                writer,
                FailResponse.response_to(request_id,
                                         RESPSTATUS.TIMEOUT,
                                         codec=codec).to_stream(),
            )
            return
        except Exception:
            logger.error(traceback.format_exc())
            await self._write_msg(
                writer,
                FailResponse.response_to(request_id,
                                         RESPSTATUS.UNKNOWN,
                                         codec=codec).to_stream(),
            )
            return

        if result:
            header = dict()
            tracer.inject(spanctx, opentracing.Format.TEXT_MAP, header)
            pkg = BoltResponse.response_to(result,
                                           request_id=request_id,
                                           codec=codec)
            try:
                await self._write_msg(writer, pkg.to_stream())
            except Exception:
                logger.error(traceback.format_exc())
Beispiel #4
0
    def _handler_connection(self, reader, writer):
        """
        Full duplex model
        Only recv request here, run a new coro to process and send back response in same connection.
        """
        first_req = True
        while True:
            try:
                try:
                    fixed_header_bs = yield from reader.readexactly(
                        BoltRequest.bolt_header_size())
                except asyncio.streams.IncompleteReadError:
                    if first_req:
                        raise
                first_req = False
                header = BoltRequest.bolt_header_from_stream(fixed_header_bs)
                call_type = header['ptype']
                cmdcode = header['cmdcode']

                class_name = yield from reader.readexactly(header['class_len'])

                bs = yield from reader.readexactly(header['header_len'])
                sofa_header = SofaHeader.from_bytes(bs)
                body = yield from reader.readexactly(header['content_len'])

                if cmdcode == CMDCODE.HEARTBEAT:
                    asyncio.ensure_future(
                        self._write_msg(
                            writer,
                            HeartbeatResponse.response_to(
                                header['request_id']).to_stream()))
                    continue

                if cmdcode == CMDCODE.RESPONSE:
                    raise ClientError("wrong cmdcode:[{}]".format(cmdcode))

                if class_name != "com.alipay.sofa.rpc.core.request.SofaRequest".encode(
                ):
                    raise ClientError(
                        "wrong class_name:[{}]".format(class_name))

                asyncio.ensure_future(
                    self._dispatch(call_type,
                                   header['request_id'],
                                   sofa_header,
                                   body,
                                   timeout_ms=header['timeout'],
                                   writer=writer))
            except ClientError as e:
                logger.error(str(e))
                yield from self._write_msg(
                    writer,
                    FailResponse.response_to(
                        header['request_id'],
                        RESPSTATUS.CLIENT_SEND_ERROR).to_stream())
                continue

            except ProtocolError as e:
                logger.error(str(e))
                yield from self._write_msg(
                    writer,
                    FailResponse.response_to(
                        header['request_id'],
                        RESPSTATUS.CODEC_EXCEPTION).to_stream())
                continue

            except EOFError as e:
                logger.info("Connection closed: {}".format(e))
                try:
                    yield from writer.drain(
                    )  # clean the buffer, avoid backpressure
                    writer.write(
                        FailResponse.response_to(
                            header['request_id'],
                            RESPSTATUS.CONNECTION_CLOSED).to_stream())
                except:
                    pass
                try:
                    writer.write_eof()
                    yield from writer.drain()
                except:
                    pass
                writer.close()
                break

            except Exception:
                logger.error(traceback.format_exc())
                try:
                    yield from writer.drain(
                    )  # clean the buffer, avoid backpressure
                    writer.write(
                        FailResponse.response_to(
                            header['request_id'],
                            RESPSTATUS.UNKNOWN).to_stream())
                except:
                    pass
                try:
                    writer.write_eof()
                    yield from writer.drain()
                except:
                    pass
                writer.close()
                break