mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-12 16:50:21 +00:00
Make tests work on public repo as well
PUBLISHED_FROM=78acb18d597b6f47f31da13087fa6685f21ce445
This commit is contained in:
1 parent
3eb4eb8074
commit
7bf5144e62
145 files changed
+30660
-16
No files matched your search
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#ifndef CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_
|
||||
#define CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_
|
||||
|
||||
#ifndef MG_LWIP
|
||||
#define MG_LWIP 0
|
||||
#endif
|
||||
|
||||
#if MG_LWIP
|
||||
|
||||
/*
|
||||
* When compiling for nRF5x chips with arm-none-eabi-gcc, it has BYTE_ORDER
|
||||
* already defined, so in order to avoid warnings in lwip, we have to undefine
|
||||
* it.
|
||||
*
|
||||
* TODO: Check if in the future versions of nRF5 SDK that changes.
|
||||
* Current version of nRF51 SDK: 0.8.0
|
||||
* nRF5 SDK: 0.9.0
|
||||
*/
|
||||
#if CS_PLATFORM == CS_P_NRF51 || CS_PLATFORM == CS_P_NRF52
|
||||
#undef BYTE_ORDER
|
||||
#endif
|
||||
|
||||
#include <lwip/opt.h>
|
||||
#include <lwip/err.h>
|
||||
#include <lwip/ip_addr.h>
|
||||
#include <lwip/inet.h>
|
||||
#include <lwip/netdb.h>
|
||||
#include <lwip/dns.h>
|
||||
|
||||
#ifndef LWIP_PROVIDE_ERRNO
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#if LWIP_SOCKET
|
||||
#include <lwip/sockets.h>
|
||||
#else
|
||||
/* We really need the definitions from sockets.h. */
|
||||
#undef LWIP_SOCKET
|
||||
#define LWIP_SOCKET 1
|
||||
#include <lwip/sockets.h>
|
||||
#undef LWIP_SOCKET
|
||||
#define LWIP_SOCKET 0
|
||||
#endif
|
||||
|
||||
#define INVALID_SOCKET (-1)
|
||||
#define SOMAXCONN 10
|
||||
typedef int sock_t;
|
||||
|
||||
#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL
|
||||
struct mg_mgr;
|
||||
struct mg_connection;
|
||||
uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr);
|
||||
void mg_lwip_set_keepalive_params(struct mg_connection *nc, int idle,
|
||||
int interval, int count);
|
||||
#endif
|
||||
|
||||
/* For older version of LWIP */
|
||||
#ifndef ipX_2_ip
|
||||
#define ipX_2_ip(x) (x)
|
||||
#endif
|
||||
|
||||
#endif /* MG_LWIP */
|
||||
|
||||
#endif /* CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_ */
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL
|
||||
|
||||
#ifndef MG_SIG_QUEUE_LEN
|
||||
#define MG_SIG_QUEUE_LEN 32
|
||||
#endif
|
||||
|
||||
struct mg_ev_mgr_lwip_signal {
|
||||
int sig;
|
||||
struct mg_connection *nc;
|
||||
};
|
||||
|
||||
struct mg_ev_mgr_lwip_data {
|
||||
struct mg_ev_mgr_lwip_signal sig_queue[MG_SIG_QUEUE_LEN];
|
||||
int sig_queue_len;
|
||||
int start_index;
|
||||
};
|
||||
|
||||
void mg_lwip_post_signal(enum mg_sig_type sig, struct mg_connection *nc) {
|
||||
struct mg_ev_mgr_lwip_data *md =
|
||||
(struct mg_ev_mgr_lwip_data *) nc->iface->data;
|
||||
mgos_lock();
|
||||
if (md->sig_queue_len >= MG_SIG_QUEUE_LEN) {
|
||||
mgos_unlock();
|
||||
return;
|
||||
}
|
||||
int end_index = (md->start_index + md->sig_queue_len) % MG_SIG_QUEUE_LEN;
|
||||
md->sig_queue[end_index].sig = sig;
|
||||
md->sig_queue[end_index].nc = nc;
|
||||
md->sig_queue_len++;
|
||||
mg_lwip_mgr_schedule_poll(nc->mgr);
|
||||
mgos_unlock();
|
||||
}
|
||||
|
||||
void mg_ev_mgr_lwip_process_signals(struct mg_mgr *mgr) {
|
||||
struct mg_ev_mgr_lwip_data *md =
|
||||
(struct mg_ev_mgr_lwip_data *) mgr->ifaces[MG_MAIN_IFACE]->data;
|
||||
while (md->sig_queue_len > 0) {
|
||||
mgos_lock();
|
||||
int sig = md->sig_queue[md->start_index].sig;
|
||||
struct mg_connection *nc = md->sig_queue[md->start_index].nc;
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
md->start_index = (md->start_index + 1) % MG_SIG_QUEUE_LEN;
|
||||
md->sig_queue_len--;
|
||||
mgos_unlock();
|
||||
if (nc->iface == NULL || nc->mgr == NULL) continue;
|
||||
switch (sig) {
|
||||
case MG_SIG_CONNECT_RESULT: {
|
||||
#if MG_ENABLE_SSL
|
||||
if (cs->err == 0 && (nc->flags & MG_F_SSL) &&
|
||||
!(nc->flags & MG_F_SSL_HANDSHAKE_DONE)) {
|
||||
mg_lwip_ssl_do_hs(nc);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
mg_if_connect_cb(nc, cs->err);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MG_SIG_CLOSE_CONN: {
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
mg_close_conn(nc);
|
||||
break;
|
||||
}
|
||||
case MG_SIG_RECV: {
|
||||
cs->recv_pending = 0;
|
||||
if (nc->flags & MG_F_UDP) {
|
||||
mg_lwip_handle_recv_udp(nc);
|
||||
} else {
|
||||
mg_lwip_handle_recv_tcp(nc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MG_SIG_TOMBSTONE: {
|
||||
break;
|
||||
}
|
||||
case MG_SIG_ACCEPT: {
|
||||
mg_lwip_handle_accept(nc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mg_lwip_if_init(struct mg_iface *iface) {
|
||||
LOG(LL_INFO, ("%p Mongoose init", iface));
|
||||
iface->data = MG_CALLOC(1, sizeof(struct mg_ev_mgr_lwip_data));
|
||||
}
|
||||
|
||||
void mg_lwip_if_free(struct mg_iface *iface) {
|
||||
MG_FREE(iface->data);
|
||||
iface->data = NULL;
|
||||
}
|
||||
|
||||
void mg_lwip_if_add_conn(struct mg_connection *nc) {
|
||||
(void) nc;
|
||||
}
|
||||
|
||||
void mg_lwip_if_remove_conn(struct mg_connection *nc) {
|
||||
struct mg_ev_mgr_lwip_data *md =
|
||||
(struct mg_ev_mgr_lwip_data *) nc->iface->data;
|
||||
/* Walk the queue and null-out further signals for this conn. */
|
||||
for (int i = 0; i < MG_SIG_QUEUE_LEN; i++) {
|
||||
if (md->sig_queue[i].nc == nc) {
|
||||
md->sig_queue[i].sig = MG_SIG_TOMBSTONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) {
|
||||
struct mg_mgr *mgr = iface->mgr;
|
||||
int n = 0;
|
||||
double now = mg_time();
|
||||
struct mg_connection *nc, *tmp;
|
||||
double min_timer = 0;
|
||||
int num_timers = 0;
|
||||
#if 0
|
||||
DBG(("begin poll @%u", (unsigned int) (now * 1000)));
|
||||
#endif
|
||||
mg_ev_mgr_lwip_process_signals(mgr);
|
||||
for (nc = mgr->active_connections; nc != NULL; nc = tmp) {
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
tmp = nc->next;
|
||||
n++;
|
||||
if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) ||
|
||||
((nc->flags & MG_F_SEND_AND_CLOSE) && (nc->flags & MG_F_UDP) &&
|
||||
(nc->send_mbuf.len == 0))) {
|
||||
mg_close_conn(nc);
|
||||
continue;
|
||||
}
|
||||
mg_if_poll(nc, now);
|
||||
mg_if_timer(nc, now);
|
||||
#if MG_ENABLE_SSL
|
||||
if ((nc->flags & MG_F_SSL) && cs != NULL && cs->pcb.tcp != NULL &&
|
||||
cs->pcb.tcp->state == ESTABLISHED) {
|
||||
if (((nc->flags & MG_F_WANT_WRITE) ||
|
||||
((nc->send_mbuf.len > 0) &&
|
||||
(nc->flags & MG_F_SSL_HANDSHAKE_DONE))) &&
|
||||
cs->pcb.tcp->snd_buf > 0) {
|
||||
/* Can write more. */
|
||||
if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) {
|
||||
if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_send(nc);
|
||||
} else {
|
||||
mg_lwip_ssl_do_hs(nc);
|
||||
}
|
||||
}
|
||||
if (cs->rx_chain != NULL || (nc->flags & MG_F_WANT_READ)) {
|
||||
if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) {
|
||||
if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_recv(nc);
|
||||
} else {
|
||||
mg_lwip_ssl_do_hs(nc);
|
||||
}
|
||||
}
|
||||
} else
|
||||
#endif /* MG_ENABLE_SSL */
|
||||
{
|
||||
if (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING)) {
|
||||
mg_lwip_send_more(nc);
|
||||
}
|
||||
}
|
||||
if (nc->sock != INVALID_SOCKET &&
|
||||
!(nc->flags & (MG_F_UDP | MG_F_LISTENING)) && cs->pcb.tcp != NULL &&
|
||||
cs->pcb.tcp->unsent != NULL) {
|
||||
tcpip_callback(tcp_output_tcpip, cs->pcb.tcp);
|
||||
}
|
||||
if (nc->ev_timer_time > 0) {
|
||||
if (num_timers == 0 || nc->ev_timer_time < min_timer) {
|
||||
min_timer = nc->ev_timer_time;
|
||||
}
|
||||
num_timers++;
|
||||
}
|
||||
|
||||
if (nc->sock != INVALID_SOCKET) {
|
||||
/* Try to consume data from cs->rx_chain */
|
||||
mg_lwip_consume_rx_chain_tcp(nc);
|
||||
|
||||
/*
|
||||
* If the connection is about to close, and rx_chain is finally empty,
|
||||
* send the MG_SIG_CLOSE_CONN signal
|
||||
*/
|
||||
if (cs->draining_rx_chain && cs->rx_chain == NULL) {
|
||||
mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
|
||||
}
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
DBG(("end poll @%u, %d conns, %d timers (min %u), next in %d ms",
|
||||
(unsigned int) (now * 1000), n, num_timers,
|
||||
(unsigned int) (min_timer * 1000), timeout_ms));
|
||||
#endif
|
||||
(void) timeout_ms;
|
||||
return now;
|
||||
}
|
||||
|
||||
uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr) {
|
||||
struct mg_connection *nc;
|
||||
double now;
|
||||
double min_timer = 0;
|
||||
int num_timers = 0;
|
||||
mg_ev_mgr_lwip_process_signals(mgr);
|
||||
for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) {
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
if (nc->ev_timer_time > 0) {
|
||||
if (num_timers == 0 || nc->ev_timer_time < min_timer) {
|
||||
min_timer = nc->ev_timer_time;
|
||||
}
|
||||
num_timers++;
|
||||
}
|
||||
if (nc->send_mbuf.len > 0
|
||||
#if MG_ENABLE_SSL
|
||||
|| (nc->flags & MG_F_WANT_WRITE)
|
||||
#endif
|
||||
) {
|
||||
int can_send = 0;
|
||||
/* We have stuff to send, but can we? */
|
||||
if (nc->flags & MG_F_UDP) {
|
||||
/* UDP is always ready for sending. */
|
||||
can_send = (cs->pcb.udp != NULL);
|
||||
} else {
|
||||
can_send = (cs->pcb.tcp != NULL && cs->pcb.tcp->snd_buf > 0);
|
||||
}
|
||||
/* We want and can send, request a poll immediately. */
|
||||
if (can_send) return 0;
|
||||
}
|
||||
}
|
||||
uint32_t timeout_ms = ~0;
|
||||
now = mg_time();
|
||||
if (num_timers > 0) {
|
||||
/* If we have a timer that is past due, do a poll ASAP. */
|
||||
if (min_timer < now) return 0;
|
||||
double timer_timeout_ms = (min_timer - now) * 1000 + 1 /* rounding */;
|
||||
if (timer_timeout_ms < timeout_ms) {
|
||||
timeout_ms = timer_timeout_ms;
|
||||
}
|
||||
}
|
||||
return timeout_ms;
|
||||
}
|
||||
|
||||
#endif /* MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL */
|
||||
@@ -0,0 +1,779 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#if MG_ENABLE_NET_IF_LWIP_LOW_LEVEL
|
||||
|
||||
#include "common/mg_mem.h"
|
||||
|
||||
#include <lwip/init.h>
|
||||
#include <lwip/pbuf.h>
|
||||
#include <lwip/tcp.h>
|
||||
#include <lwip/tcpip.h>
|
||||
#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105
|
||||
#include <lwip/priv/tcp_priv.h> /* For tcp_seg */
|
||||
#else
|
||||
#include <lwip/tcp_impl.h>
|
||||
#endif
|
||||
#include <lwip/udp.h>
|
||||
|
||||
#include "common/cs_dbg.h"
|
||||
|
||||
/*
|
||||
* Newest versions of LWIP have ip_2_ip4, older have ipX_2_ip,
|
||||
* even older have nothing.
|
||||
*/
|
||||
#ifndef ip_2_ip4
|
||||
#ifdef ipX_2_ip
|
||||
#define ip_2_ip4(addr) ipX_2_ip(addr)
|
||||
#else
|
||||
#define ip_2_ip4(addr) (addr)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Depending on whether Mongoose is compiled with ipv6 support, use right
|
||||
* lwip functions
|
||||
*/
|
||||
#if MG_ENABLE_IPV6
|
||||
#define TCP_NEW tcp_new_ip6
|
||||
#define TCP_BIND tcp_bind_ip6
|
||||
#define UDP_BIND udp_bind_ip6
|
||||
#define IPADDR_NTOA(x) ip6addr_ntoa((const ip6_addr_t *)(x))
|
||||
#define SET_ADDR(dst, src) \
|
||||
memcpy((dst)->sin6.sin6_addr.s6_addr, (src)->ip6.addr, \
|
||||
sizeof((dst)->sin6.sin6_addr.s6_addr))
|
||||
#else
|
||||
#define TCP_NEW tcp_new
|
||||
#define TCP_BIND tcp_bind
|
||||
#define UDP_BIND udp_bind
|
||||
#define IPADDR_NTOA ipaddr_ntoa
|
||||
#define SET_ADDR(dst, src) (dst)->sin.sin_addr.s_addr = ip_2_ip4(src)->addr
|
||||
#endif
|
||||
|
||||
#if NO_SYS
|
||||
#define tcpip_callback(fn, arg) (fn)(arg)
|
||||
typedef void (*tcpip_callback_fn)(void *arg);
|
||||
#endif
|
||||
|
||||
void mg_lwip_ssl_do_hs(struct mg_connection *nc);
|
||||
void mg_lwip_ssl_send(struct mg_connection *nc);
|
||||
void mg_lwip_ssl_recv(struct mg_connection *nc);
|
||||
|
||||
void mg_lwip_if_init(struct mg_iface *iface);
|
||||
void mg_lwip_if_free(struct mg_iface *iface);
|
||||
void mg_lwip_if_add_conn(struct mg_connection *nc);
|
||||
void mg_lwip_if_remove_conn(struct mg_connection *nc);
|
||||
time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms);
|
||||
|
||||
#if defined(RTOS_SDK) || defined(ESP_PLATFORM)
|
||||
extern void mgos_lock();
|
||||
extern void mgos_unlock();
|
||||
#else
|
||||
#define mgos_lock()
|
||||
#define mgos_unlock()
|
||||
#endif
|
||||
|
||||
static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p);
|
||||
|
||||
#if LWIP_TCP_KEEPALIVE
|
||||
void mg_lwip_set_keepalive_params(struct mg_connection *nc, int idle,
|
||||
int interval, int count) {
|
||||
if (nc->sock == INVALID_SOCKET || nc->flags & MG_F_UDP) {
|
||||
return;
|
||||
}
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
struct tcp_pcb *tpcb = cs->pcb.tcp;
|
||||
if (idle > 0 && interval > 0 && count > 0) {
|
||||
tpcb->keep_idle = idle * 1000;
|
||||
tpcb->keep_intvl = interval * 1000;
|
||||
tpcb->keep_cnt = count;
|
||||
tpcb->so_options |= SOF_KEEPALIVE;
|
||||
} else {
|
||||
tpcb->so_options &= ~SOF_KEEPALIVE;
|
||||
}
|
||||
}
|
||||
#elif !defined(MG_NO_LWIP_TCP_KEEPALIVE)
|
||||
#warning LWIP TCP keepalive is disabled. Please consider enabling it.
|
||||
#endif /* LWIP_TCP_KEEPALIVE */
|
||||
|
||||
static err_t mg_lwip_tcp_conn_cb(void *arg, struct tcp_pcb *tpcb, err_t err) {
|
||||
struct mg_connection *nc = (struct mg_connection *) arg;
|
||||
DBG(("%p connect to %s:%u = %d", nc, IPADDR_NTOA(ipX_2_ip(&tpcb->remote_ip)),
|
||||
tpcb->remote_port, err));
|
||||
if (nc == NULL) {
|
||||
tcp_abort(tpcb);
|
||||
return ERR_ARG;
|
||||
}
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
cs->err = err;
|
||||
#if LWIP_TCP_KEEPALIVE
|
||||
if (err == 0) mg_lwip_set_keepalive_params(nc, 60, 10, 6);
|
||||
#endif
|
||||
mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static void mg_lwip_tcp_error_cb(void *arg, err_t err) {
|
||||
struct mg_connection *nc = (struct mg_connection *) arg;
|
||||
DBG(("%p conn error %d", nc, err));
|
||||
if (nc == NULL) return;
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
cs->pcb.tcp = NULL; /* Has already been deallocated */
|
||||
if (nc->flags & MG_F_CONNECTING) {
|
||||
cs->err = err;
|
||||
mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
|
||||
} else {
|
||||
mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
|
||||
}
|
||||
}
|
||||
|
||||
static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb,
|
||||
struct pbuf *p, err_t err) {
|
||||
struct mg_connection *nc = (struct mg_connection *) arg;
|
||||
DBG(("%p %p %u %d", nc, tpcb, (p != NULL ? p->tot_len : 0), err));
|
||||
if (p == NULL) {
|
||||
if (nc != NULL && !(nc->flags & MG_F_CLOSE_IMMEDIATELY)) {
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
if (cs->rx_chain != NULL) {
|
||||
/*
|
||||
* rx_chain still contains non-consumed data, don't close the
|
||||
* connection
|
||||
*/
|
||||
cs->draining_rx_chain = 1;
|
||||
} else {
|
||||
mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
|
||||
}
|
||||
} else {
|
||||
/* Tombstoned connection, do nothing. */
|
||||
}
|
||||
return ERR_OK;
|
||||
} else if (nc == NULL) {
|
||||
tcp_abort(tpcb);
|
||||
return ERR_ARG;
|
||||
}
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
/*
|
||||
* If we get a chain of more than one segment at once, we need to bump
|
||||
* refcount on the subsequent bufs to make them independent.
|
||||
*/
|
||||
if (p->next != NULL) {
|
||||
struct pbuf *q = p->next;
|
||||
for (; q != NULL; q = q->next) pbuf_ref(q);
|
||||
}
|
||||
mgos_lock();
|
||||
if (cs->rx_chain == NULL) {
|
||||
cs->rx_offset = 0;
|
||||
} else if (pbuf_clen(cs->rx_chain) >= 4) {
|
||||
/* ESP SDK has a limited pool of 5 pbufs. We must not hog them all or RX
|
||||
* will be completely blocked. We already have at least 4 in the chain,
|
||||
* this one is, so we have to make a copy and release this one. */
|
||||
struct pbuf *np = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
|
||||
if (np != NULL) {
|
||||
pbuf_copy(np, p);
|
||||
pbuf_free(p);
|
||||
p = np;
|
||||
}
|
||||
}
|
||||
mgos_unlock();
|
||||
mg_lwip_recv_common(nc, p);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static void mg_lwip_consume_rx_chain_tcp(struct mg_connection *nc) {
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
if (cs->rx_chain == NULL) return;
|
||||
#if MG_ENABLE_SSL
|
||||
if (nc->flags & MG_F_SSL) {
|
||||
if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) {
|
||||
mg_lwip_ssl_recv(nc);
|
||||
} else {
|
||||
mg_lwip_ssl_do_hs(nc);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
mgos_lock();
|
||||
while (cs->rx_chain != NULL && nc->recv_mbuf.len < nc->recv_mbuf_limit) {
|
||||
struct pbuf *seg = cs->rx_chain;
|
||||
|
||||
size_t seg_len = (seg->len - cs->rx_offset);
|
||||
size_t buf_avail = (nc->recv_mbuf_limit - nc->recv_mbuf.len);
|
||||
size_t len = MIN(seg_len, buf_avail);
|
||||
|
||||
char *data = (char *) MG_MALLOC(len);
|
||||
if (data == NULL) {
|
||||
mgos_unlock();
|
||||
DBG(("OOM"));
|
||||
return;
|
||||
}
|
||||
pbuf_copy_partial(seg, data, len, cs->rx_offset);
|
||||
cs->rx_offset += len;
|
||||
if (cs->rx_offset == cs->rx_chain->len) {
|
||||
cs->rx_chain = pbuf_dechain(cs->rx_chain);
|
||||
pbuf_free(seg);
|
||||
cs->rx_offset = 0;
|
||||
}
|
||||
mgos_unlock();
|
||||
mg_if_recv_tcp_cb(nc, data, len, 1 /* own */);
|
||||
mgos_lock();
|
||||
}
|
||||
mgos_unlock();
|
||||
}
|
||||
|
||||
static void mg_lwip_handle_recv_tcp(struct mg_connection *nc) {
|
||||
mg_lwip_consume_rx_chain_tcp(nc);
|
||||
|
||||
if (nc->send_mbuf.len > 0) {
|
||||
mg_lwip_mgr_schedule_poll(nc->mgr);
|
||||
}
|
||||
}
|
||||
|
||||
static err_t mg_lwip_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb,
|
||||
u16_t num_sent) {
|
||||
struct mg_connection *nc = (struct mg_connection *) arg;
|
||||
DBG(("%p %p %u %p %p", nc, tpcb, num_sent, tpcb->unsent, tpcb->unacked));
|
||||
if (nc == NULL) return ERR_OK;
|
||||
if ((nc->flags & MG_F_SEND_AND_CLOSE) && !(nc->flags & MG_F_WANT_WRITE) &&
|
||||
nc->send_mbuf.len == 0 && tpcb->unsent == NULL && tpcb->unacked == NULL) {
|
||||
mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
struct mg_lwip_if_connect_tcp_ctx {
|
||||
struct mg_connection *nc;
|
||||
const union socket_address *sa;
|
||||
};
|
||||
|
||||
static void mg_lwip_if_connect_tcp_tcpip(void *arg) {
|
||||
struct mg_lwip_if_connect_tcp_ctx *ctx =
|
||||
(struct mg_lwip_if_connect_tcp_ctx *) arg;
|
||||
struct mg_connection *nc = ctx->nc;
|
||||
const union socket_address *sa = ctx->sa;
|
||||
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
struct tcp_pcb *tpcb = TCP_NEW();
|
||||
cs->pcb.tcp = tpcb;
|
||||
ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr;
|
||||
u16_t port = ntohs(sa->sin.sin_port);
|
||||
tcp_arg(tpcb, nc);
|
||||
tcp_err(tpcb, mg_lwip_tcp_error_cb);
|
||||
tcp_sent(tpcb, mg_lwip_tcp_sent_cb);
|
||||
tcp_recv(tpcb, mg_lwip_tcp_recv_cb);
|
||||
cs->err = TCP_BIND(tpcb, IP_ADDR_ANY, 0 /* any port */);
|
||||
DBG(("%p tcp_bind = %d", nc, cs->err));
|
||||
if (cs->err != ERR_OK) {
|
||||
mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
|
||||
return;
|
||||
}
|
||||
cs->err = tcp_connect(tpcb, ip, port, mg_lwip_tcp_conn_cb);
|
||||
DBG(("%p tcp_connect %p = %d", nc, tpcb, cs->err));
|
||||
if (cs->err != ERR_OK) {
|
||||
mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void mg_lwip_if_connect_tcp(struct mg_connection *nc,
|
||||
const union socket_address *sa) {
|
||||
struct mg_lwip_if_connect_tcp_ctx ctx = {.nc = nc, .sa = sa};
|
||||
tcpip_callback(mg_lwip_if_connect_tcp_tcpip, &ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lwip included in the SDKs for nRF5x chips has different type for the
|
||||
* callback of `udp_recv()`
|
||||
*/
|
||||
#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105
|
||||
static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p,
|
||||
const ip_addr_t *addr, u16_t port)
|
||||
#else
|
||||
static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p,
|
||||
ip_addr_t *addr, u16_t port)
|
||||
#endif
|
||||
{
|
||||
struct mg_connection *nc = (struct mg_connection *) arg;
|
||||
DBG(("%p %s:%u %p %u %u", nc, IPADDR_NTOA(addr), port, p, p->ref, p->len));
|
||||
/* Put address in a separate pbuf and tack it onto the packet. */
|
||||
struct pbuf *sap =
|
||||
pbuf_alloc(PBUF_RAW, sizeof(union socket_address), PBUF_RAM);
|
||||
if (sap == NULL) {
|
||||
pbuf_free(p);
|
||||
return;
|
||||
}
|
||||
union socket_address *sa = (union socket_address *) sap->payload;
|
||||
#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105
|
||||
sa->sin.sin_addr.s_addr = ip_2_ip4(addr)->addr;
|
||||
#else
|
||||
sa->sin.sin_addr.s_addr = addr->addr;
|
||||
#endif
|
||||
sa->sin.sin_port = htons(port);
|
||||
/* Logic in the recv handler requires that there be exactly one data pbuf. */
|
||||
p = pbuf_coalesce(p, PBUF_RAW);
|
||||
pbuf_chain(sap, p);
|
||||
mg_lwip_recv_common(nc, sap);
|
||||
(void) pcb;
|
||||
}
|
||||
|
||||
static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p) {
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
mgos_lock();
|
||||
if (cs->rx_chain == NULL) {
|
||||
cs->rx_chain = p;
|
||||
} else {
|
||||
pbuf_chain(cs->rx_chain, p);
|
||||
}
|
||||
if (!cs->recv_pending) {
|
||||
cs->recv_pending = 1;
|
||||
mg_lwip_post_signal(MG_SIG_RECV, nc);
|
||||
}
|
||||
mgos_unlock();
|
||||
}
|
||||
|
||||
static void mg_lwip_handle_recv_udp(struct mg_connection *nc) {
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
/*
|
||||
* For UDP, RX chain consists of interleaved address and packet bufs:
|
||||
* Address pbuf followed by exactly one data pbuf (recv_cb took care of that).
|
||||
*/
|
||||
while (cs->rx_chain != NULL) {
|
||||
struct pbuf *sap = cs->rx_chain;
|
||||
struct pbuf *p = sap->next;
|
||||
cs->rx_chain = pbuf_dechain(p);
|
||||
size_t data_len = p->len;
|
||||
char *data = (char *) MG_MALLOC(data_len);
|
||||
if (data != NULL) {
|
||||
pbuf_copy_partial(p, data, data_len, 0);
|
||||
pbuf_free(p);
|
||||
mg_if_recv_udp_cb(nc, data, data_len,
|
||||
(union socket_address *) sap->payload, sap->len);
|
||||
pbuf_free(sap);
|
||||
} else {
|
||||
pbuf_free(p);
|
||||
pbuf_free(sap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mg_lwip_if_connect_udp_tcpip(void *arg) {
|
||||
struct mg_connection *nc = (struct mg_connection *) arg;
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
struct udp_pcb *upcb = udp_new();
|
||||
cs->err = UDP_BIND(upcb, IP_ADDR_ANY, 0 /* any port */);
|
||||
DBG(("%p udp_bind %p = %d", nc, upcb, cs->err));
|
||||
if (cs->err == ERR_OK) {
|
||||
udp_recv(upcb, mg_lwip_udp_recv_cb, nc);
|
||||
cs->pcb.udp = upcb;
|
||||
} else {
|
||||
udp_remove(upcb);
|
||||
}
|
||||
mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
|
||||
}
|
||||
|
||||
void mg_lwip_if_connect_udp(struct mg_connection *nc) {
|
||||
tcpip_callback(mg_lwip_if_connect_udp_tcpip, nc);
|
||||
}
|
||||
|
||||
void mg_lwip_accept_conn(struct mg_connection *nc, struct tcp_pcb *tpcb) {
|
||||
union socket_address sa;
|
||||
SET_ADDR(&sa, &tpcb->remote_ip);
|
||||
sa.sin.sin_port = htons(tpcb->remote_port);
|
||||
mg_if_accept_tcp_cb(nc, &sa, sizeof(sa.sin));
|
||||
}
|
||||
|
||||
static void tcp_close_tcpip(void *arg) {
|
||||
tcp_close((struct tcp_pcb *) arg);
|
||||
}
|
||||
|
||||
void mg_lwip_handle_accept(struct mg_connection *nc) {
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
if (cs->pcb.tcp == NULL) return;
|
||||
#if MG_ENABLE_SSL
|
||||
if (cs->lc->flags & MG_F_SSL) {
|
||||
if (mg_ssl_if_conn_accept(nc, cs->lc) != MG_SSL_OK) {
|
||||
LOG(LL_ERROR, ("SSL error"));
|
||||
tcpip_callback(tcp_close_tcpip, cs->pcb.tcp);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
mg_lwip_accept_conn(nc, cs->pcb.tcp);
|
||||
}
|
||||
}
|
||||
|
||||
static err_t mg_lwip_accept_cb(void *arg, struct tcp_pcb *newtpcb, err_t err) {
|
||||
struct mg_connection *lc = (struct mg_connection *) arg, *nc;
|
||||
struct mg_lwip_conn_state *lcs, *cs;
|
||||
struct tcp_pcb_listen *lpcb;
|
||||
LOG(LL_DEBUG,
|
||||
("%p conn %p from %s:%u", lc, newtpcb,
|
||||
IPADDR_NTOA(ipX_2_ip(&newtpcb->remote_ip)), newtpcb->remote_port));
|
||||
if (lc == NULL) {
|
||||
tcp_abort(newtpcb);
|
||||
return ERR_ABRT;
|
||||
}
|
||||
lcs = (struct mg_lwip_conn_state *) lc->sock;
|
||||
lpcb = (struct tcp_pcb_listen *) lcs->pcb.tcp;
|
||||
#if TCP_LISTEN_BACKLOG
|
||||
tcp_accepted(lpcb);
|
||||
#endif
|
||||
nc = mg_if_accept_new_conn(lc);
|
||||
if (nc == NULL) {
|
||||
tcp_abort(newtpcb);
|
||||
return ERR_ABRT;
|
||||
}
|
||||
cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
cs->lc = lc;
|
||||
cs->pcb.tcp = newtpcb;
|
||||
/* We need to set up callbacks before returning because data may start
|
||||
* arriving immediately. */
|
||||
tcp_arg(newtpcb, nc);
|
||||
tcp_err(newtpcb, mg_lwip_tcp_error_cb);
|
||||
tcp_sent(newtpcb, mg_lwip_tcp_sent_cb);
|
||||
tcp_recv(newtpcb, mg_lwip_tcp_recv_cb);
|
||||
#if LWIP_TCP_KEEPALIVE
|
||||
mg_lwip_set_keepalive_params(nc, 60, 10, 6);
|
||||
#endif
|
||||
mg_lwip_post_signal(MG_SIG_ACCEPT, nc);
|
||||
(void) err;
|
||||
(void) lpcb;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
struct mg_lwip_if_listen_ctx {
|
||||
struct mg_connection *nc;
|
||||
union socket_address *sa;
|
||||
int ret;
|
||||
};
|
||||
|
||||
static void mg_lwip_if_listen_tcp_tcpip(void *arg) {
|
||||
struct mg_lwip_if_listen_ctx *ctx = (struct mg_lwip_if_listen_ctx *) arg;
|
||||
struct mg_connection *nc = ctx->nc;
|
||||
union socket_address *sa = ctx->sa;
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
struct tcp_pcb *tpcb = TCP_NEW();
|
||||
ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr;
|
||||
u16_t port = ntohs(sa->sin.sin_port);
|
||||
cs->err = TCP_BIND(tpcb, ip, port);
|
||||
DBG(("%p tcp_bind(%s:%u) = %d", nc, IPADDR_NTOA(ip), port, cs->err));
|
||||
if (cs->err != ERR_OK) {
|
||||
tcp_close(tpcb);
|
||||
ctx->ret = -1;
|
||||
return;
|
||||
}
|
||||
tcp_arg(tpcb, nc);
|
||||
tpcb = tcp_listen(tpcb);
|
||||
cs->pcb.tcp = tpcb;
|
||||
tcp_accept(tpcb, mg_lwip_accept_cb);
|
||||
ctx->ret = 0;
|
||||
}
|
||||
|
||||
int mg_lwip_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) {
|
||||
struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa};
|
||||
tcpip_callback(mg_lwip_if_listen_tcp_tcpip, &ctx);
|
||||
return ctx.ret;
|
||||
}
|
||||
|
||||
static void mg_lwip_if_listen_udp_tcpip(void *arg) {
|
||||
struct mg_lwip_if_listen_ctx *ctx = (struct mg_lwip_if_listen_ctx *) arg;
|
||||
struct mg_connection *nc = ctx->nc;
|
||||
union socket_address *sa = ctx->sa;
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
struct udp_pcb *upcb = udp_new();
|
||||
ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr;
|
||||
u16_t port = ntohs(sa->sin.sin_port);
|
||||
cs->err = UDP_BIND(upcb, ip, port);
|
||||
DBG(("%p udb_bind(%s:%u) = %d", nc, IPADDR_NTOA(ip), port, cs->err));
|
||||
if (cs->err != ERR_OK) {
|
||||
udp_remove(upcb);
|
||||
ctx->ret = -1;
|
||||
} else {
|
||||
udp_recv(upcb, mg_lwip_udp_recv_cb, nc);
|
||||
cs->pcb.udp = upcb;
|
||||
ctx->ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int mg_lwip_if_listen_udp(struct mg_connection *nc, union socket_address *sa) {
|
||||
struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa};
|
||||
tcpip_callback(mg_lwip_if_listen_udp_tcpip, &ctx);
|
||||
return ctx.ret;
|
||||
}
|
||||
|
||||
struct mg_lwip_tcp_write_ctx {
|
||||
struct mg_connection *nc;
|
||||
const void *data;
|
||||
uint16_t len;
|
||||
int ret;
|
||||
};
|
||||
|
||||
static void tcp_output_tcpip(void *arg) {
|
||||
tcp_output((struct tcp_pcb *) arg);
|
||||
}
|
||||
|
||||
static void mg_lwip_tcp_write_tcpip(void *arg) {
|
||||
struct mg_lwip_tcp_write_ctx *ctx = (struct mg_lwip_tcp_write_ctx *) arg;
|
||||
struct mg_connection *nc = ctx->nc;
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
struct tcp_pcb *tpcb = cs->pcb.tcp;
|
||||
size_t len = MIN(tpcb->mss, MIN(ctx->len, tpcb->snd_buf));
|
||||
size_t unsent, unacked;
|
||||
if (len == 0) {
|
||||
DBG(("%p no buf avail %u %u %p %p", tpcb, tpcb->snd_buf, tpcb->snd_queuelen,
|
||||
tpcb->unsent, tpcb->unacked));
|
||||
tcpip_callback(tcp_output_tcpip, tpcb);
|
||||
ctx->ret = 0;
|
||||
return;
|
||||
}
|
||||
unsent = (tpcb->unsent != NULL ? tpcb->unsent->len : 0);
|
||||
unacked = (tpcb->unacked != NULL ? tpcb->unacked->len : 0);
|
||||
/*
|
||||
* On ESP8266 we only allow one TCP segment in flight at any given time.
|
||||
* This may increase latency and reduce efficiency of tcp windowing,
|
||||
* but memory is scarce and precious on that platform so we do this to
|
||||
* reduce footprint.
|
||||
*/
|
||||
#if CS_PLATFORM == CS_P_ESP8266
|
||||
if (unacked > 0) {
|
||||
ctx->ret = 0;
|
||||
return;
|
||||
}
|
||||
len = MIN(len, (TCP_MSS - unsent));
|
||||
#endif
|
||||
cs->err = tcp_write(tpcb, ctx->data, len, TCP_WRITE_FLAG_COPY);
|
||||
unsent = (tpcb->unsent != NULL ? tpcb->unsent->len : 0);
|
||||
unacked = (tpcb->unacked != NULL ? tpcb->unacked->len : 0);
|
||||
DBG(("%p tcp_write %u = %d, %u %u", tpcb, len, cs->err, unsent, unacked));
|
||||
if (cs->err != ERR_OK) {
|
||||
/*
|
||||
* We ignore ERR_MEM because memory will be freed up when the data is sent
|
||||
* and we'll retry.
|
||||
*/
|
||||
ctx->ret = (cs->err == ERR_MEM ? 0 : -1);
|
||||
return;
|
||||
}
|
||||
ctx->ret = len;
|
||||
}
|
||||
|
||||
static int mg_lwip_tcp_write(struct mg_connection *nc, const void *data,
|
||||
uint16_t len) {
|
||||
struct mg_lwip_tcp_write_ctx ctx = {.nc = nc, .data = data, .len = len};
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
struct tcp_pcb *tpcb = cs->pcb.tcp;
|
||||
if (tpcb == NULL) {
|
||||
return -1;
|
||||
}
|
||||
tcpip_callback(mg_lwip_tcp_write_tcpip, &ctx);
|
||||
return ctx.ret;
|
||||
}
|
||||
|
||||
struct udp_sendto_ctx {
|
||||
struct udp_pcb *upcb;
|
||||
struct pbuf *p;
|
||||
ip_addr_t *ip;
|
||||
uint16_t port;
|
||||
int ret;
|
||||
};
|
||||
|
||||
static void udp_sendto_tcpip(void *arg) {
|
||||
struct udp_sendto_ctx *ctx = (struct udp_sendto_ctx *) arg;
|
||||
ctx->ret = udp_sendto(ctx->upcb, ctx->p, ctx->ip, ctx->port);
|
||||
}
|
||||
|
||||
static int mg_lwip_udp_send(struct mg_connection *nc, const void *data,
|
||||
uint16_t len) {
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
if (cs->pcb.udp == NULL) {
|
||||
/*
|
||||
* In case of UDP, this usually means, what
|
||||
* async DNS resolve is still in progress and connection
|
||||
* is not ready yet
|
||||
*/
|
||||
DBG(("%p socket is not connected", nc));
|
||||
return -1;
|
||||
}
|
||||
struct udp_pcb *upcb = cs->pcb.udp;
|
||||
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
|
||||
#if defined(LWIP_IPV4) && LWIP_IPV4 && defined(LWIP_IPV6) && LWIP_IPV6
|
||||
ip_addr_t ip = {.u_addr.ip4.addr = nc->sa.sin.sin_addr.s_addr, .type = 0};
|
||||
#else
|
||||
ip_addr_t ip = {.addr = nc->sa.sin.sin_addr.s_addr};
|
||||
#endif
|
||||
u16_t port = ntohs(nc->sa.sin.sin_port);
|
||||
if (p == NULL) {
|
||||
DBG(("OOM"));
|
||||
return 0;
|
||||
}
|
||||
memcpy(p->payload, data, len);
|
||||
struct udp_sendto_ctx ctx = {.upcb = upcb, .p = p, .ip = &ip, .port = port};
|
||||
tcpip_callback(udp_sendto_tcpip, &ctx);
|
||||
cs->err = ctx.ret;
|
||||
pbuf_free(p);
|
||||
return (cs->err == ERR_OK ? len : -1);
|
||||
}
|
||||
|
||||
static void mg_lwip_send_more(struct mg_connection *nc) {
|
||||
int num_sent = 0;
|
||||
if (nc->sock == INVALID_SOCKET) return;
|
||||
if (nc->flags & MG_F_UDP) {
|
||||
num_sent = mg_lwip_udp_send(nc, nc->send_mbuf.buf, nc->send_mbuf.len);
|
||||
DBG(("%p mg_lwip_udp_send %u = %d", nc, nc->send_mbuf.len, num_sent));
|
||||
} else {
|
||||
num_sent = mg_lwip_tcp_write(nc, nc->send_mbuf.buf, nc->send_mbuf.len);
|
||||
DBG(("%p mg_lwip_tcp_write %u = %d", nc, nc->send_mbuf.len, num_sent));
|
||||
}
|
||||
if (num_sent == 0) return;
|
||||
if (num_sent > 0) {
|
||||
mg_if_sent_cb(nc, num_sent);
|
||||
} else {
|
||||
mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
|
||||
}
|
||||
}
|
||||
|
||||
void mg_lwip_if_tcp_send(struct mg_connection *nc, const void *buf,
|
||||
size_t len) {
|
||||
mbuf_append(&nc->send_mbuf, buf, len);
|
||||
mg_lwip_mgr_schedule_poll(nc->mgr);
|
||||
}
|
||||
|
||||
void mg_lwip_if_udp_send(struct mg_connection *nc, const void *buf,
|
||||
size_t len) {
|
||||
mbuf_append(&nc->send_mbuf, buf, len);
|
||||
mg_lwip_mgr_schedule_poll(nc->mgr);
|
||||
}
|
||||
|
||||
struct tcp_recved_ctx {
|
||||
struct tcp_pcb *tpcb;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
void tcp_recved_tcpip(void *arg) {
|
||||
struct tcp_recved_ctx *ctx = (struct tcp_recved_ctx *) arg;
|
||||
tcp_recved(ctx->tpcb, ctx->len);
|
||||
}
|
||||
|
||||
void mg_lwip_if_recved(struct mg_connection *nc, size_t len) {
|
||||
if (nc->flags & MG_F_UDP) return;
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
if (nc->sock == INVALID_SOCKET || cs->pcb.tcp == NULL) {
|
||||
DBG(("%p invalid socket", nc));
|
||||
return;
|
||||
}
|
||||
DBG(("%p %p %u %u", nc, cs->pcb.tcp, len,
|
||||
(cs->rx_chain ? cs->rx_chain->tot_len : 0)));
|
||||
struct tcp_recved_ctx ctx = {.tpcb = cs->pcb.tcp, .len = len};
|
||||
#if MG_ENABLE_SSL
|
||||
if (!(nc->flags & MG_F_SSL)) {
|
||||
tcpip_callback(tcp_recved_tcpip, &ctx);
|
||||
} else {
|
||||
/* Currently SSL acknowledges data immediately.
|
||||
* TODO(rojer): Find a way to propagate mg_lwip_if_recved. */
|
||||
}
|
||||
#else
|
||||
tcpip_callback(tcp_recved_tcpip, &ctx);
|
||||
#endif
|
||||
mbuf_trim(&nc->recv_mbuf);
|
||||
}
|
||||
|
||||
int mg_lwip_if_create_conn(struct mg_connection *nc) {
|
||||
struct mg_lwip_conn_state *cs =
|
||||
(struct mg_lwip_conn_state *) MG_CALLOC(1, sizeof(*cs));
|
||||
if (cs == NULL) return 0;
|
||||
cs->nc = nc;
|
||||
nc->sock = (intptr_t) cs;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void udp_remove_tcpip(void *arg) {
|
||||
udp_remove((struct udp_pcb *) arg);
|
||||
}
|
||||
|
||||
void mg_lwip_if_destroy_conn(struct mg_connection *nc) {
|
||||
if (nc->sock == INVALID_SOCKET) return;
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
if (!(nc->flags & MG_F_UDP)) {
|
||||
struct tcp_pcb *tpcb = cs->pcb.tcp;
|
||||
if (tpcb != NULL) {
|
||||
tcp_arg(tpcb, NULL);
|
||||
DBG(("%p tcp_close %p", nc, tpcb));
|
||||
tcp_arg(tpcb, NULL);
|
||||
tcpip_callback(tcp_close_tcpip, tpcb);
|
||||
}
|
||||
while (cs->rx_chain != NULL) {
|
||||
struct pbuf *seg = cs->rx_chain;
|
||||
cs->rx_chain = pbuf_dechain(cs->rx_chain);
|
||||
pbuf_free(seg);
|
||||
}
|
||||
memset(cs, 0, sizeof(*cs));
|
||||
MG_FREE(cs);
|
||||
} else if (nc->listener == NULL) {
|
||||
/* Only close outgoing UDP pcb or listeners. */
|
||||
struct udp_pcb *upcb = cs->pcb.udp;
|
||||
if (upcb != NULL) {
|
||||
DBG(("%p udp_remove %p", nc, upcb));
|
||||
tcpip_callback(udp_remove_tcpip, upcb);
|
||||
}
|
||||
memset(cs, 0, sizeof(*cs));
|
||||
MG_FREE(cs);
|
||||
}
|
||||
nc->sock = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
void mg_lwip_if_get_conn_addr(struct mg_connection *nc, int remote,
|
||||
union socket_address *sa) {
|
||||
memset(sa, 0, sizeof(*sa));
|
||||
if (nc == NULL || nc->sock == INVALID_SOCKET) return;
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
if (nc->flags & MG_F_UDP) {
|
||||
struct udp_pcb *upcb = cs->pcb.udp;
|
||||
if (remote) {
|
||||
memcpy(sa, &nc->sa, sizeof(*sa));
|
||||
} else if (upcb != NULL) {
|
||||
sa->sin.sin_port = htons(upcb->local_port);
|
||||
SET_ADDR(sa, &upcb->local_ip);
|
||||
}
|
||||
} else {
|
||||
struct tcp_pcb *tpcb = cs->pcb.tcp;
|
||||
if (remote) {
|
||||
memcpy(sa, &nc->sa, sizeof(*sa));
|
||||
} else if (tpcb != NULL) {
|
||||
sa->sin.sin_port = htons(tpcb->local_port);
|
||||
SET_ADDR(sa, &tpcb->local_ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mg_lwip_if_sock_set(struct mg_connection *nc, sock_t sock) {
|
||||
nc->sock = sock;
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
#define MG_LWIP_IFACE_VTABLE \
|
||||
{ \
|
||||
mg_lwip_if_init, \
|
||||
mg_lwip_if_free, \
|
||||
mg_lwip_if_add_conn, \
|
||||
mg_lwip_if_remove_conn, \
|
||||
mg_lwip_if_poll, \
|
||||
mg_lwip_if_listen_tcp, \
|
||||
mg_lwip_if_listen_udp, \
|
||||
mg_lwip_if_connect_tcp, \
|
||||
mg_lwip_if_connect_udp, \
|
||||
mg_lwip_if_tcp_send, \
|
||||
mg_lwip_if_udp_send, \
|
||||
mg_lwip_if_recved, \
|
||||
mg_lwip_if_create_conn, \
|
||||
mg_lwip_if_destroy_conn, \
|
||||
mg_lwip_if_sock_set, \
|
||||
mg_lwip_if_get_conn_addr, \
|
||||
}
|
||||
/* clang-format on */
|
||||
|
||||
const struct mg_iface_vtable mg_lwip_iface_vtable = MG_LWIP_IFACE_VTABLE;
|
||||
#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL
|
||||
const struct mg_iface_vtable mg_default_iface_vtable = MG_LWIP_IFACE_VTABLE;
|
||||
#endif
|
||||
|
||||
#endif /* MG_ENABLE_NET_IF_LWIP_LOW_LEVEL */
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#ifndef CS_COMMON_PLATFORMS_LWIP_MG_NET_IF_LWIP_H_
|
||||
#define CS_COMMON_PLATFORMS_LWIP_MG_NET_IF_LWIP_H_
|
||||
|
||||
#ifndef MG_ENABLE_NET_IF_LWIP_LOW_LEVEL
|
||||
#define MG_ENABLE_NET_IF_LWIP_LOW_LEVEL MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL
|
||||
#endif
|
||||
|
||||
#if MG_ENABLE_NET_IF_LWIP_LOW_LEVEL
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern const struct mg_iface_vtable mg_lwip_iface_vtable;
|
||||
|
||||
struct mg_lwip_conn_state {
|
||||
struct mg_connection *nc;
|
||||
struct mg_connection *lc;
|
||||
union {
|
||||
struct tcp_pcb *tcp;
|
||||
struct udp_pcb *udp;
|
||||
} pcb;
|
||||
err_t err;
|
||||
size_t num_sent; /* Number of acknowledged bytes to be reported to the core */
|
||||
struct pbuf *rx_chain; /* Chain of incoming data segments. */
|
||||
size_t rx_offset; /* Offset within the first pbuf (if partially consumed) */
|
||||
/* Last SSL write size, for retries. */
|
||||
int last_ssl_write_size;
|
||||
/* Whether MG_SIG_RECV is already pending for this connection */
|
||||
int recv_pending : 1;
|
||||
/* Whether the connection is about to close, just `rx_chain` needs to drain */
|
||||
int draining_rx_chain : 1;
|
||||
};
|
||||
|
||||
enum mg_sig_type {
|
||||
MG_SIG_CONNECT_RESULT = 1,
|
||||
MG_SIG_RECV = 2,
|
||||
MG_SIG_CLOSE_CONN = 3,
|
||||
MG_SIG_TOMBSTONE = 4,
|
||||
MG_SIG_ACCEPT = 5,
|
||||
};
|
||||
|
||||
void mg_lwip_post_signal(enum mg_sig_type sig, struct mg_connection *nc);
|
||||
|
||||
/* To be implemented by the platform. */
|
||||
void mg_lwip_mgr_schedule_poll(struct mg_mgr *mgr);
|
||||
|
||||
#endif /* MG_ENABLE_NET_IF_LWIP_LOW_LEVEL */
|
||||
|
||||
#endif /* CS_COMMON_PLATFORMS_LWIP_MG_NET_IF_LWIP_H_ */
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#if MG_ENABLE_SSL && MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL
|
||||
|
||||
#include "common/mg_mem.h"
|
||||
#include "common/cs_dbg.h"
|
||||
|
||||
#include <lwip/pbuf.h>
|
||||
#include <lwip/tcp.h>
|
||||
|
||||
#ifndef MG_LWIP_SSL_IO_SIZE
|
||||
#define MG_LWIP_SSL_IO_SIZE 1024
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
void mg_lwip_ssl_do_hs(struct mg_connection *nc) {
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
int server_side = (nc->listener != NULL);
|
||||
enum mg_ssl_if_result res;
|
||||
if (nc->flags & MG_F_CLOSE_IMMEDIATELY) return;
|
||||
res = mg_ssl_if_handshake(nc);
|
||||
DBG(("%p %lu %d %d", nc, nc->flags, server_side, res));
|
||||
if (res != MG_SSL_OK) {
|
||||
if (res == MG_SSL_WANT_WRITE) {
|
||||
nc->flags |= MG_F_WANT_WRITE;
|
||||
cs->err = 0;
|
||||
} else if (res == MG_SSL_WANT_READ) {
|
||||
/*
|
||||
* Nothing to do in particular, we are callback-driven.
|
||||
* What we definitely do not need anymore is SSL reading (nothing left).
|
||||
*/
|
||||
nc->flags &= ~MG_F_WANT_READ;
|
||||
cs->err = 0;
|
||||
} else {
|
||||
cs->err = res;
|
||||
if (server_side) {
|
||||
mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
|
||||
} else {
|
||||
mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cs->err = 0;
|
||||
nc->flags &= ~MG_F_WANT_WRITE;
|
||||
/*
|
||||
* Handshake is done. Schedule a read immediately to consume app data
|
||||
* which may already be waiting.
|
||||
*/
|
||||
nc->flags |= (MG_F_SSL_HANDSHAKE_DONE | MG_F_WANT_READ);
|
||||
if (server_side) {
|
||||
mg_lwip_accept_conn(nc, cs->pcb.tcp);
|
||||
} else {
|
||||
mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mg_lwip_ssl_send(struct mg_connection *nc) {
|
||||
if (nc->sock == INVALID_SOCKET) {
|
||||
DBG(("%p invalid socket", nc));
|
||||
return;
|
||||
}
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
/* It's ok if the buffer is empty. Return value of 0 may also be valid. */
|
||||
int len = cs->last_ssl_write_size;
|
||||
if (len == 0) {
|
||||
len = MIN(MG_LWIP_SSL_IO_SIZE, nc->send_mbuf.len);
|
||||
}
|
||||
int ret = mg_ssl_if_write(nc, nc->send_mbuf.buf, len);
|
||||
DBG(("%p SSL_write %u = %d", nc, len, ret));
|
||||
if (ret > 0) {
|
||||
mg_if_sent_cb(nc, ret);
|
||||
cs->last_ssl_write_size = 0;
|
||||
} else if (ret < 0) {
|
||||
/* This is tricky. We must remember the exact data we were sending to retry
|
||||
* exactly the same send next time. */
|
||||
cs->last_ssl_write_size = len;
|
||||
}
|
||||
if (ret == len) {
|
||||
nc->flags &= ~MG_F_WANT_WRITE;
|
||||
} else if (ret == MG_SSL_WANT_WRITE) {
|
||||
nc->flags |= MG_F_WANT_WRITE;
|
||||
} else {
|
||||
mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
|
||||
}
|
||||
}
|
||||
|
||||
void mg_lwip_ssl_recv(struct mg_connection *nc) {
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
/* Don't deliver data before connect callback */
|
||||
if (nc->flags & MG_F_CONNECTING) return;
|
||||
while (nc->recv_mbuf.len < nc->recv_mbuf_limit) {
|
||||
char *buf = (char *) MG_MALLOC(MG_LWIP_SSL_IO_SIZE);
|
||||
if (buf == NULL) return;
|
||||
int ret = mg_ssl_if_read(nc, buf, MG_LWIP_SSL_IO_SIZE);
|
||||
DBG(("%p %p SSL_read %u = %d", nc, cs->rx_chain, MG_LWIP_SSL_IO_SIZE, ret));
|
||||
if (ret <= 0) {
|
||||
MG_FREE(buf);
|
||||
if (ret == MG_SSL_WANT_WRITE) {
|
||||
nc->flags |= MG_F_WANT_WRITE;
|
||||
return;
|
||||
} else if (ret == MG_SSL_WANT_READ) {
|
||||
/*
|
||||
* Nothing to do in particular, we are callback-driven.
|
||||
* What we definitely do not need anymore is SSL reading (nothing left).
|
||||
*/
|
||||
nc->flags &= ~MG_F_WANT_READ;
|
||||
cs->err = 0;
|
||||
return;
|
||||
} else {
|
||||
mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
mg_if_recv_tcp_cb(nc, buf, ret, 1 /* own */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef KR_VERSION
|
||||
|
||||
ssize_t kr_send(int fd, const void *buf, size_t len) {
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) fd;
|
||||
int ret = mg_lwip_tcp_write(cs->nc, buf, len);
|
||||
DBG(("%p mg_lwip_tcp_write %u = %d", cs->nc, len, ret));
|
||||
if (ret == 0) ret = KR_IO_WOULDBLOCK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t kr_recv(int fd, void *buf, size_t len) {
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) fd;
|
||||
struct pbuf *seg = cs->rx_chain;
|
||||
if (seg == NULL) {
|
||||
DBG(("%u - nothing to read", len));
|
||||
return KR_IO_WOULDBLOCK;
|
||||
}
|
||||
size_t seg_len = (seg->len - cs->rx_offset);
|
||||
DBG(("%u %u %u %u", len, cs->rx_chain->len, seg_len, cs->rx_chain->tot_len));
|
||||
len = MIN(len, seg_len);
|
||||
pbuf_copy_partial(seg, buf, len, cs->rx_offset);
|
||||
cs->rx_offset += len;
|
||||
tcp_recved(cs->pcb.tcp, len);
|
||||
if (cs->rx_offset == cs->rx_chain->len) {
|
||||
cs->rx_chain = pbuf_dechain(cs->rx_chain);
|
||||
pbuf_free(seg);
|
||||
cs->rx_offset = 0;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
#elif MG_SSL_IF == MG_SSL_IF_MBEDTLS
|
||||
|
||||
int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len) {
|
||||
struct mg_connection *nc = (struct mg_connection *) ctx;
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
int ret = mg_lwip_tcp_write(cs->nc, buf, len);
|
||||
if (ret == 0) ret = MBEDTLS_ERR_SSL_WANT_WRITE;
|
||||
LOG(LL_DEBUG, ("%p %d -> %d", nc, len, ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len) {
|
||||
struct mg_connection *nc = (struct mg_connection *) ctx;
|
||||
struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
|
||||
struct pbuf *seg = cs->rx_chain;
|
||||
if (seg == NULL) {
|
||||
DBG(("%u - nothing to read", len));
|
||||
return MBEDTLS_ERR_SSL_WANT_READ;
|
||||
}
|
||||
size_t seg_len = (seg->len - cs->rx_offset);
|
||||
DBG(("%u %u %u %u", len, cs->rx_chain->len, seg_len, cs->rx_chain->tot_len));
|
||||
mgos_lock();
|
||||
len = MIN(len, seg_len);
|
||||
pbuf_copy_partial(seg, buf, len, cs->rx_offset);
|
||||
cs->rx_offset += len;
|
||||
/* TCP PCB may be NULL if connection has already been closed
|
||||
* but we still have data to deliver to SSL. */
|
||||
if (cs->pcb.tcp != NULL) tcp_recved(cs->pcb.tcp, len);
|
||||
if (cs->rx_offset == cs->rx_chain->len) {
|
||||
cs->rx_chain = pbuf_dechain(cs->rx_chain);
|
||||
pbuf_free(seg);
|
||||
cs->rx_offset = 0;
|
||||
}
|
||||
mgos_unlock();
|
||||
LOG(LL_DEBUG, ("%p <- %d", nc, (int) len));
|
||||
return len;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* MG_ENABLE_SSL && MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL */
|
||||
Reference in new issue
Block a user