Send a message method from one pid to another with an optional body.

    Note: It is more idiomatic to send directly from a bound process rather than
    calling send on the context.

    If the destination pid is on the same context, the Context may skip the
    wire and route directly to process itself.  ``from_pid`` must be bound
    to this context.

    This method returns immediately.

    :param from_pid: The pid of the sending process.
    :type from_pid: :class:`PID`
    :param to_pid: The pid of the destination process.
    :type to_pid: :class:`PID`
    :param method: The method name of the destination process.
    :type method: ``str``
    :keyword body: Optional content to send along with the message.
    :type body: ``bytes`` or None
    :return: Nothing

def send(self, from_pid, to_pid, method, body=None):
    """Send a message method from one pid to another with an optional body.

    Note: It is more idiomatic to send directly from a bound process rather than
    calling send on the context.

    If the destination pid is on the same context, the Context may skip the
    wire and route directly to process itself.  ``from_pid`` must be bound
    to this context.

    This method returns immediately.

    :param from_pid: The pid of the sending process.
    :type from_pid: :class:`PID`
    :param to_pid: The pid of the destination process.
    :type to_pid: :class:`PID`
    :param method: The method name of the destination process.
    :type method: ``str``
    :keyword body: Optional content to send along with the message.
    :type body: ``bytes`` or None
    :return: Nothing
    """

    self._assert_started()
    self._assert_local_pid(from_pid)

    if self._is_local(to_pid):
      local_method = self._get_local_mailbox(to_pid, method)
      if local_method:
        log.info('Doing local dispatch of %s => %s (method: %s)' % (from_pid, to_pid, local_method))
        self.__loop.add_callback(local_method, from_pid, body or b'')
        return
      else:
        # TODO(wickman) Consider failing hard if no local method is detected, otherwise we're
        # just going to do a POST and have it dropped on the floor.
        pass

    request_data = encode_request(from_pid, to_pid, method, body=body)

    log.info('Sending POST %s => %s (payload: %d bytes)' % (
             from_pid, to_pid.as_url(method), len(request_data)))

    def on_connect(stream):
      log.info('Writing %s from %s to %s' % (len(request_data), from_pid, to_pid))
      stream.write(request_data)
      log.info('Wrote %s from %s to %s' % (len(request_data), from_pid, to_pid))

    self.__loop.add_callback(self._maybe_connect, to_pid, on_connect)