Hi Mohamed,

> Allow user to send chunked HTTP post request by calling the
> input callback function on write until the user
> indicate no more data then we send the 0 length chunk to
> indicate end of request body.
> Now we use a new watch for sending data. This watch is only
> active if more data need to be send.
> ---
>  gweb/gweb.c |  231 
> ++++++++++++++++++++++++++++++++++++++++-------------------
>  1 files changed, 158 insertions(+), 73 deletions(-)
> 
> diff --git a/gweb/gweb.c b/gweb/gweb.c
> index 295b93f..cc75f3b 100644
> --- a/gweb/gweb.c
> +++ b/gweb/gweb.c
> @@ -58,6 +58,7 @@ struct web_session {
>  
>       GIOChannel *transport_channel;
>       guint transport_watch;
> +     guint send_watch;
>  
>       guint resolv_action;
>       char *request;
> @@ -66,6 +67,8 @@ struct web_session {
>       gsize receive_space;
>       GString *current_header;
>       gboolean header_done;
> +     gboolean more_data;
> +     gboolean request_started;
>  
>       GWebResult result;
>  
> @@ -122,6 +125,9 @@ static void free_session(struct web_session *session)
>       if (session->transport_watch > 0)
>               g_source_remove(session->transport_watch);
>  
> +     if (session->send_watch > 0)
> +             g_source_remove(session->send_watch);
> +
>       if (session->transport_channel != NULL)
>               g_io_channel_unref(session->transport_channel);
>  
> @@ -313,6 +319,144 @@ static inline void call_result_func(struct web_session 
> *session, guint16 status)
>       session->result_func(&session->result, session->user_data);
>  }
>  
> +static void get_pending_input_data(struct web_session *session)
> +{
> +     GString *buf;
> +     gchar *str;
> +     const guint8 *body;
> +     gsize length;
> +     gsize count, bytes_written;
> +     GIOStatus status;
> +
> +     if (session->input_func != NULL)
> +             session->more_data = session->input_func(&body, &length,
> +                                                     session->user_data);
> +     else {
> +             session->more_data = FALSE;
> +             return;
> +     }

so this should be written like this:

        if (session->input_func == NULL) {
                session->more_data = FALSE;
                return;
        }

        session->more_data = session->input_func(...

This way it is a lot simple to read. General rule is always to the error
check and then leave that function.

> +     buf = g_string_new(NULL);
> +
> +     if (length > 0) {
> +             g_string_append_printf(buf, "%zx\r\n", length);
> +             g_string_append_len(buf, (char *) body, length);
> +             g_string_append(buf, "\r\n");
> +     }

Add an empty line here.

> +     if (session->more_data == FALSE)
> +             g_string_append(buf, "0\r\n\r\n");
> +
> +     str = g_string_free(buf, FALSE);
> +     count = buf->len;

You can't do this. buf is not valid anymore. You need to do the
assignment before you free buf.

> +     if (count > 0) {
> +             status = g_io_channel_write_chars(session->transport_channel,
> +                                     str, count, &bytes_written, NULL);
> +
> +             debug(session->web, "status %u bytes written %zu",
> +                                             status, bytes_written);
> +     }
> +
> +     g_free(str);
> +}

And coming to read through this function, then it is clearly only for
the chunked encoding to. So calling it process_next_chunk() would be a
little bit clearer.

> +static void start_request(struct web_session *session)
> +{
> +     GString *buf;
> +     gchar *str;
> +     const guint8 *body;
> +     gsize length;
> +     gsize count, bytes_written;
> +     GIOStatus status;
> +
> +     debug(session->web, "request %s from %s",
> +                                     session->request, session->host);
> +
> +     buf = g_string_new(NULL);
> +
> +     if (session->content_type == NULL)
> +             g_string_append_printf(buf, "GET %s HTTP/1.1\r\n",
> +                                                     session->request);
> +     else
> +             g_string_append_printf(buf, "POST %s HTTP/1.1\r\n",
> +                                                     session->request);
> +     g_string_append_printf(buf, "Host: %s\r\n", session->host);
> +     if (session->web->user_agent != NULL)
> +             g_string_append_printf(buf, "User-Agent: %s\r\n",
> +                                             session->web->user_agent);
> +     if (session->web->accept_option != NULL)
> +             g_string_append_printf(buf, "Accept: %s\r\n",
> +                                             session->web->accept_option);
> +     if (session->content_type != NULL) {
> +             g_string_append_printf(buf, "Content-Type: %s\r\n",
> +                                                     session->content_type);
> +             if (session->input_func != NULL)
> +                     session->more_data = session->input_func(&body, &length,
> +                                                     session->user_data);
> +             else {
> +                     session->more_data = FALSE;
> +                     length = 0;
> +             }

I prefer to have the { } coming first. So turn this around and use
input_func == NULL.

> +             if (session->more_data == FALSE)
> +                     g_string_append_printf(buf, "Content-Length: %zu\r\n",
> +                                                                     length);
> +             else
> +                     g_string_append(buf, "Transfer-Encoding: chunked\r\n");
> +     }
> +     if (session->web->close_connection == TRUE)
> +             g_string_append(buf, "Connection: close\r\n");
> +     g_string_append(buf, "\r\n");
> +
> +     if (session->content_type != NULL && length > 0) {
> +             if (session->more_data == TRUE) {
> +                     g_string_append_printf(buf, "%zx\r\n", length);
> +                     g_string_append_len(buf, (char *) body, length);
> +                     g_string_append(buf, "\r\n");
> +             } else
> +                     g_string_append_len(buf, (char *) body, length);
> +     }
> +
> +     count = buf->len;
> +     str = g_string_free(buf, FALSE);
> +
> +     debug(session->web, "bytes to write %zu", count);
> +
> +     status = g_io_channel_write_chars(session->transport_channel,
> +                                     str, count, &bytes_written, NULL);
> +
> +     debug(session->web, "status %u bytes written %zu",
> +                                     status, bytes_written);
> +
> +     //printf("%s", str);
> +
> +     g_free(str);
> +}
> +
> +static gboolean send_data(GIOChannel *channel, GIOCondition cond,
> +                                             gpointer user_data)
> +{
> +     struct web_session *session = user_data;
> +
> +     if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
> +             session->send_watch = 0;
> +             return FALSE;
> +     }
> +
> +     if (cond & G_IO_OUT) {
> +             if (session->request_started == FALSE) {
> +                     session->request_started = TRUE;
> +                     start_request(session);
> +             } else if (session->more_data == TRUE)
> +                     get_pending_input_data(session);
> +     }

You don't really need the G_IO_OUT check here. It is your watch and the
error cases are already handled.

> +
> +     if (session->more_data == TRUE)
> +             return TRUE;
> +
> +     session->send_watch = 0;
> +
> +     return FALSE;
> +}
> +
>  static gboolean received_data(GIOChannel *channel, GIOCondition cond,
>                                                       gpointer user_data)
>  {
> @@ -398,7 +542,6 @@ static gboolean received_data(GIOChannel *channel, 
> GIOCondition cond,
>  
>               g_string_truncate(session->current_header, 0);
>       }
> -

Please don't just remove some empty lines. This is suppose to be here.

>       return TRUE;
>  }
>  
> @@ -416,11 +559,6 @@ static int connect_session_transport(struct web_session 
> *session)
>       sin.sin_port = htons(session->port);
>       sin.sin_addr.s_addr = inet_addr(session->address);
>  
> -     if (connect(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
> -             close(sk);
> -             return -EIO;
> -     }
> -
>       if (session->flags & SESSION_FLAG_USE_TLS)
>               session->transport_channel = g_io_channel_gnutls_new(sk);
>       else
> @@ -431,6 +569,8 @@ static int connect_session_transport(struct web_session 
> *session)
>               return -ENOMEM;
>       }
>  
> +     g_io_channel_set_flags(session->transport_channel,
> +                                     G_IO_FLAG_NONBLOCK, NULL);
>       g_io_channel_set_encoding(session->transport_channel, NULL, NULL);
>       g_io_channel_set_buffered(session->transport_channel, FALSE);
>  
> @@ -440,6 +580,18 @@ static int connect_session_transport(struct web_session 
> *session)
>                               G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
>                                               received_data, session);
>  
> +     session->send_watch = g_io_add_watch(session->transport_channel,
> +                             G_IO_OUT | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
> +                                             send_data, session);
> +
> +     if (connect(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
> +             if (errno == EINPROGRESS)
> +                     return 0;
> +
> +             close(sk);
> +             return -EIO;
> +     }
> +
>       return 0;

Coming to think about it, you should add the watch only after you know
that connect() succeeded.

        if (connect(....) < 0) {
                if (errno != EINPROGRESS) {
                        close(sk);
                        return -EIO;
                }
        }

        send_watch = g_io_add_watch(...

        return 0;
}

Otherwise you leak the watch here.

>  }
>  
> @@ -457,69 +609,6 @@ static int create_transport(struct web_session *session)
>       return 0;
>  }
>  
> -static void start_request(struct web_session *session)
> -{
> -     GString *buf;
> -     gchar *str;
> -     const guint8 *body;
> -     gsize length;
> -     gsize count, bytes_written;
> -     GIOStatus status;
> -
> -     debug(session->web, "request %s from %s",
> -                                     session->request, session->host);
> -
> -     buf = g_string_new(NULL);
> -
> -     if (session->content_type == NULL)
> -             g_string_append_printf(buf, "GET %s HTTP/1.1\r\n",
> -                                                     session->request);
> -     else
> -             g_string_append_printf(buf, "POST %s HTTP/1.1\r\n",
> -                                                     session->request);
> -     g_string_append_printf(buf, "Host: %s\r\n", session->host);
> -     if (session->web->user_agent != NULL)
> -             g_string_append_printf(buf, "User-Agent: %s\r\n",
> -                                             session->web->user_agent);
> -     if (session->web->accept_option != NULL)
> -             g_string_append_printf(buf, "Accept: %s\r\n",
> -                                             session->web->accept_option);
> -     if (session->content_type != NULL) {
> -             g_string_append_printf(buf, "Content-Type: %s\r\n",
> -                                             session->content_type);
> -             if (session->input_func != NULL)
> -                     session->input_func(&body, &length, session->user_data);
> -             else
> -                     length = 0;
> -
> -             g_string_append_printf(buf, "Content-Length: %zu\r\n", length);
> -     }
> -     if (session->web->close_connection == TRUE)
> -             g_string_append(buf, "Connection: close\r\n");
> -     g_string_append(buf, "\r\n");
> -
> -     count = buf->len;
> -
> -     if (session->content_type != NULL && length > 0) {
> -             g_string_append_len(buf, (char *) body, length);
> -             count += length;
> -     }
> -
> -     str = g_string_free(buf, FALSE);
> -
> -     debug(session->web, "bytes to write %zu", count);
> -
> -     status = g_io_channel_write_chars(session->transport_channel,
> -                                     str, count, &bytes_written, NULL);
> -
> -     debug(session->web, "status %u bytes written %zu",
> -                                             status, bytes_written);
> -
> -     //printf("%s", str);
> -
> -     g_free(str);
> -}
> -
>  static int parse_url(struct web_session *session, const char *url)
>  {
>       char *scheme, *host, *port, *path;
> @@ -594,8 +683,6 @@ static void resolv_result(GResolvResultStatus status,
>               call_result_func(session, 409);
>               return;
>       }
> -
> -     start_request(session);
>  }
>  
>  static guint do_request(GWeb *web, const char *url,
> @@ -657,8 +744,6 @@ static guint do_request(GWeb *web, const char *url,
>                       free_session(session);
>                       return 0;
>               }
> -
> -             start_request(session);
>       }
>  
>       web->session_list = g_list_append(web->session_list, session);

Otherwise it looks fine to me.

Regards

Marcel


_______________________________________________
connman mailing list
[email protected]
http://lists.connman.net/listinfo/connman

Reply via email to