/** Receive from stream.
 *
 * @retval -1 error
 * @retval 0  end-of-stream
 * @retval 1  normal receive
 * @retval 2  incomplete recv, recv again
 *
 */
int tport_recv_stream_ws(tport_t *self)
{
  msg_t *msg;
  ssize_t n, N, veclen, i, m;
  int err;
  msg_iovec_t iovec[msg_n_fragments] = {{ 0 }};
  tport_ws_t *wstp = (tport_ws_t *)self;
  uint8_t *data;
  ws_opcode_t oc;

  if (wstp->ws_initialized < 0) {
	  return -1;
  }

  N = ws_read_frame(&wstp->ws, &oc, &data);

  if (N == -2) {
	  return 2;
  }

  if ((N == -1000) || (N == 0)) {
	  if (self->tp_msg) {
		  msg_recv_commit(self->tp_msg, 0, 1);
	  }
	  return 0;    /* End of stream */
  }
  if (N < 0) {
	  err = errno = EHOSTDOWN;
	  SU_DEBUG_1(("%s(%p): su_getmsgsize(): %s (%d)\n", __func__, (void *)self,
				  su_strerror(err), err));
	  return 0;
  }

  veclen = tport_recv_iovec(self, &self->tp_msg, iovec, N, 0);
  if (veclen < 0)
    return -1;

  msg = self->tp_msg;

  msg_set_address(msg, self->tp_addr, self->tp_addrlen);

  for (i = 0, n = 0; i < veclen; i++) {
    m = iovec[i].mv_len; assert(N >= n + m);
    memcpy(iovec[i].mv_base, data + n, m);
    n += m;
  }

  assert(N == n);

  /* Write the received data to the message dump file */
  if (self->tp_master->mr_dump_file)
	  tport_dump_iovec(self, msg, n, iovec, veclen, "recv", "from");

  /* Mark buffer as used */
  msg_recv_commit(msg, N, 0);

  return 1;
}
/** Receive from stream.
 *
 * @retval -1 error
 * @retval 0  end-of-stream
 * @retval 1  normal receive
 * @retval 2  incomplete recv, recv again
 *
 */
int tport_recv_stream(tport_t *self)
{
  msg_t *msg;
  ssize_t n, N, veclen;
  int err, initial;
  msg_iovec_t iovec[msg_n_fragments] = {{ 0 }};

  N = su_getmsgsize(self->tp_socket);
  if (N == 0) {
    if (self->tp_msg)
      msg_recv_commit(self->tp_msg, 0, 1);
    return 0;    /* End of stream */
  }
  if (N == -1) {
    err = su_errno();
    SU_DEBUG_1(("%s(%p): su_getmsgsize(): %s (%d)\n", __func__, (void *)self,
		su_strerror(err), err));
    return -1;
  }

  initial = self->tp_msg == NULL;
  memset(&self->tp_ptime, 0, sizeof self->tp_ptime);

  while (initial && N <= 8) {	/* Check for whitespace */
    char crlf[9];
    size_t i;

    n = su_recv(self->tp_socket, crlf, N, MSG_PEEK);

    i = ws_span(crlf, n);
    if (i == 0)
      break;

    n = su_recv(self->tp_socket, crlf, i, 0);
    if (n <= 0)
      return (int)n;

    SU_DEBUG_7(("%s(%p): received keepalive (total %u)\n", __func__,
		(void *)self, self->tp_ping));

    N -= n, self->tp_ping += n;

    tport_recv_bytes(self, n, n);

    if (N == 0) {
      /* outbound-10 section 3.5.1  - send pong */
      if (self->tp_ping >= 4)
	tport_tcp_pong(self);

      return 1;
    }
  }

  veclen = tport_recv_iovec(self, &self->tp_msg, iovec, N, 0);
  if (veclen == -1)
    return -1;

  msg = self->tp_msg;

  msg_set_address(msg, self->tp_addr, (socklen_t)(self->tp_addrlen));

  n = su_vrecv(self->tp_socket, iovec, veclen, 0, NULL, NULL);

  if (n == SOCKET_ERROR)
    return tport_recv_error_report(self);

  assert(n <= N);

  tport_recv_bytes(self, n, n);

  /* Check if message contains only whitespace */
  /* This can happen if multiple PINGs are received at once */
  if (initial) {
    size_t i = ws_span(iovec->siv_base, iovec->siv_len);

    if (i + self->tp_ping >= 4)
      tport_tcp_pong(self);
    else

      self->tp_ping += (unsigned short)i;

    if (i == iovec->siv_len && veclen == 1) {
      SU_DEBUG_7(("%s(%p): received %u bytes of keepalive\n",
		  __func__, (void *)self, (unsigned)i));
      msg_destroy(self->tp_msg), self->tp_msg = NULL;
      return 1;
    }
  }

  /* Write the received data to the message dump file */
  if (self->tp_master->mr_dump_file)
    tport_dump_iovec(self, msg, n, iovec, veclen, "recv", "from");
    
  if (self->tp_master->mr_capt_sock)
      tport_capt_msg(self, msg, n, iovec, veclen, "recv");
         

  /* Mark buffer as used */
  msg_recv_commit(msg, n, n == 0);

  if (n > 0)
    self->tp_ping = 0;

  return n != 0;
}
/** Receive datagram.
 *
 * @retval -1 error
 * @retval 0  end-of-stream
 * @retval 1  normal receive (should never happen)
 * @retval 2  incomplete recv, call me again (should never happen)
 * @retval 3  STUN keepalive, ignore
 */
int tport_recv_dgram(tport_t *self)
{
  msg_t *msg;
  ssize_t n, veclen, N;
  su_addrinfo_t *ai;
  su_sockaddr_t *from;
  socklen_t fromlen;
  msg_iovec_t iovec[msg_n_fragments] = {{ 0 }};
  uint8_t sample[1];

  /* Simulate packet loss */
  if (self->tp_params->tpp_drop &&
      (unsigned)su_randint(0, 1000) < self->tp_params->tpp_drop) {
    su_recv(self->tp_socket, sample, 1, 0);
    SU_DEBUG_3(("tport(%p): simulated packet loss!\n", (void *)self));
    return 0;
  }

  assert(self->tp_msg == NULL);

#if nomore
  /* We used to resize the buffer, but it fragments the memory */
  N = 65535;
#else
  N = (ssize_t)su_getmsgsize(self->tp_socket);
  if (N == -1) {
    int err = su_errno();
    SU_DEBUG_1(("%s(%p): su_getmsgsize(): %s (%d)\n", __func__, (void *)self,
		su_strerror(err), err));
    return -1;
  }
  if (N == 0) {
    su_recv(self->tp_socket, sample, 1, 0);
    SU_DEBUG_3(("tport(%p): zero length packet", (void *)self));
    return 0;
  }
#endif

  veclen = tport_recv_iovec(self, &self->tp_msg, iovec, N, 1);
  if (veclen == -1)
    return -1;

  msg = self->tp_msg;

  ai = msg_addrinfo(msg);
  from = (su_sockaddr_t *)ai->ai_addr, fromlen = (socklen_t)(ai->ai_addrlen);

  n = su_vrecv(self->tp_socket, iovec, veclen, 0, from, &fromlen);

  ai->ai_addrlen = fromlen;

  if (n == SOCKET_ERROR) {
    int error = su_errno();
    msg_destroy(msg); self->tp_msg = NULL;
    su_seterrno(error);

    if (su_is_blocking(error))
      return 0;
    else
      return -1;
  }
  else if (n <= 1) {
    SU_DEBUG_1(("%s(%p): runt of "MOD_ZD" bytes\n",
		"tport_recv_dgram", (void *)self, n));
    msg_destroy(msg), self->tp_msg = NULL;
    return 0;
  }

  tport_recv_bytes(self, n, n);

  SU_CANONIZE_SOCKADDR(from);

  if (self->tp_master->mr_dump_file)
    tport_dump_iovec(self, msg, n, iovec, veclen, "recv", "from");

  *sample = *((uint8_t *)iovec[0].mv_base);

  /* Commit received data into buffer. This may relocate iovec contents */
  msg_recv_commit(msg, n, 1);

  if ((sample[0] & 0xf8) == 0xf8)
    /* SigComp */
    return tport_recv_comp_dgram(self, self->tp_comp, &self->tp_msg,
				 from, fromlen);
#if HAVE_SOFIA_STUN
  else if (sample[0] == 0 || sample[0] == 1)
    /* STUN request or response */
    return tport_recv_stun_dgram(self, &self->tp_msg, from, fromlen);
#endif
  else
    return 0;
}