Commit ed24afd1 authored by Sergey Lyubka's avatar Sergey Lyubka Committed by rojer

Connect timeouts for resolving connections

    PUBLISHED_FROM=068b6f75550b10913d7f13106f9a7f7dce5babec
parent 714556de
...@@ -2488,11 +2488,13 @@ void mg_if_connect_cb(struct mg_connection *nc, int err) { ...@@ -2488,11 +2488,13 @@ void mg_if_connect_cb(struct mg_connection *nc, int err) {
* either failure (and dealloc the connection) * either failure (and dealloc the connection)
* or success (and proceed with connect() * or success (and proceed with connect()
*/ */
static void resolve_cb(struct mg_dns_message *msg, void *data) { static void resolve_cb(struct mg_dns_message *msg, void *data,
enum mg_resolve_err e) {
struct mg_connection *nc = (struct mg_connection *) data; struct mg_connection *nc = (struct mg_connection *) data;
int i; int i;
int failure = -1; int failure = -1;
nc->flags &= ~MG_F_RESOLVING;
if (msg != NULL) { if (msg != NULL) {
/* /*
* Take the first DNS A answer and run... * Take the first DNS A answer and run...
...@@ -2512,6 +2514,11 @@ static void resolve_cb(struct mg_dns_message *msg, void *data) { ...@@ -2512,6 +2514,11 @@ static void resolve_cb(struct mg_dns_message *msg, void *data) {
} }
} }
if (e == MG_RESOLVE_TIMEOUT) {
double now = time(NULL);
mg_call(nc, NULL, MG_EV_TIMER, &now);
}
/* /*
* If we get there was no MG_DNS_A_RECORD in the answer * If we get there was no MG_DNS_A_RECORD in the answer
*/ */
...@@ -2557,12 +2564,18 @@ struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address, ...@@ -2557,12 +2564,18 @@ struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address,
* DNS resolution is required for host. * DNS resolution is required for host.
* mg_parse_address() fills port in nc->sa, which we pass to resolve_cb() * mg_parse_address() fills port in nc->sa, which we pass to resolve_cb()
*/ */
if (mg_resolve_async(nc->mgr, host, MG_DNS_A_RECORD, resolve_cb, nc) != 0) { struct mg_connection *dns_conn = NULL;
struct mg_resolve_async_opts o;
memset(&o, 0, sizeof(o));
o.dns_conn = &dns_conn;
if (mg_resolve_async_opt(nc->mgr, host, MG_DNS_A_RECORD, resolve_cb, nc,
o) != 0) {
MG_SET_PTRPTR(opts.error_string, "cannot schedule DNS lookup"); MG_SET_PTRPTR(opts.error_string, "cannot schedule DNS lookup");
mg_destroy_conn(nc); mg_destroy_conn(nc);
return NULL; return NULL;
} }
nc->priv_2 = dns_conn;
nc->flags |= MG_F_RESOLVING;
return nc; return nc;
#else #else
MG_SET_PTRPTR(opts.error_string, "Resolver is disabled"); MG_SET_PTRPTR(opts.error_string, "Resolver is disabled");
...@@ -2700,6 +2713,21 @@ void mg_forward(struct mg_connection *from, struct mg_connection *to) { ...@@ -2700,6 +2713,21 @@ void mg_forward(struct mg_connection *from, struct mg_connection *to) {
mg_send(to, from->recv_mbuf.buf, from->recv_mbuf.len); mg_send(to, from->recv_mbuf.buf, from->recv_mbuf.len);
mbuf_remove(&from->recv_mbuf, from->recv_mbuf.len); mbuf_remove(&from->recv_mbuf, from->recv_mbuf.len);
} }
double mg_set_timer(struct mg_connection *c, double timestamp) {
double result = c->ev_timer_time;
c->ev_timer_time = timestamp;
/*
* If this connection is resolving, it's not in the list of active
* connections, so not processed yet. It has a DNS resolver connection
* linked to it. Set up a timer for the DNS connection.
*/
DBG(("%p %p %d", c, c->priv_2, c->flags & MG_F_RESOLVING));
if ((c->flags & MG_F_RESOLVING) && c->priv_2 != NULL) {
((struct mg_connection *) c->priv_2)->ev_timer_time = timestamp;
}
return result;
}
#ifdef NS_MODULE_LINES #ifdef NS_MODULE_LINES
#line 1 "src/net_if_socket.c" #line 1 "src/net_if_socket.c"
/**/ /**/
...@@ -7835,6 +7863,7 @@ struct mg_resolve_async_request { ...@@ -7835,6 +7863,7 @@ struct mg_resolve_async_request {
void *data; void *data;
time_t timeout; time_t timeout;
int max_retries; int max_retries;
enum mg_resolve_err err;
/* state */ /* state */
time_t last_time; time_t last_time;
...@@ -7964,6 +7993,7 @@ static void mg_resolve_async_eh(struct mg_connection *nc, int ev, void *data) { ...@@ -7964,6 +7993,7 @@ static void mg_resolve_async_eh(struct mg_connection *nc, int ev, void *data) {
case MG_EV_CONNECT: case MG_EV_CONNECT:
case MG_EV_POLL: case MG_EV_POLL:
if (req->retries > req->max_retries) { if (req->retries > req->max_retries) {
req->err = MG_RESOLVE_EXCEEDED_RETRY_COUNT;
nc->flags |= MG_F_CLOSE_IMMEDIATELY; nc->flags |= MG_F_CLOSE_IMMEDIATELY;
break; break;
} }
...@@ -7977,9 +8007,11 @@ static void mg_resolve_async_eh(struct mg_connection *nc, int ev, void *data) { ...@@ -7977,9 +8007,11 @@ static void mg_resolve_async_eh(struct mg_connection *nc, int ev, void *data) {
msg = (struct mg_dns_message *) MG_MALLOC(sizeof(*msg)); msg = (struct mg_dns_message *) MG_MALLOC(sizeof(*msg));
if (mg_parse_dns(nc->recv_mbuf.buf, *(int *) data, msg) == 0 && if (mg_parse_dns(nc->recv_mbuf.buf, *(int *) data, msg) == 0 &&
msg->num_answers > 0) { msg->num_answers > 0) {
req->callback(msg, req->data); req->callback(msg, req->data, MG_RESOLVE_OK);
nc->user_data = NULL; nc->user_data = NULL;
MG_FREE(req); MG_FREE(req);
} else {
req->err = MG_RESOLVE_NO_ANSWERS;
} }
MG_FREE(msg); MG_FREE(msg);
nc->flags |= MG_F_CLOSE_IMMEDIATELY; nc->flags |= MG_F_CLOSE_IMMEDIATELY;
...@@ -7992,10 +8024,14 @@ static void mg_resolve_async_eh(struct mg_connection *nc, int ev, void *data) { ...@@ -7992,10 +8024,14 @@ static void mg_resolve_async_eh(struct mg_connection *nc, int ev, void *data) {
nc->flags &= ~MG_F_CLOSE_IMMEDIATELY; nc->flags &= ~MG_F_CLOSE_IMMEDIATELY;
mbuf_remove(&nc->send_mbuf, nc->send_mbuf.len); mbuf_remove(&nc->send_mbuf, nc->send_mbuf.len);
break; break;
case MG_EV_TIMER:
req->err = MG_RESOLVE_TIMEOUT;
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
break;
case MG_EV_CLOSE: case MG_EV_CLOSE:
/* If we got here with request still not done, fire an error callback. */ /* If we got here with request still not done, fire an error callback. */
if (req != NULL) { if (req != NULL) {
req->callback(NULL, req->data); req->callback(NULL, req->data, req->err);
nc->user_data = NULL; nc->user_data = NULL;
MG_FREE(req); MG_FREE(req);
} }
...@@ -8017,7 +8053,7 @@ int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query, ...@@ -8017,7 +8053,7 @@ int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query,
struct mg_connection *dns_nc; struct mg_connection *dns_nc;
const char *nameserver = opts.nameserver_url; const char *nameserver = opts.nameserver_url;
DBG(("%s %d", name, query)); DBG(("%s %d %p", name, query, opts.dns_conn));
/* resolve with DNS */ /* resolve with DNS */
req = (struct mg_resolve_async_request *) MG_CALLOC(1, sizeof(*req)); req = (struct mg_resolve_async_request *) MG_CALLOC(1, sizeof(*req));
...@@ -8050,6 +8086,9 @@ int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query, ...@@ -8050,6 +8086,9 @@ int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query,
return -1; return -1;
} }
dns_nc->user_data = req; dns_nc->user_data = req;
if (opts.dns_conn != NULL) {
*opts.dns_conn = dns_nc;
}
return 0; return 0;
} }
......
...@@ -1063,6 +1063,35 @@ enum v7_err mg_enable_javascript(struct mg_mgr *m, struct v7 *v7, ...@@ -1063,6 +1063,35 @@ enum v7_err mg_enable_javascript(struct mg_mgr *m, struct v7 *v7,
const char *init_js_file_name); const char *init_js_file_name);
#endif #endif
/*
* Schedule MG_EV_TIMER event to be delivered at `timestamp` time.
* `timestamp` is a UNIX time (a number of seconds since Epoch). It is
* `double` instead of `time_t` to allow for sub-second precision.
* Return the old timer value.
*
* Example: set connect timeout to 1.5 seconds:
*
* ```
* c = mg_connect(&mgr, "cesanta.com", ev_handler);
* mg_set_timer(c, time(NULL) + 1.5);
* ...
*
* void ev_handler(struct mg_connection *c, int ev, void *ev_data) {
* switch (ev) {
* case MG_EV_CONNECT:
* mg_set_timer(c, 0); // Clear connect timer
* break;
* case MG_EV_TIMER:
* log("Connect timeout");
* c->flags |= MG_F_CLOSE_IMMEDIATELY;
* break;
```
*
* NOTE: sub-second precision is not implemented yet, current granularity
* is 1 second.
*/
double mg_set_timer(struct mg_connection *c, double timestamp);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */
...@@ -2556,7 +2585,15 @@ void mg_dns_send_reply(struct mg_connection *, struct mg_dns_reply *); ...@@ -2556,7 +2585,15 @@ void mg_dns_send_reply(struct mg_connection *, struct mg_dns_reply *);
extern "C" { extern "C" {
#endif /* __cplusplus */ #endif /* __cplusplus */
typedef void (*mg_resolve_callback_t)(struct mg_dns_message *, void *); enum mg_resolve_err {
MG_RESOLVE_OK = 0,
MG_RESOLVE_NO_ANSWERS = 1,
MG_RESOLVE_EXCEEDED_RETRY_COUNT = 2,
MG_RESOLVE_TIMEOUT = 3
};
typedef void (*mg_resolve_callback_t)(struct mg_dns_message *dns_message,
void *user_data, enum mg_resolve_err);
/* Options for `mg_resolve_async_opt`. */ /* Options for `mg_resolve_async_opt`. */
struct mg_resolve_async_opts { struct mg_resolve_async_opts {
...@@ -2565,6 +2602,7 @@ struct mg_resolve_async_opts { ...@@ -2565,6 +2602,7 @@ struct mg_resolve_async_opts {
int timeout; /* in seconds; defaults to 5 if zero */ int timeout; /* in seconds; defaults to 5 if zero */
int accept_literal; /* pseudo-resolve literal ipv4 and ipv6 addrs */ int accept_literal; /* pseudo-resolve literal ipv4 and ipv6 addrs */
int only_literal; /* only resolves literal addrs; sync cb invocation */ int only_literal; /* only resolves literal addrs; sync cb invocation */
struct mg_connection **dns_conn; /* return DNS connection */
}; };
/* See `mg_resolve_async_opt()` */ /* See `mg_resolve_async_opt()` */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment