mirror of
git://git.gnupg.org/gnupg.git
synced 2025-04-17 15:44:34 +02:00
Support to read response headers. Required for redirection support in the
dirmngr package.
This commit is contained in:
parent
529a543bb7
commit
92550e3016
@ -1,3 +1,10 @@
|
|||||||
|
2006-09-04 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
|
* http.c (http_get_header): New.
|
||||||
|
(capitalize_header_name, store_header): New.
|
||||||
|
(parse_response): Store headers away.
|
||||||
|
* http.h: New flag HTTP_FLAG_NEED_HEADER.
|
||||||
|
|
||||||
2006-08-21 Werner Koch <wk@g10code.com>
|
2006-08-21 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
* Makefile.am (libcommon_a_SOURCES): Added keyserver.h
|
* Makefile.am (libcommon_a_SOURCES): Added keyserver.h
|
||||||
|
180
common/http.c
180
common/http.c
@ -65,6 +65,10 @@ typedef gnutls_session gnutls_session_t;
|
|||||||
typedef gnutls_transport_ptr gnutls_transport_ptr_t;
|
typedef gnutls_transport_ptr gnutls_transport_ptr_t;
|
||||||
#endif /*HTTP_USE_GNUTLS*/
|
#endif /*HTTP_USE_GNUTLS*/
|
||||||
|
|
||||||
|
#ifdef TEST
|
||||||
|
#undef USE_DNS_SRV
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
#ifdef USE_DNS_SRV
|
#ifdef USE_DNS_SRV
|
||||||
@ -157,6 +161,17 @@ typedef struct cookie_s *cookie_t;
|
|||||||
static gpg_error_t (*tls_callback) (http_t, gnutls_session_t, int);
|
static gpg_error_t (*tls_callback) (http_t, gnutls_session_t, int);
|
||||||
#endif /*HTTP_USE_GNUTLS*/
|
#endif /*HTTP_USE_GNUTLS*/
|
||||||
|
|
||||||
|
|
||||||
|
/* An object to save header lines. */
|
||||||
|
struct header_s
|
||||||
|
{
|
||||||
|
struct header_s *next;
|
||||||
|
char *value; /* The value of the header (malloced). */
|
||||||
|
char name[1]; /* The name of the header (canonicalized). */
|
||||||
|
};
|
||||||
|
typedef struct header_s *header_t;
|
||||||
|
|
||||||
|
|
||||||
/* Our handle context. */
|
/* Our handle context. */
|
||||||
struct http_context_s
|
struct http_context_s
|
||||||
{
|
{
|
||||||
@ -178,6 +193,7 @@ struct http_context_s
|
|||||||
char *buffer; /* Line buffer. */
|
char *buffer; /* Line buffer. */
|
||||||
size_t buffer_size;
|
size_t buffer_size;
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
|
header_t headers; /* Received headers. */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -444,6 +460,13 @@ http_close (http_t hd, int keep_read_stream)
|
|||||||
if (hd->fp_write)
|
if (hd->fp_write)
|
||||||
P_ES(fclose) (hd->fp_write);
|
P_ES(fclose) (hd->fp_write);
|
||||||
http_release_parsed_uri (hd->uri);
|
http_release_parsed_uri (hd->uri);
|
||||||
|
while (hd->headers)
|
||||||
|
{
|
||||||
|
header_t tmp = hd->headers->next;
|
||||||
|
xfree (hd->headers->value);
|
||||||
|
xfree (hd->headers);
|
||||||
|
hd->headers = tmp;
|
||||||
|
}
|
||||||
xfree (hd->buffer);
|
xfree (hd->buffer);
|
||||||
xfree (hd);
|
xfree (hd);
|
||||||
}
|
}
|
||||||
@ -1160,6 +1183,129 @@ my_read_line (
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Transform a header name into a standard capitalized format; e.g.
|
||||||
|
"Content-Type". Conversion stops at the colon. As usual we don't
|
||||||
|
use the localized versions of ctype.h. */
|
||||||
|
static void
|
||||||
|
capitalize_header_name (char *name)
|
||||||
|
{
|
||||||
|
int first = 1;
|
||||||
|
|
||||||
|
for (; *name && *name != ':'; name++)
|
||||||
|
{
|
||||||
|
if (*name == '-')
|
||||||
|
first = 1;
|
||||||
|
else if (first)
|
||||||
|
{
|
||||||
|
if (*name >= 'a' && *name <= 'z')
|
||||||
|
*name = *name - 'a' + 'A';
|
||||||
|
first = 0;
|
||||||
|
}
|
||||||
|
else if (*name >= 'A' && *name <= 'Z')
|
||||||
|
*name = *name - 'A' + 'a';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Store an HTTP header line in LINE away. Line continuation is
|
||||||
|
supported as well as merging of headers with the same name. This
|
||||||
|
function may modify LINE. */
|
||||||
|
static gpg_error_t
|
||||||
|
store_header (http_t hd, char *line)
|
||||||
|
{
|
||||||
|
size_t n;
|
||||||
|
char *p, *value;
|
||||||
|
header_t h;
|
||||||
|
|
||||||
|
n = strlen (line);
|
||||||
|
if (n && line[n-1] == '\n')
|
||||||
|
{
|
||||||
|
line[--n] = 0;
|
||||||
|
if (n && line[n-1] == '\r')
|
||||||
|
line[--n] = 0;
|
||||||
|
}
|
||||||
|
if (!n) /* we are never called to hit this. */
|
||||||
|
return gpg_error (GPG_ERR_BUG);
|
||||||
|
if (*line == ' ' || *line == '\t')
|
||||||
|
{
|
||||||
|
/* Continuation. This won't happen too often as it is not
|
||||||
|
recommended. We use a straightforward implementaion. */
|
||||||
|
if (!hd->headers)
|
||||||
|
return gpg_error (GPG_ERR_PROTOCOL_VIOLATION);
|
||||||
|
n += strlen (hd->headers->value);
|
||||||
|
p = xtrymalloc (n+1);
|
||||||
|
if (!p)
|
||||||
|
return gpg_error_from_errno (errno);
|
||||||
|
strcpy (stpcpy (p, hd->headers->value), line);
|
||||||
|
xfree (hd->headers->value);
|
||||||
|
hd->headers->value = p;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
capitalize_header_name (line);
|
||||||
|
p = strchr (line, ':');
|
||||||
|
if (!p)
|
||||||
|
return gpg_error (GPG_ERR_PROTOCOL_VIOLATION);
|
||||||
|
*p++ = 0;
|
||||||
|
while (*p == ' ' || *p == '\t')
|
||||||
|
p++;
|
||||||
|
value = p;
|
||||||
|
|
||||||
|
for (h=hd->headers; h; h = h->next)
|
||||||
|
if ( !strcmp (h->name, line) )
|
||||||
|
break;
|
||||||
|
if (h)
|
||||||
|
{
|
||||||
|
/* We have already seen a line with that name. Thus we assume
|
||||||
|
it is a comma separated list and merge them. */
|
||||||
|
p = xtrymalloc (strlen (h->value) + 1 + strlen (value)+ 1);
|
||||||
|
if (!p)
|
||||||
|
return gpg_error_from_errno (errno);
|
||||||
|
strcpy (stpcpy (stpcpy (p, h->value), ","), value);
|
||||||
|
xfree (h->value);
|
||||||
|
h->value = p;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Append a new header. */
|
||||||
|
h = xtrymalloc (sizeof *h + strlen (line));
|
||||||
|
if (!h)
|
||||||
|
return gpg_error_from_errno (errno);
|
||||||
|
strcpy (h->name, line);
|
||||||
|
h->value = xtrymalloc (strlen (value)+1);
|
||||||
|
if (!h->value)
|
||||||
|
{
|
||||||
|
xfree (h);
|
||||||
|
return gpg_error_from_errno (errno);
|
||||||
|
}
|
||||||
|
strcpy (h->value, value);
|
||||||
|
h->next = hd->headers;
|
||||||
|
hd->headers = h;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return the header NAME from the last response. The returned value
|
||||||
|
is valid as along as HD has not been closed and no othe request has
|
||||||
|
been send. If the header was not found, NULL is returned. Name
|
||||||
|
must be canonicalized, that is the first letter of each dash
|
||||||
|
delimited part must be uppercase and all other letters lowercase.
|
||||||
|
Note that the context must have been opened with the
|
||||||
|
HTTP_FLAG_NEED_HEADER. */
|
||||||
|
const char *
|
||||||
|
http_get_header (http_t hd, const char *name)
|
||||||
|
{
|
||||||
|
header_t h;
|
||||||
|
|
||||||
|
for (h=hd->headers; h; h = h->next)
|
||||||
|
if ( !strcmp (h->name, name) )
|
||||||
|
return h->value;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse the response from a server.
|
* Parse the response from a server.
|
||||||
* Returns: Errorcode and sets some files in the handle
|
* Returns: Errorcode and sets some files in the handle
|
||||||
@ -1170,6 +1316,15 @@ parse_response (http_t hd)
|
|||||||
char *line, *p, *p2;
|
char *line, *p, *p2;
|
||||||
size_t maxlen, len;
|
size_t maxlen, len;
|
||||||
|
|
||||||
|
/* Delete old header lines. */
|
||||||
|
while (hd->headers)
|
||||||
|
{
|
||||||
|
header_t tmp = hd->headers->next;
|
||||||
|
xfree (hd->headers->value);
|
||||||
|
xfree (hd->headers);
|
||||||
|
hd->headers = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
/* Wait for the status line. */
|
/* Wait for the status line. */
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
@ -1231,6 +1386,12 @@ parse_response (http_t hd)
|
|||||||
if ( (hd->flags & HTTP_FLAG_LOG_RESP) )
|
if ( (hd->flags & HTTP_FLAG_LOG_RESP) )
|
||||||
log_info ("RESP: `%.*s'\n",
|
log_info ("RESP: `%.*s'\n",
|
||||||
(int)strlen(line)-(*line&&line[1]?2:0),line);
|
(int)strlen(line)-(*line&&line[1]?2:0),line);
|
||||||
|
if ( (hd->flags & HTTP_FLAG_NEED_HEADER) && *line )
|
||||||
|
{
|
||||||
|
gpg_error_t err = store_header (hd, line);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
while (len && *line);
|
while (len && *line);
|
||||||
|
|
||||||
@ -1703,6 +1864,7 @@ main (int argc, char **argv)
|
|||||||
gnutls_certificate_credentials certcred;
|
gnutls_certificate_credentials certcred;
|
||||||
const int certprio[] = { GNUTLS_CRT_X509, 0 };
|
const int certprio[] = { GNUTLS_CRT_X509, 0 };
|
||||||
#endif /*HTTP_USE_GNUTLS*/
|
#endif /*HTTP_USE_GNUTLS*/
|
||||||
|
header_t hdr;
|
||||||
|
|
||||||
#ifdef HTTP_USE_ESTREAM
|
#ifdef HTTP_USE_ESTREAM
|
||||||
es_init ();
|
es_init ();
|
||||||
@ -1792,7 +1954,8 @@ main (int argc, char **argv)
|
|||||||
http_release_parsed_uri (uri);
|
http_release_parsed_uri (uri);
|
||||||
uri = NULL;
|
uri = NULL;
|
||||||
|
|
||||||
rc = http_open_document (&hd, *argv, NULL, HTTP_FLAG_NO_SHUTDOWN,
|
rc = http_open_document (&hd, *argv, NULL,
|
||||||
|
HTTP_FLAG_NO_SHUTDOWN | HTTP_FLAG_NEED_HEADER,
|
||||||
NULL, tls_session);
|
NULL, tls_session);
|
||||||
if (rc)
|
if (rc)
|
||||||
{
|
{
|
||||||
@ -1801,8 +1964,19 @@ main (int argc, char **argv)
|
|||||||
}
|
}
|
||||||
log_info ("open_http_document succeeded; status=%u\n",
|
log_info ("open_http_document succeeded; status=%u\n",
|
||||||
http_get_status_code (hd));
|
http_get_status_code (hd));
|
||||||
while ((c = P_ES(getc) (http_get_read_ptr (hd))) != EOF)
|
for (hdr = hd->headers; hdr; hdr = hdr->next)
|
||||||
putchar (c);
|
printf ("HDR: %s: %s\n", hdr->name, hdr->value);
|
||||||
|
switch (http_get_status_code (hd))
|
||||||
|
{
|
||||||
|
case 200:
|
||||||
|
while ((c = P_ES(getc) (http_get_read_ptr (hd))) != EOF)
|
||||||
|
putchar (c);
|
||||||
|
break;
|
||||||
|
case 301:
|
||||||
|
case 302:
|
||||||
|
printf ("Redirected to `%s'\n", http_get_header (hd, "Location"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
http_close (hd, 0);
|
http_close (hd, 0);
|
||||||
|
|
||||||
#ifdef HTTP_USE_GNUTLS
|
#ifdef HTTP_USE_GNUTLS
|
||||||
|
@ -66,7 +66,8 @@ enum
|
|||||||
HTTP_FLAG_TRY_PROXY = 1,
|
HTTP_FLAG_TRY_PROXY = 1,
|
||||||
HTTP_FLAG_NO_SHUTDOWN = 2,
|
HTTP_FLAG_NO_SHUTDOWN = 2,
|
||||||
HTTP_FLAG_TRY_SRV = 4,
|
HTTP_FLAG_TRY_SRV = 4,
|
||||||
HTTP_FLAG_LOG_RESP = 8
|
HTTP_FLAG_LOG_RESP = 8,
|
||||||
|
HTTP_FLAG_NEED_HEADER = 16
|
||||||
};
|
};
|
||||||
|
|
||||||
struct http_context_s;
|
struct http_context_s;
|
||||||
@ -106,6 +107,7 @@ FILE *http_get_read_ptr (http_t hd);
|
|||||||
FILE *http_get_write_ptr (http_t hd);
|
FILE *http_get_write_ptr (http_t hd);
|
||||||
#endif /*!HTTP_USE_ESTREAM*/
|
#endif /*!HTTP_USE_ESTREAM*/
|
||||||
unsigned int http_get_status_code (http_t hd);
|
unsigned int http_get_status_code (http_t hd);
|
||||||
|
const char *http_get_header (http_t hd, const char *name);
|
||||||
|
|
||||||
char *http_escape_string (const char *string, const char *specials);
|
char *http_escape_string (const char *string, const char *specials);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user