diff options
author | Marcel Holtmann <marcel@holtmann.org> | 2010-08-01 16:41:44 -0700 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2010-08-01 16:41:44 -0700 |
commit | 21277c943191120d84839713caefb762ca918016 (patch) | |
tree | b36261385fa44b3433b5b0ae51c0e328ba4675da | |
parent | 2509163e47302bef6afe494b11720d510760a0ed (diff) | |
download | connman-21277c943191120d84839713caefb762ca918016.tar.gz connman-21277c943191120d84839713caefb762ca918016.tar.bz2 connman-21277c943191120d84839713caefb762ca918016.zip |
Add some initial support for HTTP to web service library
-rw-r--r-- | gweb/gweb.c | 244 | ||||
-rw-r--r-- | gweb/gweb.h | 15 |
2 files changed, 257 insertions, 2 deletions
diff --git a/gweb/gweb.c b/gweb/gweb.c index 2dda02b7..31a41dcf 100644 --- a/gweb/gweb.c +++ b/gweb/gweb.c @@ -24,13 +24,36 @@ #endif #include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdarg.h> #include <string.h> +#include <sys/socket.h> +#include <arpa/inet.h> #include "gweb.h" +struct web_session { + GWeb *web; + + char *address; + char *host; + uint16_t port; + unsigned long flags; + + GIOChannel *transport_channel; + guint transport_watch; +}; + struct _GWeb { gint ref_count; + guint next_query_id; + + int index; + GList *session_list; + GWebDebugFunc debug_func; gpointer debug_data; }; @@ -51,16 +74,52 @@ static inline void debug(GWeb *web, const char *format, ...) va_end(ap); } -GWeb *g_web_new(void) +static void free_session(struct web_session *session) +{ + if (session == NULL) + return; + + if (session->transport_watch > 0) + g_source_remove(session->transport_watch); + + if (session->transport_channel != NULL) + g_io_channel_unref(session->transport_channel); + + g_free(session->host); + g_free(session->address); + g_free(session); +} + +static void flush_sessions(GWeb *web) +{ + GList *list; + + for (list = g_list_first(web->session_list); + list; list = g_list_next(list)) + free_session(list->data); + + g_list_free(web->session_list); + web->session_list = NULL; +} + +GWeb *g_web_new(int index) { GWeb *web; + if (index < 0) + return NULL; + web = g_try_new0(GWeb, 1); if (web == NULL) return NULL; web->ref_count = 1; + web->next_query_id = 1; + + web->index = index; + web->session_list = NULL; + return web; } @@ -82,6 +141,8 @@ void g_web_unref(GWeb *web) if (g_atomic_int_dec_and_test(&web->ref_count) == FALSE) return; + flush_sessions(web); + g_free(web); } @@ -93,3 +154,184 @@ void g_web_set_debug(GWeb *web, GWebDebugFunc func, gpointer user_data) web->debug_func = func; web->debug_data = user_data; } + +static gboolean received_data(GIOChannel *channel, GIOCondition cond, + gpointer user_data) +{ + struct web_session *session = user_data; + unsigned char buf[4096]; + int sk, len; + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + session->transport_watch = 0; + return FALSE; + } + + sk = g_io_channel_unix_get_fd(session->transport_channel); + + memset(buf, 0, sizeof(buf)); + len = recv(sk, buf, sizeof(buf) - 1, 0); + + printf("%s", buf); + + return TRUE; +} + +static int connect_session_transport(struct web_session *session) +{ + struct sockaddr_in sin; + int sk; + + sk = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sk < 0) + return -EIO; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + 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; + } + + session->transport_channel = g_io_channel_unix_new(sk); + if (session->transport_channel == NULL) { + close(sk); + return -ENOMEM; + } + + g_io_channel_set_close_on_unref(session->transport_channel, TRUE); + + session->transport_watch = g_io_add_watch(session->transport_channel, + G_IO_IN, received_data, + session); + + return 0; +} + +static void start_request(struct web_session *session, const char *request) +{ + GString *buf; + char *str; + ssize_t len; + int sk; + + debug(session->web, "request %s from %s", request, session->host); + + sk = g_io_channel_unix_get_fd(session->transport_channel); + + buf = g_string_new(NULL); + g_string_append_printf(buf, "GET %s HTTP/1.1\r\n", request); + g_string_append_printf(buf, "Host: %s\r\n", session->host); + g_string_append_printf(buf, "User-Agent: ConnMan/%s\r\n", VERSION); + g_string_append(buf, "Accept: */*\r\n"); + g_string_append(buf, "\r\n"); + str = g_string_free(buf, FALSE); + + len = send(sk, str, strlen(str), 0); + + printf("%s", str); + + g_free(str); +} + +static char *parse_url(struct web_session *session, const char *url) +{ + char *scheme, *host, *port, *path, *request; + + scheme = g_strdup(url); + if (scheme == NULL) + return NULL; + + host = strstr(scheme, "://"); + if (host != NULL) { + *host = '\0'; + host += 3; + + if (strcasecmp(scheme, "https") == 0) + session->port = 443; + else if (strcasecmp(scheme, "http") == 0) + session->port = 80; + else { + g_free(scheme); + return NULL; + } + } else { + host = scheme; + session->port = 80; + } + + path = strchr(host, '/'); + if (path != NULL) + *(path++) = '\0'; + + request = g_strdup_printf("/%s", path ? path : ""); + + port = strrchr(host, ':'); + if (port != NULL) { + char *end; + int tmp = strtol(port + 1, &end, 10); + + if (*end == '\0') { + *port = '\0'; + session->port = tmp; + } + } + + session->host = g_strdup(host); + + g_free(scheme); + + return request; +} + +guint g_web_request(GWeb *web, GWebMethod method, const char *url, + GWebResultFunc func, gpointer user_data) +{ + struct web_session *session; + char *request; + + if (web == NULL || url == NULL) + return 0; + + debug(web, "request %s", url); + + session = g_try_new0(struct web_session, 1); + if (session == NULL) + return 0; + + request = parse_url(session, url); + if (request == NULL) { + g_free(session); + return 0; + } + + if (inet_aton(session->host, NULL) == 0) { + g_free(session); + return 0; + } + + session->address = g_strdup(session->host); + + debug(web, "host %s:%u", session->host, session->port); + + if (connect_session_transport(session) < 0) { + g_free(request); + free_session(session); + return FALSE; + } + + session->web = web; + + web->session_list = g_list_append(web->session_list, session); + + debug(web, "creating session %s:%u", session->address, session->port); + + start_request(session, request); + + g_free(request); + + return web->next_query_id++; +} diff --git a/gweb/gweb.h b/gweb/gweb.h index c0814310..0b997d5b 100644 --- a/gweb/gweb.h +++ b/gweb/gweb.h @@ -22,6 +22,8 @@ #ifndef __G_WEB_H #define __G_WEB_H +#include <stdint.h> + #include <glib.h> #ifdef __cplusplus @@ -32,15 +34,26 @@ struct _GWeb; typedef struct _GWeb GWeb; +typedef enum { + G_WEB_METHOD_GET, +} GWebMethod; + +typedef void (*GWebResultFunc)(uint16_t status, gpointer user_data); + typedef void (*GWebDebugFunc)(const char *str, gpointer user_data); -GWeb *g_web_new(void); +GWeb *g_web_new(int index); GWeb *g_web_ref(GWeb *web); void g_web_unref(GWeb *web); void g_web_set_debug(GWeb *web, GWebDebugFunc func, gpointer user_data); +guint g_web_request(GWeb *web, GWebMethod method, const char *url, + GWebResultFunc func, gpointer user_data); + +gboolean g_web_cancel(GWeb *web, guint id); + #ifdef __cplusplus } #endif |