[PATCH] [RFC] Replace own http client with libcurl

Paul P Komkoff Jr <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Organization Department of Fish & Wildlife
Message-ID <[email protected]>
I've put some time adding curl support to wapbox.
Basically, I was not satisfied with gwlib http client, for example,
it's inability to work with ssl tunnel thru http proxy in my setup.

Also I think it will be better to not reinvent bicycle and let libcurl
handle urls.

So, this code is preliminary, just for testing. Btw, it works in my
setup here.

Enjoy.


--- gateway/aclocal.m4	2003-09-26 18:25:47.000000000 +0400
+++ gw/aclocal.m4	2004-02-09 09:01:37.000000000 +0300
@@ -159,3 +159,37 @@
 fi
 ])
 
+dnl MY_CURL
+dnl -------
+dnl set my_cv_curl_vers to the version of libcurl or NONE
+dnl if libcurl is not found or is too old
+ 
+AC_DEFUN(MY_CURL,[
+ AC_CACHE_VAL(my_cv_curl_vers,[
+ my_cv_curl_vers=NONE
+ dnl check is the plain-text version of the required version
+ check="7.9.7"
+ dnl check_hex must be UPPERCASE if any hex letters are present
+ check_hex="070907"
+ 
+ AC_MSG_CHECKING([for curl >= $check])
+ 
+ if eval curl-config --version 2>/dev/null >/dev/null; then
+   ver=`curl-config --version | sed -e "s/libcurl //g"`
+   hex_ver=`curl-config --vernum | tr 'a-f' 'A-F'`
+   ok=`echo "ibase=16; if($hex_ver>=$check_hex) $hex_ver else 0" | bc`
+ 
+   if test x$ok != x0; then
+     my_cv_curl_vers="$ver"
+     AC_MSG_RESULT([$my_cv_curl_vers])
+   else
+     AC_MSG_RESULT(FAILED)
+     AC_MSG_WARN([$ver is too old. Need version $check or higher.])
+   fi
+ else
+   AC_MSG_RESULT(FAILED)
+   AC_MSG_WARN([curl-config was not found])
+ fi
+ ])
+])
+
--- gateway/configure.in	2004-01-20 21:51:14.000000000 +0300
+++ gw/configure.in	2004-02-09 09:08:40.901905990 +0300
@@ -130,8 +130,8 @@
 dnl Checks for library functions.
 
 AC_CHECK_FUNCS(gettimeofday select socket strdup getopt_long localtime_r gmtime_r backtrace srandom)
-AC_CHECK_FUNC(getopt, , LIBOBJS="$LIBOBJS utils/attgetopt.o")
-AC_SUBST(LIBOBJS)
+AC_CHECK_FUNC(getopt, , AC_LIBOBJ="utils/attgetopt")
+dnl AC_SUBST(LIBOBJS)
 
 dnl Check if we have reentrant gethostbyname and which one
 AC_CHECK_FUNC(gethostbyname_r, [ AC_FUNC_WHICH_GETHOSTBYNAME_R ], [
@@ -139,6 +139,10 @@
     AC_MSG_ERROR([Couldnot find gethostbyname_r nor gethostbyname functions])])]
 )
 
+MY_CURL
+LIBS="$LIBS $(curl-config --libs)"
+CFLAGS="$CFLAGS $(curl-config --cflags)"
+
 dnl Extra feature checks
 
 dnl GW_HAVE_TYPE_FROM(HDRNAME, TYPE, HAVENAME, DESCRIPTION)
--- gateway/gw/wapbox.c	2004-01-22 17:08:24.000000000 +0300
+++ gw/gw/wapbox.c	2004-02-05 12:26:38.000000000 +0300
@@ -66,6 +66,8 @@
 #include <unistd.h>
 #include <signal.h>
 
+#include <curl/curl.h>
+
 #include "gwlib/gwlib.h"
 #include "shared.h"
 #include "wml_compiler.h"
@@ -671,6 +673,8 @@
     cf_index = get_and_set_debugs(argc, argv, NULL);
     
     setup_signal_handlers();
+
+    curl_global_init(CURL_GLOBAL_ALL);
     
     if (argv[cf_index] == NULL)
         config_filename = octstr_create("kannel.conf");
@@ -824,6 +828,8 @@
 
     gwlib_shutdown();
 
+    curl_global_cleanup();
+
     /* now really restart */
     if (restart)
         execvp(argv[0], argv);
--- gateway/gw/wap-appl.c	2004-01-22 17:08:24.000000000 +0300
+++ gw/gw/wap-appl.c	2004-02-08 22:08:18.000000000 +0300
@@ -79,6 +79,7 @@
  */
 
 #include <string.h>
+#include <curl/curl.h>
 
 #include "gwlib/gwlib.h"
 #include "wmlscript/ws.h"
@@ -165,6 +166,150 @@
 extern int wsp_smart_errors;
 extern Octstr *device_home;
 
+static CURLM* CurlMClient = NULL;
+static int pipeh[2] = { 0 };
+
+struct curl_http_request {
+    Octstr * body;
+    void * id;
+
+    Octstr* reply;
+    List* headers;
+
+    struct curl_slist * request_headers;
+    Octstr * url;
+};
+
+static size_t curl_writefunc(void *ptr, size_t size, size_t nmemb, void *stream) {
+    Octstr * s = stream;
+    int realsize = size * nmemb;
+
+    if (s == NULL) return realsize;
+    octstr_append_data(s, ptr, realsize);
+    return realsize;
+}
+
+static size_t curl_headfunc(void *ptr, size_t size, size_t nmemb, void *stream) {
+    List * l = stream;
+    int realsize = size * nmemb;
+    Octstr * s;
+
+    if (l == NULL) return realsize;
+    s = octstr_create_from_data(ptr, realsize);
+    octstr_strip_crlfs(s);
+    if ((octstr_len(s) == 0)  || (octstr_search_char(s, ':', 0) == -1)) {
+      octstr_destroy(s);
+    } else {
+      list_append(l, s);
+    }
+    return realsize;
+}
+
+void curl_start_request(int method, Octstr *url,
+                        List *headers, Octstr *body, int follow, void *id) {
+    CURL* req = NULL;
+    struct curl_http_request * bfd = gw_malloc(sizeof(struct curl_http_request));
+
+    int i = 0;
+
+    req = curl_easy_init();
+    bfd->url = octstr_duplicate(url);
+    bfd->request_headers = NULL;
+    bfd->body = NULL;
+    curl_easy_setopt(req, CURLOPT_URL, octstr_get_cstr(bfd->url));
+    if (method == HTTP_METHOD_POST) {
+      curl_easy_setopt(req, CURLOPT_POST, 1);
+      bfd->body = octstr_duplicate(body);
+      curl_easy_setopt(req, CURLOPT_POSTFIELDS, octstr_get_cstr(bfd->body));
+      curl_easy_setopt(req, CURLOPT_POSTFIELDSIZE, octstr_len(bfd->body));
+    }
+    if (method == HTTP_METHOD_HEAD)
+      curl_easy_setopt(req, CURLOPT_NOBODY, 1);
+    curl_easy_setopt(req, CURLOPT_SSL_VERIFYPEER, 0);
+    curl_easy_setopt(req, CURLOPT_NOPROGRESS, 1);
+    curl_easy_setopt(req, CURLOPT_MUTE, 1);
+    curl_easy_setopt(req, CURLOPT_NOSIGNAL, 1);
+    curl_easy_setopt(req, CURLOPT_ENCODING, "");
+    for (i = 0; i < list_len(headers); i++) {
+      bfd->request_headers = curl_slist_append(bfd->request_headers, octstr_get_cstr(list_get(headers, i)));
+    }
+
+    curl_easy_setopt(req, CURLOPT_HTTPHEADER, bfd->request_headers);
+
+    bfd->id = id;
+    bfd->reply = octstr_create("");
+    bfd->headers = list_create();
+
+    curl_easy_setopt(req, CURLOPT_PRIVATE, bfd);
+
+    curl_easy_setopt(req, CURLOPT_WRITEFUNCTION, curl_writefunc);
+    curl_easy_setopt(req, CURLOPT_HEADERFUNCTION, curl_headfunc);
+    curl_easy_setopt(req, CURLOPT_WRITEDATA, bfd->reply);
+    curl_easy_setopt(req, CURLOPT_WRITEHEADER, bfd->headers);
+
+    curl_multi_add_handle(CurlMClient, req);
+    write(pipeh[1], "X", 1);
+}
+
+void *curl_receive_result(int *status, Octstr **final_url,
+                         List **headers, Octstr **body) {
+    fd_set f_read, f_write, f_exc;
+    int maxfd = 0;
+    int transfers = 0, msgs = 0;
+    char x;
+    struct curl_http_request * bfd;
+    void * p;
+    char* furl;
+    CURLMsg * msg;
+    CURL* hnd;
+    int result = 0;
+
+    while (1) {
+      while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(CurlMClient, &transfers));
+      while (msg = curl_multi_info_read(CurlMClient, &msgs)) {
+        hnd = msg->easy_handle;
+        result = msg->data.result;
+        curl_multi_remove_handle(CurlMClient, hnd);
+        if (CURLE_OK != curl_easy_getinfo(hnd, CURLINFO_PRIVATE, &bfd)) bfd = NULL;
+        if (bfd == NULL) continue;
+        if (CURLE_OK != curl_easy_getinfo(hnd, CURLINFO_HTTP_CODE, status)) (*status) = -1;
+        if (result != CURLE_OK) (*status) = -1;
+
+        (*headers) = bfd->headers;
+        (*body) = bfd->reply;
+        if (CURLE_OK == curl_easy_getinfo(hnd, CURLINFO_EFFECTIVE_URL, &furl))
+          (*final_url) = octstr_create(furl);
+        else (*final_url) = NULL;
+        p = bfd->id;
+        curl_easy_cleanup(hnd);
+        if (bfd->request_headers) curl_slist_free_all(bfd->request_headers);
+        octstr_destroy(bfd->url);
+        octstr_destroy(bfd->body);
+        gw_free(bfd);
+        return p;
+      }
+
+      FD_ZERO(&f_read);
+      FD_ZERO(&f_write);
+      FD_ZERO(&f_exc);
+      
+      curl_multi_fdset(CurlMClient, &f_read, &f_write, &f_exc, &maxfd);
+      if (maxfd < pipeh[0]) maxfd = pipeh[0];
+      FD_SET(pipeh[0], &f_read);
+      if (select(maxfd + 1, &f_read, &f_write, &f_exc, NULL) >= 0) {
+        if (FD_ISSET(pipeh[0], &f_read)) {
+          if (read(pipeh[0], &x, 1) == 1) {
+            continue;
+          } else {
+            return NULL;
+          }
+        }
+      } else return NULL;
+    }
+}
+
+
+
 /*
  * Defines if PPG is running in wapbox instance
  */
@@ -244,6 +389,8 @@
 void wap_appl_init(Cfg *cfg) 
 {
     gw_assert(run_status == limbo);
+    pipe(pipeh);
+    CurlMClient = curl_multi_init();
     queue = list_create();
     fetches = counter_create();
     list_add_producer(queue);
@@ -267,6 +414,7 @@
     
     list_remove_producer(queue);
     gwthread_join_every(main_thread);
+    close(pipeh[1]);
     
     http_caller_signal_shutdown(caller);
     gwthread_join_every(return_replies_thread);
@@ -765,6 +913,7 @@
          ua ? octstr_get_cstr(ua) : "",
          server ? octstr_get_cstr(server) : "");
 
+
     octstr_destroy(ua);
     octstr_destroy(server);
     
@@ -1061,7 +1210,7 @@
 
     while (run_status == running) {
 
-        p = http_receive_result(caller, &status, &final_url, &headers, &body);
+        p = curl_receive_result(&status, &final_url, &headers, &body);
         if (p == NULL)
             break;
 
@@ -1262,8 +1411,8 @@
         p->request_headers = actual_headers;
 
         /* issue the request to the HTTP server */
-        http_start_request(caller, http_name2method(method), url, actual_headers, 
-                           request_body, 0, p, NULL);
+        curl_start_request(http_name2method(method), url, actual_headers, 
+                           request_body, 0, p);
 
         octstr_destroy(request_body);
     } 
-- 
Paul P 'Stingray' Komkoff Jr // http://stingr.net/key <- my pgp key
 This message represents the official view of the voices in my head
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.