http: Add reference counting to the session object.
authorWerner Koch <wk@gnupg.org>
Mon, 5 May 2014 14:06:42 +0000 (16:06 +0200)
committerWerner Koch <wk@gnupg.org>
Mon, 5 May 2014 14:06:42 +0000 (16:06 +0200)
* common/http.c (http_session_t): Add field "refcount".
(_my_socket_new, _my_socket_ref, _my_socket_unref): Add debug code.
(send_request, my_npth_read, my_npth_write): Use SOCK object for the
transport ptr.
(http_session_release): Factor all code out to ...
(session_unref): here.  Deref SOCK.
(http_session_new): Init refcount and transport ptr.
(http_session_ref): New.  Ref and unref all assignments.
--

Having the reference counted session objects makes it easier for the
application to pass around only an estream.  Without that the
application would need to implement an es_onclose machinery for the
session object.

common/http.c
common/http.h
common/t-http.c

index 590893d..eb95dcb 100644 (file)
@@ -222,6 +222,7 @@ typedef struct cookie_s *cookie_t;
 /* The session object. */
 struct http_session_s
 {
+  int refcount;    /* Number of references to this object.  */
 #ifdef HTTP_USE_GNUTLS
   gnutls_certificate_credentials_t certcred;
   gnutls_session_t tls_session;
@@ -231,9 +232,7 @@ struct http_session_s
     unsigned int status; /* Verification status.  */
   } verify;
   char *servername; /* Malloced server name.  */
-#else
-  int dummy;
-#endif
+#endif /*HTTP_USE_GNUTLS*/
 };
 
 
@@ -327,7 +326,7 @@ init_sockets (void)
 /* Create a new socket object.  Returns NULL and closes FD if not
    enough memory is available.  */
 static my_socket_t
-my_socket_new (int fd)
+_my_socket_new (int lnr, int fd)
 {
   my_socket_t so;
 
@@ -341,34 +340,39 @@ my_socket_new (int fd)
     }
   so->fd = fd;
   so->refcount = 1;
-  /* log_debug ("my_socket_new(%d): object %p for fd %d created\n", */
+  /* log_debug ("http.c:socket_new(%d): object %p for fd %d created\n", */
   /*            lnr, so, so->fd); */
+  (void)lnr;
   return so;
 }
-/* #define my_socket_new(a) _my_socket_new ((a),__LINE__) */
+#define my_socket_new(a) _my_socket_new (__LINE__, (a))
 
 /* Bump up the reference counter for the socket object SO.  */
 static my_socket_t
-my_socket_ref (my_socket_t so)
+_my_socket_ref (int lnr, my_socket_t so)
 {
   so->refcount++;
-  /* log_debug ("my_socket_ref(%d): object %p for fd %d refcount now %d\n", */
+  /* log_debug ("http.c:socket_ref(%d) object %p for fd %d refcount now %d\n", */
   /*            lnr, so, so->fd, so->refcount); */
+  (void)lnr;
   return so;
 }
-/* #define my_socket_ref(a) _my_socket_ref ((a),__LINE__) */
+#define my_socket_ref(a) _my_socket_ref (__LINE__,(a))
+
 
 /* Bump down the reference counter for the socket object SO.  If SO
    has no more references, close the socket and release the
    object.  */
 static void
-my_socket_unref (my_socket_t so, void (*preclose)(void*), void *preclosearg)
+_my_socket_unref (int lnr, my_socket_t so,
+                  void (*preclose)(void*), void *preclosearg)
 {
   if (so)
     {
       so->refcount--;
-      /* log_debug ("my_socket_unref(%d): object %p for fd %d ref now %d\n", */
+      /* log_debug ("http.c:socket_unref(%d): object %p for fd %d ref now %d\n", */
       /*            lnr, so, so->fd, so->refcount); */
+      (void)lnr;
       if (!so->refcount)
         {
           if (preclose)
@@ -378,19 +382,21 @@ my_socket_unref (my_socket_t so, void (*preclose)(void*), void *preclosearg)
         }
     }
 }
-/* #define my_socket_unref(a) _my_socket_unref ((a),__LINE__) */
+#define my_socket_unref(a,b,c) _my_socket_unref (__LINE__,(a),(b),(c))
 
 
 #if defined (USE_NPTH) && defined(HTTP_USE_GNUTLS)
 static ssize_t
 my_npth_read (gnutls_transport_ptr_t ptr, void *buffer, size_t size)
 {
-  return npth_read ((int)(unsigned long)ptr, buffer, size);
+  my_socket_t sock = ptr;
+  return npth_read (sock->fd, buffer, size);
 }
 static ssize_t
 my_npth_write (gnutls_transport_ptr_t ptr, const void *buffer, size_t size)
 {
-  return npth_write ((int)(unsigned long)ptr, buffer, size);
+  my_socket_t sock = ptr;
+  return npth_write (sock->fd, buffer, size);
 }
 #endif /*USE_NPTH && HTTP_USE_GNUTLS*/
 
@@ -498,6 +504,44 @@ http_register_tls_ca (const char *fname)
 }
 
 
+/* Release a session.  Take care not to release it while it is being
+   used by a http context object.  */
+static void
+session_unref (int lnr, http_session_t sess)
+{
+  if (!sess)
+    return;
+
+  sess->refcount--;
+  /* log_debug ("http.c:session_unref(%d): sess %p ref now %d\n", */
+  /*            lnr, sess, sess->refcount); */
+  (void)lnr;
+  if (sess->refcount)
+    return;
+
+#ifdef HTTP_USE_GNUTLS
+  if (sess->tls_session)
+    {
+      my_socket_t sock = gnutls_transport_get_ptr (sess->tls_session);
+      my_socket_unref (sock, NULL, NULL);
+      gnutls_deinit (sess->tls_session);
+    }
+  if (sess->certcred)
+    gnutls_certificate_free_credentials (sess->certcred);
+  xfree (sess->servername);
+#endif /*HTTP_USE_GNUTLS*/
+
+  xfree (sess);
+}
+#define http_session_unref(a) session_unref (__LINE__, (a))
+
+void
+http_session_release (http_session_t sess)
+{
+  http_session_unref (sess);
+}
+
+
 /* Create a new session object which is currently used to enable TLS
    support.  It may eventually allow reusing existing connections.  */
 gpg_error_t
@@ -511,6 +555,7 @@ http_session_new (http_session_t *r_session, const char *tls_priority)
   sess = xtrycalloc (1, sizeof *sess);
   if (!sess)
     return gpg_error_from_syserror ();
+  sess->refcount = 1;
 
 #ifdef HTTP_USE_GNUTLS
   {
@@ -544,6 +589,10 @@ http_session_new (http_session_t *r_session, const char *tls_priority)
         err = gpg_error (GPG_ERR_GENERAL);
         goto leave;
       }
+    /* A new session has the transport ptr set to (void*(-1), we need
+       it to be NULL.  */
+    gnutls_transport_set_ptr (sess->tls_session, NULL);
+
     rc = gnutls_priority_set_direct (sess->tls_session,
                                      tls_priority? tls_priority : "NORMAL",
                                      &errpos);
@@ -569,13 +618,14 @@ http_session_new (http_session_t *r_session, const char *tls_priority)
   (void)tls_priority;
 #endif /*!HTTP_USE_GNUTLS*/
 
+  /* log_debug ("http.c:session_new: sess %p created\n", sess); */
   err = 0;
 
 #ifdef HTTP_USE_GNUTLS
  leave:
 #endif /*HTTP_USE_GNUTLS*/
   if (err)
-    http_session_release (sess);
+    http_session_unref (sess);
   else
     *r_session = sess;
 
@@ -583,23 +633,13 @@ http_session_new (http_session_t *r_session, const char *tls_priority)
 }
 
 
-/* Release a session.  Take care not to release it while it is beeing
-   used by a http contect object.  */
-void
-http_session_release (http_session_t sess)
+/* Increment the reference count for session SESS.  */
+http_session_t
+http_session_ref (http_session_t sess)
 {
-  if (!sess)
-    return;
-
-#ifdef HTTP_USE_GNUTLS
-  if (sess->tls_session)
-    gnutls_deinit (sess->tls_session);
-  if (sess->certcred)
-    gnutls_certificate_free_credentials (sess->certcred);
-  xfree (sess->servername);
-#endif /*HTTP_USE_GNUTLS*/
-
-  xfree (sess);
+  sess->refcount++;
+  /* log_debug ("http.c:session_ref: sess %p ref now %d\n", sess, sess->refcount); */
+  return sess;
 }
 
 
@@ -625,7 +665,7 @@ http_open (http_t *r_hd, http_req_t reqtype, const char *url,
     return gpg_error_from_syserror ();
   hd->req_type = reqtype;
   hd->flags = flags;
-  hd->session = session;
+  hd->session = http_session_ref (session);
 
   err = parse_uri (&hd->uri, url, 0, !!(flags & HTTP_FLAG_FORCE_TLS));
   if (!err)
@@ -638,6 +678,7 @@ http_open (http_t *r_hd, http_req_t reqtype, const char *url,
         es_fclose (hd->fp_read);
       if (hd->fp_write)
         es_fclose (hd->fp_write);
+      http_session_unref (hd->session);
       xfree (hd);
     }
   else
@@ -791,7 +832,7 @@ http_wait_response (http_t hd)
   if (!cookie)
     return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
   cookie->sock = my_socket_ref (hd->sock);
-  cookie->session = hd->session;
+  cookie->session = http_session_ref (hd->session);
   cookie->use_tls = hd->uri->use_tls;
 
   hd->read_cookie = cookie;
@@ -800,6 +841,7 @@ http_wait_response (http_t hd)
     {
       err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
       my_socket_unref (cookie->sock, NULL, NULL);
+      http_session_unref (cookie->session);
       xfree (cookie);
       hd->read_cookie = NULL;
       return err;
@@ -857,6 +899,7 @@ http_close (http_t hd, int keep_read_stream)
     es_fclose (hd->fp_read);
   if (hd->fp_write)
     es_fclose (hd->fp_write);
+  http_session_unref (hd->session);
   http_release_parsed_uri (hd->uri);
   while (hd->headers)
     {
@@ -1431,12 +1474,7 @@ send_request (http_t hd, const char *auth,
       int rc;
 
       my_socket_ref (hd->sock);
-#if GNUTLS_VERSION_NUMBER >= 0x030109
-      gnutls_transport_set_int (hd->session->tls_session, hd->sock->fd);
-#else
-      gnutls_transport_set_ptr (hd->session->tls_session,
-                                (gnutls_transport_ptr_t)(hd->sock->fd));
-#endif
+      gnutls_transport_set_ptr (hd->session->tls_session, hd->sock);
 #ifdef USE_NPTH
       gnutls_transport_set_pull_function (hd->session->tls_session,
                                           my_npth_read);
@@ -1561,7 +1599,7 @@ send_request (http_t hd, const char *auth,
     cookie->sock = my_socket_ref (hd->sock);
     hd->write_cookie = cookie;
     cookie->use_tls = hd->uri->use_tls;
-    cookie->session = hd->session;
+    cookie->session = http_session_ref (hd->session);
 
     hd->fp_write = es_fopencookie (cookie, "w", cookie_functions);
     if (!hd->fp_write)
@@ -2382,6 +2420,8 @@ cookie_close (void *cookie)
     if (c->sock)
       my_socket_unref (c->sock, NULL, NULL);
 
+  if (c->session)
+    http_session_unref (c->session);
   xfree (c);
   return 0;
 }
index fa73b6e..b6471b6 100644 (file)
@@ -95,6 +95,7 @@ void http_register_tls_ca (const char *fname);
 
 gpg_error_t http_session_new (http_session_t *r_session,
                               const char *tls_priority);
+http_session_t http_session_ref (http_session_t sess);
 void http_session_release (http_session_t sess);
 
 
index 0191e85..7b9c744 100644 (file)
@@ -144,6 +144,7 @@ main (int argc, char **argv)
   int c;
   unsigned int my_http_flags = 0;
   int no_out = 0;
+  int tls_dbg = 0;
   const char *cafile = NULL;
   http_session_t session = NULL;
 
@@ -163,12 +164,13 @@ main (int argc, char **argv)
         {
           fputs ("usage: " PGM " URL\n"
                  "Options:\n"
-                 "  --verbose       print timings etc.\n"
-                 "  --debug         flyswatter\n"
-                 "  --cacert FNAME  expect CA certificate in file FNAME\n"
-                 "  --no-verify     do not verify the certificate\n"
-                 "  --force-tls     use HTTP_FLAG_FORCE_TLS\n"
-                 "  --no-out        do not print the content\n",
+                 "  --verbose         print timings etc.\n"
+                 "  --debug           flyswatter\n"
+                 "  --gnutls-debug N  use GNUTLS debug level N\n"
+                 "  --cacert FNAME    expect CA certificate in file FNAME\n"
+                 "  --no-verify       do not verify the certificate\n"
+                 "  --force-tls       use HTTP_FLAG_FORCE_TLS\n"
+                 "  --no-out          do not print the content\n",
                  stdout);
           exit (0);
         }
@@ -183,6 +185,15 @@ main (int argc, char **argv)
           debug++;
           argc--; argv++;
         }
+      else if (!strcmp (*argv, "--gnutls-debug"))
+        {
+          argc--; argv++;
+          if (argc)
+            {
+              tls_dbg = atoi (*argv);
+              argc--; argv++;
+            }
+        }
       else if (!strcmp (*argv, "--cacert"))
         {
           argc--; argv++;
@@ -248,7 +259,8 @@ main (int argc, char **argv)
   /* gnutls_certificate_set_dh_params (certcred, dh_params); */
 
   gnutls_global_set_log_function (my_gnutls_log);
-  /* gnutls_global_set_log_level (2); */
+  if (tls_dbg)
+    gnutls_global_set_log_level (tls_dbg);
 
 #endif /*HTTP_USE_GNUTLS*/