From de0c84ebf1e18a5de4e1adb39cf33499b66d27b4 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 25 Mar 2016 18:38:13 +0200 Subject: [PATCH] extmod/modlwip: lwip_tcp_send: Handle properly send buffer full condition. Per POSIX http://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html : "If space is not available at the sending socket to hold the message to be transmitted, and the socket file descriptor does not have O_NONBLOCK set, send() shall block until space is available. If space is not available at the sending socket to hold the message to be transmitted, and the socket file descriptor does have O_NONBLOCK set, send() shall fail [with EAGAIN]." --- extmod/modlwip.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/extmod/modlwip.c b/extmod/modlwip.c index bce5d9f889..59216c5d83 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -365,6 +365,35 @@ STATIC mp_uint_t lwip_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_ // Helper function for send/sendto to handle TCP packets STATIC mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_uint_t len, int *_errno) { u16_t available = tcp_sndbuf(socket->pcb.tcp); + + if (available == 0) { + // Non-blocking socket + if (socket->timeout == 0) { + *_errno = EAGAIN; + return -1; + } + + mp_uint_t start = mp_hal_ticks_ms(); + // Assume that STATE_PEER_CLOSED may mean half-closed connection, where peer closed it + // sending direction, but not receiving. Consequently, check for both STATE_CONNECTED + // and STATE_PEER_CLOSED as normal conditions and still waiting for buffers to be sent. + // If peer fully closed socket, we would have socket->state set to ERR_RST (connection + // reset) by error callback. + // Avoid sending too small packets, so wait until at least 16 bytes available + while (socket->state >= STATE_CONNECTED && (available = tcp_sndbuf(socket->pcb.tcp)) < 16) { + if (socket->timeout != -1 && mp_hal_ticks_ms() - start > socket->timeout) { + *_errno = ETIMEDOUT; + return -1; + } + poll_sockets(); + } + + if (socket->state < 0) { + *_errno = error_lookup_table[-socket->state]; + return -1; + } + } + u16_t write_len = MIN(available, len); err_t err = tcp_write(socket->pcb.tcp, buf, write_len, TCP_WRITE_FLAG_COPY);