http: Add reference counting to the session object.

* 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.
This commit is contained in:
Werner Koch 2014-05-05 16:06:42 +02:00
parent 0e59195642
commit ea0f5481f0
3 changed files with 100 additions and 47 deletions

View File

@ -222,6 +222,7 @@ typedef struct cookie_s *cookie_t;
/* The session object. */ /* The session object. */
struct http_session_s struct http_session_s
{ {
int refcount; /* Number of references to this object. */
#ifdef HTTP_USE_GNUTLS #ifdef HTTP_USE_GNUTLS
gnutls_certificate_credentials_t certcred; gnutls_certificate_credentials_t certcred;
gnutls_session_t tls_session; gnutls_session_t tls_session;
@ -231,9 +232,7 @@ struct http_session_s
unsigned int status; /* Verification status. */ unsigned int status; /* Verification status. */
} verify; } verify;
char *servername; /* Malloced server name. */ char *servername; /* Malloced server name. */
#else #endif /*HTTP_USE_GNUTLS*/
int dummy;
#endif
}; };
@ -327,7 +326,7 @@ init_sockets (void)
/* Create a new socket object. Returns NULL and closes FD if not /* Create a new socket object. Returns NULL and closes FD if not
enough memory is available. */ enough memory is available. */
static my_socket_t static my_socket_t
my_socket_new (int fd) _my_socket_new (int lnr, int fd)
{ {
my_socket_t so; my_socket_t so;
@ -341,34 +340,39 @@ my_socket_new (int fd)
} }
so->fd = fd; so->fd = fd;
so->refcount = 1; 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); */ /* lnr, so, so->fd); */
(void)lnr;
return so; 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. */ /* Bump up the reference counter for the socket object SO. */
static my_socket_t static my_socket_t
my_socket_ref (my_socket_t so) _my_socket_ref (int lnr, my_socket_t so)
{ {
so->refcount++; 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); */ /* lnr, so, so->fd, so->refcount); */
(void)lnr;
return so; 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 /* Bump down the reference counter for the socket object SO. If SO
has no more references, close the socket and release the has no more references, close the socket and release the
object. */ object. */
static void 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) if (so)
{ {
so->refcount--; 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); */ /* lnr, so, so->fd, so->refcount); */
(void)lnr;
if (!so->refcount) if (!so->refcount)
{ {
if (preclose) 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) #if defined (USE_NPTH) && defined(HTTP_USE_GNUTLS)
static ssize_t static ssize_t
my_npth_read (gnutls_transport_ptr_t ptr, void *buffer, size_t size) 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 static ssize_t
my_npth_write (gnutls_transport_ptr_t ptr, const void *buffer, size_t size) 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*/ #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 /* Create a new session object which is currently used to enable TLS
support. It may eventually allow reusing existing connections. */ support. It may eventually allow reusing existing connections. */
gpg_error_t gpg_error_t
@ -511,6 +555,7 @@ http_session_new (http_session_t *r_session, const char *tls_priority)
sess = xtrycalloc (1, sizeof *sess); sess = xtrycalloc (1, sizeof *sess);
if (!sess) if (!sess)
return gpg_error_from_syserror (); return gpg_error_from_syserror ();
sess->refcount = 1;
#ifdef HTTP_USE_GNUTLS #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); err = gpg_error (GPG_ERR_GENERAL);
goto leave; 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, rc = gnutls_priority_set_direct (sess->tls_session,
tls_priority? tls_priority : "NORMAL", tls_priority? tls_priority : "NORMAL",
&errpos); &errpos);
@ -569,13 +618,14 @@ http_session_new (http_session_t *r_session, const char *tls_priority)
(void)tls_priority; (void)tls_priority;
#endif /*!HTTP_USE_GNUTLS*/ #endif /*!HTTP_USE_GNUTLS*/
/* log_debug ("http.c:session_new: sess %p created\n", sess); */
err = 0; err = 0;
#ifdef HTTP_USE_GNUTLS #ifdef HTTP_USE_GNUTLS
leave: leave:
#endif /*HTTP_USE_GNUTLS*/ #endif /*HTTP_USE_GNUTLS*/
if (err) if (err)
http_session_release (sess); http_session_unref (sess);
else else
*r_session = sess; *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 /* Increment the reference count for session SESS. */
used by a http contect object. */ http_session_t
void http_session_ref (http_session_t sess)
http_session_release (http_session_t sess)
{ {
if (!sess) sess->refcount++;
return; /* log_debug ("http.c:session_ref: sess %p ref now %d\n", sess, sess->refcount); */
return sess;
#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);
} }
@ -625,7 +665,7 @@ http_open (http_t *r_hd, http_req_t reqtype, const char *url,
return gpg_error_from_syserror (); return gpg_error_from_syserror ();
hd->req_type = reqtype; hd->req_type = reqtype;
hd->flags = flags; hd->flags = flags;
hd->session = session; hd->session = http_session_ref (session);
err = parse_uri (&hd->uri, url, 0, !!(flags & HTTP_FLAG_FORCE_TLS)); err = parse_uri (&hd->uri, url, 0, !!(flags & HTTP_FLAG_FORCE_TLS));
if (!err) if (!err)
@ -638,6 +678,7 @@ http_open (http_t *r_hd, http_req_t reqtype, const char *url,
es_fclose (hd->fp_read); es_fclose (hd->fp_read);
if (hd->fp_write) if (hd->fp_write)
es_fclose (hd->fp_write); es_fclose (hd->fp_write);
http_session_unref (hd->session);
xfree (hd); xfree (hd);
} }
else else
@ -791,7 +832,7 @@ http_wait_response (http_t hd)
if (!cookie) if (!cookie)
return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
cookie->sock = my_socket_ref (hd->sock); 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; cookie->use_tls = hd->uri->use_tls;
hd->read_cookie = cookie; 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 ()); err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
my_socket_unref (cookie->sock, NULL, NULL); my_socket_unref (cookie->sock, NULL, NULL);
http_session_unref (cookie->session);
xfree (cookie); xfree (cookie);
hd->read_cookie = NULL; hd->read_cookie = NULL;
return err; return err;
@ -857,6 +899,7 @@ http_close (http_t hd, int keep_read_stream)
es_fclose (hd->fp_read); es_fclose (hd->fp_read);
if (hd->fp_write) if (hd->fp_write)
es_fclose (hd->fp_write); es_fclose (hd->fp_write);
http_session_unref (hd->session);
http_release_parsed_uri (hd->uri); http_release_parsed_uri (hd->uri);
while (hd->headers) while (hd->headers)
{ {
@ -1431,12 +1474,7 @@ send_request (http_t hd, const char *auth,
int rc; int rc;
my_socket_ref (hd->sock); my_socket_ref (hd->sock);
#if GNUTLS_VERSION_NUMBER >= 0x030109 gnutls_transport_set_ptr (hd->session->tls_session, hd->sock);
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
#ifdef USE_NPTH #ifdef USE_NPTH
gnutls_transport_set_pull_function (hd->session->tls_session, gnutls_transport_set_pull_function (hd->session->tls_session,
my_npth_read); my_npth_read);
@ -1561,7 +1599,7 @@ send_request (http_t hd, const char *auth,
cookie->sock = my_socket_ref (hd->sock); cookie->sock = my_socket_ref (hd->sock);
hd->write_cookie = cookie; hd->write_cookie = cookie;
cookie->use_tls = hd->uri->use_tls; 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); hd->fp_write = es_fopencookie (cookie, "w", cookie_functions);
if (!hd->fp_write) if (!hd->fp_write)
@ -2382,6 +2420,8 @@ cookie_close (void *cookie)
if (c->sock) if (c->sock)
my_socket_unref (c->sock, NULL, NULL); my_socket_unref (c->sock, NULL, NULL);
if (c->session)
http_session_unref (c->session);
xfree (c); xfree (c);
return 0; return 0;
} }

View File

@ -95,6 +95,7 @@ void http_register_tls_ca (const char *fname);
gpg_error_t http_session_new (http_session_t *r_session, gpg_error_t http_session_new (http_session_t *r_session,
const char *tls_priority); const char *tls_priority);
http_session_t http_session_ref (http_session_t sess);
void http_session_release (http_session_t sess); void http_session_release (http_session_t sess);

View File

@ -144,6 +144,7 @@ main (int argc, char **argv)
int c; int c;
unsigned int my_http_flags = 0; unsigned int my_http_flags = 0;
int no_out = 0; int no_out = 0;
int tls_dbg = 0;
const char *cafile = NULL; const char *cafile = NULL;
http_session_t session = NULL; http_session_t session = NULL;
@ -163,12 +164,13 @@ main (int argc, char **argv)
{ {
fputs ("usage: " PGM " URL\n" fputs ("usage: " PGM " URL\n"
"Options:\n" "Options:\n"
" --verbose print timings etc.\n" " --verbose print timings etc.\n"
" --debug flyswatter\n" " --debug flyswatter\n"
" --cacert FNAME expect CA certificate in file FNAME\n" " --gnutls-debug N use GNUTLS debug level N\n"
" --no-verify do not verify the certificate\n" " --cacert FNAME expect CA certificate in file FNAME\n"
" --force-tls use HTTP_FLAG_FORCE_TLS\n" " --no-verify do not verify the certificate\n"
" --no-out do not print the content\n", " --force-tls use HTTP_FLAG_FORCE_TLS\n"
" --no-out do not print the content\n",
stdout); stdout);
exit (0); exit (0);
} }
@ -183,6 +185,15 @@ main (int argc, char **argv)
debug++; debug++;
argc--; argv++; argc--; argv++;
} }
else if (!strcmp (*argv, "--gnutls-debug"))
{
argc--; argv++;
if (argc)
{
tls_dbg = atoi (*argv);
argc--; argv++;
}
}
else if (!strcmp (*argv, "--cacert")) else if (!strcmp (*argv, "--cacert"))
{ {
argc--; argv++; argc--; argv++;
@ -248,7 +259,8 @@ main (int argc, char **argv)
/* gnutls_certificate_set_dh_params (certcred, dh_params); */ /* gnutls_certificate_set_dh_params (certcred, dh_params); */
gnutls_global_set_log_function (my_gnutls_log); 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*/ #endif /*HTTP_USE_GNUTLS*/