Esempio n. 1
0
    def call_processor(self, input, client_type, protocol_type,
                       client_principal):
        try:
            # The input string has already had the header removed, but
            # the python processor will expect it to be there.  In
            # order to reconstitute the message with headers, we use
            # the THeaderProtocol object to write into a memory
            # buffer, then pass that buffer to the python processor.

            write_buf = TMemoryBuffer()
            trans = THeaderTransport(write_buf, client_types=[client_type])
            trans.set_protocol_id(protocol_type)
            trans.write(input)
            trans.flush()

            prot_buf = TMemoryBuffer(write_buf.getvalue())
            prot = THeaderProtocol(prot_buf)

            ctx = TCppConnectionContext(client_principal)

            self.processor.process(prot, prot, ctx)

            # And on the way out, we need to strip off the header,
            # because the C++ code will expect to add it.

            read_buf = TMemoryBuffer(prot_buf.getvalue())
            trans = THeaderTransport(read_buf, client_types=[client_type])
            trans.readFrame(0)

            return trans.cstringio_buf.read()
        except:
            # Don't let exceptions escape back into C++
            traceback.print_exc()
Esempio n. 2
0
    def round_robin(self, compress=None):
        original = b"A" * MAX_FRAME_SIZE
        mb = TMemoryBuffer()
        trans = THeaderTransport(mb, client_type=CLIENT_TYPE.HEADER)
        trans.set_max_frame_size(MAX_FRAME_SIZE + MIN_HEADER_SIZE)
        if compress:
            trans.add_transform(compress)
        trans.write(original)
        trans.flush()
        frame = mb.getvalue()
        # Cleanup the memory buffer
        mb.close()
        del mb

        if compress is None:
            # Partial Decode the frame and see if its correct size wise
            sz = struct.unpack("!I", frame[:4])[0]
            self.assertEqual(sz, BIG_FRAME_MAGIC)
            sz = struct.unpack("!Q", frame[4:12])[0]
            self.assertEqual(len(frame), sz + 12)

        # Read it back
        mb = TMemoryBuffer(frame)
        trans = THeaderTransport(mb, client_type=CLIENT_TYPE.HEADER)
        trans.set_max_frame_size(len(frame))
        trans.readFrame(0)
        result = trans.read(MAX_FRAME_SIZE)
        mb.close()
        del mb
        self.assertEqual(result, original,
                         "round-robin different from original")
Esempio n. 3
0
def create_thrift_client(
    eden_dir: "Optional[str]" = None,
    socket_path: "Optional[str]" = None,
    timeout: "Optional[float]" = None,
) -> "EdenClient":
    """
    Construct a thrift client to speak to the running eden server
    instance associated with the specified mount point.

    @return Returns a context manager for EdenService.Client.
    """

    if socket_path is not None:
        pass
    elif eden_dir is not None:
        socket_path = os.path.join(eden_dir, SOCKET_PATH)
    else:
        raise TypeError("one of eden_dir or socket_path is required")
    if sys.platform == "win32":
        socket = WinTSocket(unix_socket=socket_path)
    else:
        socket = TSocket(unix_socket=socket_path)

    # We used to set a default timeout here, but picking the right duration is hard,
    # and safely retrying an arbitrary thrift call may not be safe.  So we
    # just leave the client with no timeout, unless one is given.
    if timeout is None:
        timeout_ms = None
    else:
        timeout_ms = timeout * 1000
    socket.setTimeout(timeout_ms)

    transport = THeaderTransport(socket)
    protocol = THeaderProtocol(transport)
    return EdenClient(socket_path, transport, protocol)
Esempio n. 4
0
    def _get_client_by_transport(self, options, transport, socket=None):
        # Create the protocol and client
        if options.json:
            protocol = TJSONProtocol.TJSONProtocol(transport)
        elif options.compact:
            protocol = TCompactProtocol.TCompactProtocol(transport)

        # No explicit option about protocol is specified. Try to infer.
        elif options.framed or options.unframed:
            protocol = TBinaryProtocol.TBinaryProtocolAccelerated(transport)

        elif socket is not None:
            # If json, compact, framed, and unframed are not specified,
            # THeaderProtocol is the default. Create a protocol using either
            # fuzzy or non-fuzzy transport depending on if options.fuzz is set.
            if options.fuzz is not None:
                transport = TFuzzyHeaderTransport(socket,
                                                  fuzz_fields=options.fuzz,
                                                  verbose=True)
            else:
                transport = THeaderTransport(socket)
            protocol = THeaderProtocol.THeaderProtocol(transport)
        else:
            self._exit(error_message=('No valid protocol '
                                      'specified for %s' % (type(self))),
                       status=os.EX_USAGE)

        transport.open()
        self._transport = transport

        client = self.service_class.Client(protocol)
        return client
Esempio n. 5
0
    def __init__(self,
                 trans,
                 strictRead=False,
                 client_types=None,
                 client_type=None):
        """Create a THeaderProtocol instance

        @param transport(TTransport) The underlying transport.
        @param strictRead(bool) Turn on strictRead if using TBinaryProtocol
        @param client_types([CLIENT_TYPE.HEADER, ...])
                   List of client types to support.  Defaults to
                   CLIENT_TYPE.HEADER only.
        """

        if isinstance(trans, THeaderTransport):
            trans._THeaderTransport__supported_client_types = set(
                client_types or (CLIENT_TYPE.HEADER, ))
            if client_type is not None:
                trans._THeaderTransport__client_type = client_type
            htrans = trans
        else:
            htrans = THeaderTransport(trans, client_types, client_type)
        TProtocolBase.__init__(self, htrans)
        self.strictRead = strictRead
        self.reset_protocol()
    def setUp(self):
        self.trans = TTransport.TMemoryBuffer()
        self.trans = THeaderTransport(self.trans)
        self.prot = TBinaryProtocol.TBinaryProtocol(self.trans)

        self.x = Xtruct()
        self.x.string_thing = "Zero"
        self.x.byte_thing = 0
Esempio n. 7
0
    def call_processor(self, input, headers, client_type, protocol_type,
                       context_data):
        try:
            # The input string has already had the header removed, but
            # the python processor will expect it to be there.  In
            # order to reconstitute the message with headers, we use
            # the THeaderProtocol object to write into a memory
            # buffer, then pass that buffer to the python processor.

            write_buf = TMemoryBuffer()
            trans = THeaderTransport(write_buf)
            trans._THeaderTransport__client_type = client_type
            trans._THeaderTransport__write_headers = headers
            trans.set_protocol_id(protocol_type)
            trans.write(input)
            trans.flush()

            prot_buf = TMemoryBuffer(write_buf.getvalue())
            prot = THeaderProtocol(prot_buf, client_types=[client_type])

            ctx = TCppConnectionContext(context_data)

            self.processor.process(prot, prot, ctx)

            # Check for empty result. If so, return an empty string
            # here.  This is probably a oneway request, but we can't
            # reliably tell.  The C++ code does basically the same
            # thing.

            response = prot_buf.getvalue()
            if len(response) == 0:
                return response

            # And on the way out, we need to strip off the header,
            # because the C++ code will expect to add it.

            read_buf = TMemoryBuffer(response)
            trans = THeaderTransport(read_buf, client_types=[client_type])
            trans.readFrame(len(response))

            return trans.cstringio_buf.read()
        except:
            # Don't let exceptions escape back into C++
            traceback.print_exc()
Esempio n. 8
0
    def call_processor(self, input, headers, client_type, protocol_type,
                       context_data, callback):
        try:
            # The input string has already had the header removed, but
            # the python processor will expect it to be there.  In
            # order to reconstitute the message with headers, we use
            # the THeaderProtocol object to write into a memory
            # buffer, then pass that buffer to the python processor.

            should_sample = self._shouldSample()

            timestamps = CallTimestamps()
            timestamps.processBegin = 0
            timestamps.processEnd = 0
            if self.observer and should_sample:
                timestamps.processBegin = int(time.time() * 10**6)

            write_buf = TMemoryBuffer()
            trans = THeaderTransport(write_buf)
            trans._THeaderTransport__client_type = client_type
            trans._THeaderTransport__write_headers = headers
            trans.set_protocol_id(protocol_type)
            trans.write(input)
            trans.flush()

            prot_buf = TMemoryBuffer(write_buf.getvalue())
            prot = THeaderProtocol(prot_buf, client_types=[client_type])

            ctx = TCppConnectionContext(context_data)

            ret = self.processor.process(prot, prot, ctx)

            done_callback = partial(_ProcessorAdapter.done,
                                    prot_buf=prot_buf,
                                    client_type=client_type,
                                    callback=callback)

            if self.observer:
                if should_sample:
                    timestamps.processEnd = int(time.time() * 10**6)

                # This only bumps counters if `processBegin != 0` and
                # `processEnd != 0` and these will only be non-zero if
                # we are sampling this request.
                self.observer.callCompleted(timestamps)

            # This future is created by and returned from the processor's
            # ThreadPoolExecutor, which keeps a reference to it. So it is
            # fine for this future to end its lifecycle here.
            if isinstance(ret, Future):
                ret.add_done_callback(lambda x, d=done_callback: d())
            else:
                done_callback()
        except:
            # Don't let exceptions escape back into C++
            traceback.print_exc()
Esempio n. 9
0
 def __init__(self, transport, allowed_client_types):
     # much of the actual work for THeaderProtocol happens down in
     # THeaderTransport since we need to do low-level shenanigans to detect
     # if the client is sending us headers or one of the headerless formats
     # we support. this wraps the real transport with the one that does all
     # the magic.
     if not isinstance(transport, THeaderTransport):
         transport = THeaderTransport(transport, allowed_client_types)
     super(THeaderProtocol, self).__init__(transport)
     self._set_protocol()
Esempio n. 10
0
 def __init__(self, eden_dir=None, mounted_path=None):
     self._eden_dir = eden_dir
     if mounted_path:
         sock_path = os.path.join(mounted_path, '.eden', 'socket')
     else:
         sock_path = os.path.join(self._eden_dir, SOCKET_PATH)
     self._socket = TSocket(unix_socket=sock_path)
     self._socket.setTimeout(60000)  # in milliseconds
     self._transport = THeaderTransport(self._socket)
     self._protocol = THeaderProtocol(self._transport)
     super(EdenClient, self).__init__(self._protocol)
Esempio n. 11
0
    def __init__(self, host, port=None, timeout=2.0):
        self.host = host

        self._socket = TSocket(host, self.DEFAULT_PORT)
        # TSocket.setTimeout() takes a value in milliseconds
        self._socket.setTimeout(timeout * 1000)
        self._transport = THeaderTransport(self._socket)
        self._protocol = THeaderProtocol(self._transport)

        self._transport.open()
        QsfpService.Client.__init__(self, self._protocol)
Esempio n. 12
0
    def __init__(self, host, port=None, timeout=5.0):
        self.host = host
        if port is None:
            port = self.DEFAULT_PORT

        self._socket = TSocket(host, port)
        # TSocket.setTimeout() takes a value in milliseconds
        self._socket.setTimeout(timeout * 1000)
        self._transport = THeaderTransport(self._socket)
        self._protocol = THeaderProtocol(self._transport)
        self._transport.open()
        PcapPushSubscriber.Client.__init__(self, self._protocol)
Esempio n. 13
0
    def __init__(self, host, port=None, timeout=5.0):
        self.host = host
        if port is None:
            port = self.DEFAULT_PORT

        self._socket = TSocket(host, port)
        self._socket.setTimeout(timeout * 1000)
        self._transport = THeaderTransport(self._socket)
        self._protocol = THeaderProtocol(self._transport)

        self._transport.open()
        NetlinkManagerService.Client.__init__(self, self._protocol)
Esempio n. 14
0
    def __init__(self, host, port=None, timeout=10.0):
        # In a box with all 32 QSFP ports populated, it takes about 7.5s right
        # now to read all 32 QSFP ports. So, put the defaut timeout to 10s.
        self.host = host

        self._socket = TSocket(host, self.DEFAULT_PORT)
        # TSocket.setTimeout() takes a value in milliseconds
        self._socket.setTimeout(timeout * 1000)
        self._transport = THeaderTransport(self._socket)
        self._protocol = THeaderProtocol(self._transport)

        self._transport.open()
        QsfpService.Client.__init__(self, self._protocol)
Esempio n. 15
0
    def __init__(self, trans, strictRead=False, client_types=None):
        """Create a THeaderProtocol instance

        @param transport(TTransport) The underlying transport.
        @param strictRead(bool) Turn on strictRead if using TBinaryProtocol
        @param client_types([THeaderTransport.HEADERS_CLIENT_TYPE, ...])
                   List of client types to support.  Defaults to
                   HEADERS_CLIENT_TYPE only.
        """

        htrans = THeaderTransport(trans, client_types)
        TProtocolBase.__init__(self, htrans)
        self.strictRead = strictRead
        self.reset_protocol()
Esempio n. 16
0
 def __init__(self, eden_dir=None, mounted_path=None):
     self._eden_dir = eden_dir
     if mounted_path:
         sock_path = os.readlink(
             os.path.join(mounted_path, '.eden', 'socket'))
     else:
         sock_path = os.path.join(self._eden_dir, SOCKET_PATH)
     self._socket = TSocket(unix_socket=sock_path)
     # We used to set a timeout here, but picking the right duration is hard,
     # and safely retrying an arbitrary thrift call may not be safe.  So we
     # just leave the client with no timeout.
     #self._socket.setTimeout(60000)  # in milliseconds
     self._transport = THeaderTransport(self._socket)
     self._protocol = THeaderProtocol(self._transport)
     super(EdenClient, self).__init__(self._protocol)
Esempio n. 17
0
 def __init__(self, eden_dir=None, socket_path=None):
     if socket_path is not None:
         self._socket_path = socket_path
     elif eden_dir is not None:
         self._socket_path = os.path.join(eden_dir, SOCKET_PATH)
     else:
         raise TypeError("one of eden_dir or socket_path is required")
     self._socket = TSocket(unix_socket=self._socket_path)
     # We used to set a timeout here, but picking the right duration is hard,
     # and safely retrying an arbitrary thrift call may not be safe.  So we
     # just leave the client with no timeout.
     # self._socket.setTimeout(60000)  # in milliseconds
     self._transport = THeaderTransport(self._socket)
     self._protocol = THeaderProtocol(self._transport)
     super(EdenClient, self).__init__(self._protocol)
Esempio n. 18
0
    def done(prot_buf, client_type, callback):
        try:
            response = prot_buf.getvalue()

            if len(response) == 0:
                callback.call(response)
            else:
                # And on the way out, we need to strip off the header,
                # because the C++ code will expect to add it.

                read_buf = TMemoryBuffer(response)
                trans = THeaderTransport(read_buf, client_types=[client_type])
                trans.readFrame(len(response))
                callback.call(trans.cstringio_buf.read())
        except:
            traceback.print_exc()
Esempio n. 19
0
    def schedule_timeout(self, fname, seqid):
        timeout = self.timeouts[fname]
        if not timeout:
            return

        tmo = TMemoryBuffer()
        thp = THeaderTransport(tmo)
        oprot = THeaderProtocol(thp)
        exc = TApplicationException(TApplicationException.TIMEOUT,
                                    "Call to {} timed out".format(fname))
        oprot.writeMessageBegin(fname, TMessageType.EXCEPTION, seqid)
        exc.write(oprot)
        oprot.writeMessageEnd()
        thp.flush()
        timeout_task = self.loop.create_task(
            self.message_received(tmo.getvalue(), delay=timeout), )
        self.update_pending_tasks(seqid, timeout_task)
Esempio n. 20
0
    def _get_client_by_transport(self, options, transport, socket=None):
        # Create the protocol and client
        if options.json:
            protocol = TJSONProtocol.TJSONProtocol(transport)
        elif options.compact:
            protocol = TCompactProtocol.TCompactProtocol(transport)

        # No explicit option about protocol is specified. Try to infer.
        elif options.framed or options.unframed:
            protocol = TBinaryProtocol.TBinaryProtocolAccelerated(transport)

        elif socket is not None:
            # If json, compact, framed, and unframed are not specified,
            # THeaderProtocol is the default. Create a protocol using either
            # fuzzy or non-fuzzy transport depending on if options.fuzz is set.
            if options.fuzz is not None:
                transport = TFuzzyHeaderTransport(socket,
                                                  fuzz_fields=options.fuzz,
                                                  verbose=True)
            else:
                transport = THeaderTransport(socket)
                if options.headers is not None:
                    try:
                        parsed_headers = eval(options.headers)
                    except Exception:
                        self._exit(
                            error_message='Request headers (--headers) argument'
                            ' failed eval')
                    if not isinstance(parsed_headers, dict):
                        self._exit(
                            error_message='Request headers (--headers) argument'
                            ' must evaluate to a dict')
                    for header_name, header_value in parsed_headers.items():
                        transport.set_header(header_name, header_value)
            protocol = THeaderProtocol.THeaderProtocol(transport)
        else:
            self._exit(error_message=('No valid protocol '
                                      'specified for %s' % (type(self))),
                       status=os.EX_USAGE)

        transport.open()
        self._transport = transport

        client = self.service_class.Client(protocol)

        return client
Esempio n. 21
0
    def done(prot_buf, client_type, callback):
        try:
            response = prot_buf.getvalue()

            if len(response) == 0:
                callback.call(response)
            else:
                # And on the way out, we need to strip off the header,
                # because the C++ code will expect to add it.

                read_buf = TMemoryBuffer(response)
                trans = THeaderTransport(read_buf, client_types=[client_type])
                trans.set_max_frame_size(MAX_BIG_FRAME_SIZE)
                trans.readFrame(len(response))
                callback.call(trans.cstringio_buf.read())
        except:  # noqa
            # Don't let exceptions escape back into C++
            traceback.print_exc()
    def setUp(self):
        """Create two buffers, transports, and protocols.

        self._h_trans uses THeaderTransport
        self._f_trans uses TFuzzyHeaderTransport
        """
        cls = self.__class__

        # THeaderTransport attributes
        self._h_buf = TMemoryBuffer()
        self._h_trans = THeaderTransport(self._h_buf)
        self._h_prot = THeaderProtocol(self._h_trans)

        # TFuzzyHeaderTransport attributes
        self._f_buf = TMemoryBuffer()
        self._f_trans = TFuzzyHeaderTransport(
            self._f_buf, fuzz_fields=cls.fuzz_fields,
            fuzz_all_if_empty=False, verbose=False)
        self._f_prot = THeaderProtocol(self._f_trans)
Esempio n. 23
0
    def _get_client_by_transport(self, options, transport, socket=None):
        # Create the protocol and client
        if options.json:
            protocol = TJSONProtocol.TJSONProtocol(transport)
        elif options.compact:
            protocol = TCompactProtocol.TCompactProtocol(transport)

        # No explicit option about protocol is specified. Try to infer.
        elif options.framed or options.unframed:
            protocol = TBinaryProtocol.TBinaryProtocolAccelerated(transport)

        elif socket is not None:
            # If json, compact, framed, and unframed are not specified,
            # THeaderProtocol is the default.
            transport = THeaderTransport(socket)
            if options.headers is not None:
                try:
                    parsed_headers = eval(options.headers)
                except Exception:
                    self._exit(
                        error_message="Request headers (--headers) argument"
                        " failed eval")
                if not isinstance(parsed_headers, dict):
                    self._exit(
                        error_message="Request headers (--headers) argument"
                        " must evaluate to a dict")
                for header_name, header_value in parsed_headers.items():
                    transport.set_header(header_name, header_value)
            protocol = THeaderProtocol.THeaderProtocol(transport)
        else:
            self._exit(
                error_message=("No valid protocol "
                               "specified for %s" % (type(self))),
                status=os.EX_USAGE,
            )

        transport.open()
        self._transport = transport

        client = self.service_class.Client(protocol)

        return client
Esempio n. 24
0
    def testOversizeFrameRecv(self):
        """Test that an oversize frame on recv gets a TTransportException"""

        self.trans.set_max_frame_size(200)

        self.x.write(self.prot)
        self.trans.flush()

        inTrans = TTransport.TMemoryBuffer(self.trans.getTransport().getvalue())
        inTrans = THeaderTransport(inTrans)
        inProt = TBinaryProtocol.TBinaryProtocol(inTrans)

        inTrans.set_max_frame_size(2)

        try:
            self.x.read(inProt)
        except TTransport.TTransportException:
            return

        self.fail("Should have gotten TTransportException")
Esempio n. 25
0
    def __init__(self, eden_dir=None, socket_path=None):
        # type: (Optional[str], Optional[str]) -> None
        if socket_path is not None:
            self._socket_path = socket_path
        elif eden_dir is not None:
            self._socket_path = os.path.join(eden_dir, SOCKET_PATH)
        else:
            raise TypeError("one of eden_dir or socket_path is required")
        if sys.platform == "win32":
            self._socket = WinTSocket(unix_socket=self._socket_path)
        else:
            self._socket = TSocket(unix_socket=self._socket_path)

        # We used to set a timeout here, but picking the right duration is hard,
        # and safely retrying an arbitrary thrift call may not be safe.  So we
        # just leave the client with no timeout.
        # self.set_timeout(60)
        self.set_timeout(None)
        transport = THeaderTransport(self._socket)
        self._transport = transport  # type: Optional[THeaderTransport]
        self._protocol = THeaderProtocol(transport)
        super(EdenClient, self).__init__(self._protocol)
Esempio n. 26
0
    def serialize_texception(cls, fname, seqid, exception):
        """This saves us a bit of processing time for timeout handling by
        reusing the Thrift structs involved in exception serialization.

        NOTE: this is not thread-safe nor it is meant to be.
        """
        # the serializer is a singleton
        if cls._exception_serializer is None:
            buffer = TWriteOnlyBuffer()
            transport = THeaderTransport(buffer)
            cls._exception_serializer = THeaderProtocol(transport)
        else:
            transport = cls._exception_serializer.trans
            buffer = transport.getTransport()
            buffer.reset()

        serializer = cls._exception_serializer
        serializer.writeMessageBegin(fname, TMessageType.EXCEPTION, seqid)
        exception.write(serializer)
        serializer.writeMessageEnd()
        serializer.trans.flush()
        return buffer.getvalue()
Esempio n. 27
0
    def call_processor(self, input, headers, client_type, protocol_type,
                       context_data, callback):
        try:
            # The input string has already had the header removed, but
            # the python processor will expect it to be there.  In
            # order to reconstitute the message with headers, we use
            # the THeaderProtocol object to write into a memory
            # buffer, then pass that buffer to the python processor.

            write_buf = TMemoryBuffer()
            trans = THeaderTransport(write_buf)
            trans._THeaderTransport__client_type = client_type
            trans._THeaderTransport__write_headers = headers
            trans.set_protocol_id(protocol_type)
            trans.write(input)
            trans.flush()

            prot_buf = TMemoryBuffer(write_buf.getvalue())
            prot = THeaderProtocol(prot_buf, client_types=[client_type])

            ctx = TCppConnectionContext(context_data)

            ret = self.processor.process(prot, prot, ctx)

            done_callback = partial(_ProcessorAdapter.done,
                                    prot_buf=prot_buf,
                                    client_type=client_type,
                                    callback=callback)
            # This future is created by and returned from the processor's
            # ThreadPoolExecutor, which keeps a reference to it. So it is
            # fine for this future to end its lifecycle here.
            if isinstance(ret, Future):
                ret.add_done_callback(lambda x, d=done_callback: d())
            else:
                done_callback()
        except:
            # Don't let exceptions escape back into C++
            traceback.print_exc()
Esempio n. 28
0
    def call_processor(
        self, input, headers, client_type, protocol_type, context_data, callback
    ):
        try:
            # TCppServer threads are not created by Python so they are
            # missing settrace() hooks.  We need to manually set the
            # hook here for things to work (e.g. coverage and pdb).
            if sys.gettrace() is None and threading._trace_hook is not None:
                sys.settrace(threading._trace_hook)

            # The input string has already had the header removed, but
            # the python processor will expect it to be there.  In
            # order to reconstitute the message with headers, we use
            # the THeaderProtocol object to write into a memory
            # buffer, then pass that buffer to the python processor.

            should_sample = self._shouldSample()

            timestamps = CallTimestamps()
            if self.observer and should_sample:
                timestamps.setProcessBeginNow()

            write_buf = TMemoryBuffer()
            trans = THeaderTransport(write_buf)
            trans.set_max_frame_size(MAX_BIG_FRAME_SIZE)
            trans._THeaderTransport__client_type = client_type
            trans._THeaderTransport__write_headers = headers
            trans.set_protocol_id(protocol_type)
            trans.write(input)
            trans.flush()

            prot_buf = TMemoryBuffer(write_buf.getvalue())
            prot = THeaderProtocol(prot_buf, client_types=[client_type])
            prot.trans.set_max_frame_size(MAX_BIG_FRAME_SIZE)

            ctx = TCppConnectionContext(context_data)

            ret = self.processor.process(prot, prot, ctx)

            done_callback = partial(
                _ProcessorAdapter.done,
                prot_buf=prot_buf,
                client_type=client_type,
                callback=callback,
            )

            if self.observer:
                if should_sample:
                    timestamps.setProcessEndNow()

                # This only bumps counters if `processBegin != 0` and
                # `processEnd != 0` and these will only be non-zero if
                # we are sampling this request.
                self.observer.callCompleted(timestamps)

            # This future is created by and returned from the processor's
            # ThreadPoolExecutor, which keeps a reference to it. So it is
            # fine for this future to end its lifecycle here.
            if isinstance(ret, Future):
                ret.add_done_callback(lambda x, d=done_callback: d())
            else:
                done_callback()
        except:  # noqa
            # Don't let exceptions escape back into C++
            traceback.print_exc()