support for connection timeout
authorRafał Miłecki <zajec5@gmail.com>
Fri, 16 Jan 2015 10:21:02 +0000 (11:21 +0100)
committerFelix Fietkau <nbd@openwrt.org>
Sun, 18 Jan 2015 00:41:26 +0000 (01:41 +0100)
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
uclient-fetch.c
uclient-http.c
uclient.c
uclient.h

index 22f15c6feb801cdafe05b811ce076e56d22f9ea4..0617a02fea0bcfc711e7920b552befa52f39b6ae 100644 (file)
@@ -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;
index c25e52f1b9439be9b7649fe2bb99228fa143e60a..af43b0508c323c26367a65e6cd1321763ad7ec02 100644 (file)
@@ -689,8 +689,13 @@ 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 +1035,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;
 }
 
index ab2d5b6c393fa6c14d03693ae76f5dbe392767f2..d5997633273b83ee4b3c96f51592dc6be81476bb 100644 (file)
--- 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);
 }
index d5a0d5b4f804dd68523c4993921fe50edc8d1da3..5904a38dbc97e537dd371a8e25d1cd9a39a39068 100644 (file)
--- a/uclient.h
+++ b/uclient.h
 #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);