[OpenWrt-Devel,V2,uclient] support for connection timeout
diff mbox

Message ID 1421401374-9085-1-git-send-email-zajec5@gmail.com
State Changes Requested
Headers show

Commit Message

Rafał Miłecki Jan. 16, 2015, 9:42 a.m. UTC
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
---
V2: Add uclient_set_timeout
    Stop timer after receiving data
    Start timer after fetching data by uclient user
---
 uclient-fetch.c |  4 ++++
 uclient-http.c  | 12 ++++++++++--
 uclient.c       | 36 +++++++++++++++++++++++++++++++++++-
 uclient.h       | 20 ++++++++++++++++++++
 4 files changed, 69 insertions(+), 3 deletions(-)

Comments

John Szakmeister Jan. 16, 2015, 10:01 a.m. UTC | #1
On Fri, Jan 16, 2015 at 4:42 AM, Rafał Miłecki <zajec5@gmail.com> wrote:
[snip]
> -       if (uh->state == HTTP_STATE_RECV_DATA && uc->cb->data_read)
> -               uc->cb->data_read(uc);
> +       if (uh->state == HTTP_STATE_RECV_DATA)
> +               /* Now it's uclient user turn to read some data */
> +               uloop_timeout_cancel(&uc->connection_timeout);
> +
> +               if (uc->cb->data_read)
> +                       uc->cb->data_read(uc);

The above snippet doesn't look right.  I think you probably
meant to add curly braces around the block:

    if (uh->state == HTTP_STATE_RECV_DATA) {
        // ...
    }

-John

Patch
diff mbox

diff --git a/uclient-fetch.c b/uclient-fetch.c
index 22f15c6..0617a02 100644
--- a/uclient-fetch.c
+++ b/uclient-fetch.c
@@ -194,6 +194,10 @@  static void handle_uclient_error(struct uclient *cl, int code)
 		type = "Connection failed";
 		error_ret = 4;
 		break;
+	case UCLIENT_ERROR_TIMEDOUT:
+		type = "Connection timed out";
+		error_ret = 4;
+		break;
 	case UCLIENT_ERROR_SSL_INVALID_CERT:
 		type = "Invalid SSL certificate";
 		ignore = !verify;
diff --git a/uclient-http.c b/uclient-http.c
index c25e52f..38e7761 100644
--- a/uclient-http.c
+++ b/uclient-http.c
@@ -689,8 +689,12 @@  static void __uclient_notify_read(struct uclient_http *uh)
 	if (uh->eof)
 		return;
 
-	if (uh->state == HTTP_STATE_RECV_DATA && uc->cb->data_read)
-		uc->cb->data_read(uc);
+	if (uh->state == HTTP_STATE_RECV_DATA)
+		/* Now it's uclient user turn to read some data */
+		uloop_timeout_cancel(&uc->connection_timeout);
+
+		if (uc->cb->data_read)
+			uc->cb->data_read(uc);
 }
 
 static void __uclient_notify_write(struct uclient_http *uh)
@@ -1030,6 +1034,10 @@  uclient_http_read(struct uclient *cl, char *buf, unsigned int len)
 
 	uclient_notify_eof(uh);
 
+	/* Now that we consumed something and if this isn't EOF, start timer again */
+	if (!uh->uc.eof && !cl->connection_timeout.pending)
+		uloop_timeout_set(&cl->connection_timeout, cl->timeout_msecs);
+
 	return len;
 }
 
diff --git a/uclient.c b/uclient.c
index ab2d5b6..d599763 100644
--- a/uclient.c
+++ b/uclient.c
@@ -141,6 +141,16 @@  free:
 	return NULL;
 }
 
+static void uclient_connection_timeout(struct uloop_timeout *timeout)
+{
+	struct uclient *cl = container_of(timeout, struct uclient, connection_timeout);
+
+	if (cl->backend->disconnect)
+		cl->backend->disconnect(cl);
+
+	uclient_backend_set_error(cl, UCLIENT_ERROR_TIMEDOUT);
+}
+
 struct uclient *uclient_new(const char *url_str, const char *auth_str, const struct uclient_cb *cb)
 {
 	struct uclient *cl;
@@ -157,6 +167,8 @@  struct uclient *uclient_new(const char *url_str, const char *auth_str, const str
 	cl->backend = url->backend;
 	cl->cb = cb;
 	cl->url = url;
+	cl->timeout_msecs = UCLIENT_DEFAULT_TIMEOUT_MS;
+	cl->connection_timeout.cb = uclient_connection_timeout;
 
 	return cl;
 }
@@ -182,6 +194,16 @@  int uclient_set_url(struct uclient *cl, const char *url_str, const char *auth_st
 	return 0;
 }
 
+int uclient_set_timeout(struct uclient *cl, int msecs)
+{
+	if (msecs <= 0)
+		return -EINVAL;
+
+	cl->timeout_msecs = msecs;
+
+	return 0;
+}
+
 int uclient_connect(struct uclient *cl)
 {
 	return cl->backend->connect(cl);
@@ -209,10 +231,18 @@  int uclient_write(struct uclient *cl, char *buf, int len)
 
 int uclient_request(struct uclient *cl)
 {
+	int err;
+
 	if (!cl->backend->request)
 		return -1;
 
-	return cl->backend->request(cl);
+	err = cl->backend->request(cl);
+	if (err)
+		return err;
+
+	uloop_timeout_set(&cl->connection_timeout, cl->timeout_msecs);
+
+	return 0;
 }
 
 int uclient_read(struct uclient *cl, char *buf, int len)
@@ -225,6 +255,8 @@  int uclient_read(struct uclient *cl, char *buf, int len)
 
 void uclient_disconnect(struct uclient *cl)
 {
+	uloop_timeout_cancel(&cl->connection_timeout);
+
 	if (!cl->backend->disconnect)
 		return;
 
@@ -252,6 +284,7 @@  void __hidden uclient_backend_set_error(struct uclient *cl, int code)
 	if (cl->error_code)
 		return;
 
+	uloop_timeout_cancel(&cl->connection_timeout);
 	cl->error_code = code;
 	uclient_backend_change_state(cl);
 }
@@ -261,6 +294,7 @@  void __hidden uclient_backend_set_eof(struct uclient *cl)
 	if (cl->eof || cl->error_code)
 		return;
 
+	uloop_timeout_cancel(&cl->connection_timeout);
 	cl->eof = true;
 	uclient_backend_change_state(cl);
 }
diff --git a/uclient.h b/uclient.h
index d5a0d5b..5904a38 100644
--- a/uclient.h
+++ b/uclient.h
@@ -24,12 +24,15 @@ 
 #include <libubox/ustream.h>
 #include <libubox/ustream-ssl.h>
 
+#define UCLIENT_DEFAULT_TIMEOUT_MS			30000
+
 struct uclient_cb;
 struct uclient_backend;
 
 enum uclient_error_code {
 	UCLIENT_ERROR_UNKNOWN,
 	UCLIENT_ERROR_CONNECT,
+	UCLIENT_ERROR_TIMEDOUT,
 	UCLIENT_ERROR_SSL_INVALID_CERT,
 	UCLIENT_ERROR_SSL_CN_MISMATCH,
 	UCLIENT_ERROR_MISSING_SSL_CONTEXT,
@@ -59,6 +62,7 @@  struct uclient {
 	union uclient_addr local_addr, remote_addr;
 
 	struct uclient_url *url;
+	int timeout_msecs;
 	void *priv;
 
 	bool eof;
@@ -67,6 +71,7 @@  struct uclient {
 	int status_code;
 	struct blob_attr *meta;
 
+	struct uloop_timeout connection_timeout;
 	struct uloop_timeout timeout;
 };
 
@@ -82,6 +87,21 @@  struct uclient *uclient_new(const char *url, const char *auth_str, const struct
 void uclient_free(struct uclient *cl);
 
 int uclient_set_url(struct uclient *cl, const char *url, const char *auth);
+
+/**
+ * Sets connection timeout.
+ *
+ * Provided timeout value will be used for:
+ * 1) Receiving HTTP response
+ * 2) Receiving data
+ *
+ * In case of timeout uclient will use error callback with
+ * UCLIENT_ERROR_TIMEDOUT code.
+ *
+ * @param msecs timeout in milliseconds
+ */
+int uclient_set_timeout(struct uclient *cl, int msecs);
+
 int uclient_connect(struct uclient *cl);
 void uclient_disconnect(struct uclient *cl);