X-Git-Url: http://git.indexdata.com/?p=yaz-moved-to-github.git;a=blobdiff_plain;f=src%2Fhttp.c;h=0f17df533b598e5f505330ec4795afbb1d04e0ef;hp=3d62fcd2ee8683da9d56c729f08bd58f8409231c;hb=d606d0d7c6015414a1d5ba065d09b507679be5a3;hpb=3576736bac485d8f04b2fc4f6b5cfcef7576d741 diff --git a/src/http.c b/src/http.c index 3d62fcd..0f17df5 100644 --- a/src/http.c +++ b/src/http.c @@ -1,129 +1,72 @@ -/* - * Copyright (C) 1995-2007, Index Data ApS +/* This file is part of the YAZ toolkit. + * Copyright (C) Index Data * See the file LICENSE for details. - * - * $Id: http.c,v 1.4 2007-11-30 11:44:47 adam Exp $ */ - /** * \file http.c * \brief Implements HTTP decoding */ +#if HAVE_CONFIG_H +#include +#endif -#include -#include +#include "odr-priv.h" #include #include +#include #include - -#ifdef WIN32 -#define strncasecmp _strnicmp -#define strcasecmp _stricmp -#endif - - -/* - * This function's counterpart, yaz_base64decode(), is in srwutil.c. - * I feel bad that they're not together, but each function is only - * needed in one place, and those places are not together. Maybe one - * day we'll move them into a new httputil.c, and declare them in a - * corresponding httputil.h - */ -static void yaz_base64encode(const char *in, char *out) -{ - static char encoding[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - unsigned char buf[3]; - long n; - - while (*in != 0) { - char *pad = 0; - buf[0] = in[0]; - buf[1] = in[1]; - if (in[1] == 0) { - buf[2] = 0; - pad = "=="; - } else { - buf[2] = in[2]; - if (in[2] == 0) - pad = "="; - } - - /* Treat three eight-bit numbers as on 24-bit number */ - n = (buf[0] << 16) + (buf[1] << 8) + buf[2]; - - /* Write the six-bit chunks out as four encoded characters */ - *out++ = encoding[(n >> 18) & 63]; - *out++ = encoding[(n >> 12) & 63]; - if (in[1] != 0) - *out++ = encoding[(n >> 6) & 63]; - if (in[1] != 0 && in[2] != 0) - *out++ = encoding[n & 63]; - - if (pad != 0) { - while (*pad != 0) - *out++ = *pad++; - break; - } - in += 3; - } - - *out++ = 0; -} - +#include +#include static int decode_headers_content(ODR o, int off, Z_HTTP_Header **headers, char **content_buf, int *content_len) { int i = off; int chunked = 0; + const char *buf = o->op->buf; + int size = o->op->size; *headers = 0; - while (i < o->size-1 && o->buf[i] == '\n') + while (i < size-1 && buf[i] == '\n') { int po; i++; - if (o->buf[i] == '\r' && i < o->size-1 && o->buf[i+1] == '\n') + if (buf[i] == '\r' && i < size-1 && buf[i+1] == '\n') { i++; break; } - if (o->buf[i] == '\n') + if (buf[i] == '\n') break; for (po = i; ; i++) { - if (i == o->size) + if (i == size) { o->error = OHTTP; return 0; } - else if (o->buf[i] == ':') + else if (buf[i] == ':') break; } *headers = (Z_HTTP_Header *) odr_malloc(o, sizeof(**headers)); - (*headers)->name = (char*) odr_malloc(o, i - po + 1); - memcpy ((*headers)->name, o->buf + po, i - po); - (*headers)->name[i - po] = '\0'; + (*headers)->name = odr_strdupn(o, buf + po, i - po); i++; - while (i < o->size-1 && o->buf[i] == ' ') + while (i < size-1 && buf[i] == ' ') i++; - for (po = i; i < o->size-1 && !strchr("\r\n", o->buf[i]); i++) + for (po = i; i < size-1 && !strchr("\r\n", buf[i]); i++) ; - - (*headers)->value = (char*) odr_malloc(o, i - po + 1); - memcpy ((*headers)->value, o->buf + po, i - po); - (*headers)->value[i - po] = '\0'; - - if (!strcasecmp((*headers)->name, "Transfer-Encoding") + + (*headers)->value = odr_strdupn(o, buf + po, i - po); + if (!yaz_strcasecmp((*headers)->name, "Transfer-Encoding") && - !strcasecmp((*headers)->value, "chunked")) + !yaz_strcasecmp((*headers)->value, "chunked")) chunked = 1; headers = &(*headers)->next; - if (i < o->size-1 && o->buf[i] == '\r') + if (i < size-1 && buf[i] == '\r') i++; } *headers = 0; - if (o->buf[i] != '\n') + if (buf[i] != '\n') { o->error = OHTTP; return 0; @@ -133,30 +76,30 @@ static int decode_headers_content(ODR o, int off, Z_HTTP_Header **headers, if (chunked) { int off = 0; - + /* we know buffer will be smaller than o->size - i*/ - *content_buf = (char*) odr_malloc(o, o->size - i); - + *content_buf = (char*) odr_malloc(o, size - i); + while (1) { /* chunk length .. */ int chunk_len = 0; - for (; i < o->size-2; i++) - if (isdigit(o->buf[i])) - chunk_len = chunk_len * 16 + - (o->buf[i] - '0'); - else if (isupper(o->buf[i])) - chunk_len = chunk_len * 16 + - (o->buf[i] - ('A'-10)); - else if (islower(o->buf[i])) - chunk_len = chunk_len * 16 + - (o->buf[i] - ('a'-10)); + for (; i < size-2; i++) + if (yaz_isdigit(buf[i])) + chunk_len = chunk_len * 16 + + (buf[i] - '0'); + else if (yaz_isupper(buf[i])) + chunk_len = chunk_len * 16 + + (buf[i] - ('A'-10)); + else if (yaz_islower(buf[i])) + chunk_len = chunk_len * 16 + + (buf[i] - ('a'-10)); else break; /* chunk extension ... */ - while (o->buf[i] != '\r' && o->buf[i+1] != '\n') + while (buf[i] != '\r' && buf[i+1] != '\n') { - if (i >= o->size-2) + if (i >= size-2) { o->error = OHTTP; return 0; @@ -166,13 +109,13 @@ static int decode_headers_content(ODR o, int off, Z_HTTP_Header **headers, i += 2; /* skip CRLF */ if (chunk_len == 0) break; - if (chunk_len < 0 || off + chunk_len > o->size) + if (chunk_len < 0 || off + chunk_len > size) { o->error = OHTTP; return 0; } /* copy chunk .. */ - memcpy (*content_buf + off, o->buf + i, chunk_len); + memcpy (*content_buf + off, buf + i, chunk_len); i += chunk_len + 2; /* skip chunk+CRLF */ off += chunk_len; } @@ -182,22 +125,20 @@ static int decode_headers_content(ODR o, int off, Z_HTTP_Header **headers, } else { - if (i > o->size) + if (i > size) { o->error = OHTTP; return 0; } - else if (i == o->size) + else if (i == size) { *content_buf = 0; *content_len = 0; } - else + else { - *content_len = o->size - i; - *content_buf = (char*) odr_malloc(o, *content_len + 1); - memcpy(*content_buf, o->buf + i, *content_len); - (*content_buf)[*content_len] = '\0'; + *content_len = size - i; + *content_buf = odr_strdupn(o, buf + i, *content_len); } } return 1; @@ -232,6 +173,8 @@ void z_HTTP_header_add_basic_auth(ODR o, Z_HTTP_Header **hp, if (username == 0) return; + if (password == 0) + password = ""; len = strlen(username) + strlen(password); tmp = (char *) odr_malloc(o, len+2); @@ -239,7 +182,7 @@ void z_HTTP_header_add_basic_auth(ODR o, Z_HTTP_Header **hp, buf = (char *) odr_malloc(o, (len+1) * 8/6 + 12); strcpy(buf, "Basic "); yaz_base64encode(tmp, &buf[strlen(buf)]); - z_HTTP_header_add(o, hp, "Authorization", buf); + z_HTTP_header_set(o, hp, "Authorization", buf); } @@ -254,10 +197,43 @@ void z_HTTP_header_add(ODR o, Z_HTTP_Header **hp, const char *n, (*hp)->next = 0; } +void z_HTTP_header_set(ODR o, Z_HTTP_Header **hp, const char *n, + const char *v) +{ + while (*hp) + { + if (!yaz_strcasecmp((*hp)->name, n)) + { + (*hp)->value = odr_strdup(o, v); + return; + } + hp = &(*hp)->next; + } + *hp = (Z_HTTP_Header *) odr_malloc(o, sizeof(**hp)); + (*hp)->name = odr_strdup(o, n); + (*hp)->value = odr_strdup(o, v); + (*hp)->next = 0; +} + +const char *z_HTTP_header_remove(Z_HTTP_Header **hp, const char *n) +{ + while (*hp) + { + if (!yaz_strcasecmp((*hp)->name, n)) + { + const char *v = (*hp)->value; + *hp = (*hp)->next; + return v; + } + hp = &(*hp)->next; + } + return 0; +} + const char *z_HTTP_header_lookup(const Z_HTTP_Header *hp, const char *n) { for (; hp; hp = hp->next) - if (!yaz_matchstr(hp->name, n)) + if (!yaz_strcasecmp(hp->name, n)) return hp->value; return 0; } @@ -305,18 +281,55 @@ Z_GDU *z_get_HTTP_Request_host_path(ODR odr, if (cp0 && cp1) { - char *h = (char*) odr_malloc(odr, cp1 - cp0 + 1); - memcpy (h, cp0, cp1 - cp0); - h[cp1-cp0] = '\0'; - z_HTTP_header_add(odr, &p->u.HTTP_Request->headers, - "Host", h); + char *h = odr_strdupn(odr, cp0, cp1 - cp0); + z_HTTP_header_add(odr, &p->u.HTTP_Request->headers, "Host", h); } } return p; } +Z_GDU *z_get_HTTP_Request_uri(ODR odr, const char *uri, const char *args, + int use_full_uri) +{ + Z_GDU *p = z_get_HTTP_Request(odr); + const char *cp0 = strstr(uri, "://"); + const char *cp1 = 0; + if (cp0) + cp0 = cp0+3; + else + cp0 = uri; + + cp1 = strchr(cp0, '/'); + if (!cp1) + cp1 = cp0+strlen(cp0); -Z_GDU *z_get_HTTP_Response(ODR o, int code) + if (cp0 && cp1) + { + char *h = odr_strdupn(odr, cp0, cp1 - cp0); + z_HTTP_header_add(odr, &p->u.HTTP_Request->headers, "Host", h); + } + + if (!args) + { + if (*cp1) + args = cp1 + 1; + else + args = ""; + } + p->u.HTTP_Request->path = odr_malloc(odr, cp1 - uri + strlen(args) + 2); + if (use_full_uri) + { + memcpy(p->u.HTTP_Request->path, uri, cp1 - uri); + strcpy(p->u.HTTP_Request->path + (cp1 - uri), "/"); + } + else + strcpy(p->u.HTTP_Request->path, "/"); + strcat(p->u.HTTP_Request->path, args); + return p; +} + +Z_GDU *z_get_HTTP_Response_server(ODR o, int code, const char *details, + const char *server, const char *server_url) { Z_GDU *p = (Z_GDU *) odr_malloc(o, sizeof(*p)); Z_HTTP_Response *hres; @@ -329,171 +342,267 @@ Z_GDU *z_get_HTTP_Response(ODR o, int code) hres->content_buf = 0; hres->code = code; hres->version = "1.1"; - z_HTTP_header_add(o, &hres->headers, "Server", - "YAZ/" YAZ_VERSION); + z_HTTP_header_add(o, &hres->headers, "Server", server); if (code != 200) { - hres->content_buf = (char*) odr_malloc(o, 400); - sprintf (hres->content_buf, - "\n" - "\n" - " \n" - " YAZ " YAZ_VERSION "\n" - " \n" - " \n" - "

YAZ " - YAZ_VERSION "

\n" - "

Error: %d

\n" - "

Description: %.50s

\n" - " \n" - "\n", - code, z_HTTP_errmsg(code)); + const char *http_err = z_HTTP_errmsg(code); + size_t sz = 400 + strlen(http_err) + (details ? + strlen(details) : 0); + hres->content_buf = (char*) odr_malloc(o, sz); + sprintf(hres->content_buf, + "\n" + "\n" + " \n" + " %s\n" + " \n" + " \n" + "

%s

\n" + "

Error: %d

\n" + "

Description: %s

\n", server, server_url, server, + code, http_err); + if (details) + { + sprintf(hres->content_buf + strlen(hres->content_buf), + "

Details: %s

\n", details); + } + sprintf(hres->content_buf + strlen(hres->content_buf), + " \n" + "\n"); hres->content_len = strlen(hres->content_buf); z_HTTP_header_add(o, &hres->headers, "Content-Type", "text/html"); } return p; } +Z_GDU *z_get_HTTP_Response_details(ODR o, int code, const char *details) +{ + return z_get_HTTP_Response_server(o, code, details, "YAZ/" YAZ_VERSION, + "http://www.indexdata.com/yaz"); +} + +Z_GDU *z_get_HTTP_Response(ODR o, int code) +{ + return z_get_HTTP_Response_details(o, code, 0); +} + const char *z_HTTP_errmsg(int code) { - if (code == 200) + switch (code) + { + case 100: + return "Continue"; + case 101: + return "Switching Protocols"; + case 200: return "OK"; - else if (code == 400) + case 201: + return "Created"; + case 202: + return "Accepted"; + case 203: + return "Non-Authoritative Information"; + case 204: + return "No Content"; + case 205: + return "Reset Content"; + case 206: + return "Partial Content"; + case 300: + return "Multiple Choices"; + case 301: + return "Moved Permenently"; + case 302: + return "Found"; + case 303: + return "See Other"; + case 304: + return "Not Modified"; + case 305: + return "Use Proxy"; + case 307: + return "Temporary Redirect"; + case 400: return "Bad Request"; - else if (code == 404) + case 404: return "Not Found"; - else if (code == 405) + case 405: return "Method Not Allowed"; - else if (code == 500) + case 406: + return "Not Acceptable"; + case 407: + return "Proxy Authentication Required"; + case 408: + return "Request Timeout"; + case 409: + return "Conflict"; + case 410: + return "Gone"; + case 411: + return "Length Required"; + case 412: + return "Precondition Failed"; + case 413: + return "Request Entity Too Large"; + case 414: + return "Request-URI Too Long"; + case 415: + return "Unsupported Media Type"; + case 416: + return "Requested Range Not Satisfiable"; + case 417: + return "Expectation Failed"; + case 500: return "Internal Error"; - else + case 501: + return "Not Implemented"; + case 502: + return "Bad Gateway"; + case 503: + return "Service Unavailable"; + case 504: + return "Gateway Timeout"; + case 505: + return "HTTP Version Not Supported"; + default: return "Unknown Error"; + } } int yaz_decode_http_response(ODR o, Z_HTTP_Response **hr_p) { int i, po; Z_HTTP_Response *hr = (Z_HTTP_Response *) odr_malloc(o, sizeof(*hr)); - + const char *buf = o->op->buf; + int size = o->op->size; + *hr_p = hr; hr->content_buf = 0; hr->content_len = 0; - + po = i = 5; - while (i < o->size-2 && !strchr(" \r\n", o->buf[i])) + while (i < size-2 && !strchr(" \r\n", buf[i])) i++; - hr->version = (char *) odr_malloc(o, i - po + 1); - if (i - po) - memcpy(hr->version, o->buf + po, i - po); - hr->version[i-po] = 0; - if (o->buf[i] != ' ') + hr->version = odr_strdupn(o, buf + po, i - po); + if (buf[i] != ' ') { o->error = OHTTP; return 0; } i++; hr->code = 0; - while (i < o->size-2 && o->buf[i] >= '0' && o->buf[i] <= '9') + while (i < size-2 && buf[i] >= '0' && buf[i] <= '9') { - hr->code = hr->code*10 + (o->buf[i] - '0'); + hr->code = hr->code*10 + (buf[i] - '0'); i++; } - while (i < o->size-1 && o->buf[i] != '\n') + while (i < size-1 && buf[i] != '\n') i++; return decode_headers_content(o, i, &hr->headers, - &hr->content_buf, &hr->content_len); + &hr->content_buf, &hr->content_len); } int yaz_decode_http_request(ODR o, Z_HTTP_Request **hr_p) { int i, po; Z_HTTP_Request *hr = (Z_HTTP_Request *) odr_malloc(o, sizeof(*hr)); - + const char *buf = o->op->buf; + int size = o->op->size; + int lspace = 0; + *hr_p = hr; - /* method .. */ - for (i = 0; o->buf[i] != ' '; i++) - if (i >= o->size-5 || i > 30) + for (i = 0; buf[i] != ' '; i++) + if (i >= size-5 || i > 30) { o->error = OHTTP; return 0; } - hr->method = (char *) odr_malloc(o, i+1); - memcpy (hr->method, o->buf, i); - hr->method[i] = '\0'; - /* path */ - po = i+1; - for (i = po; o->buf[i] != ' '; i++) - if (i >= o->size-5) - { - o->error = OHTTP; - return 0; - } - hr->path = (char *) odr_malloc(o, i - po + 1); - memcpy (hr->path, o->buf+po, i - po); - hr->path[i - po] = '\0'; - /* HTTP version */ - i++; - if (i > o->size-5 || memcmp(o->buf+i, "HTTP/", 5)) + hr->method = odr_strdupn(o, buf, i); + po = ++i; + while (i < size && !strchr("\r\n", buf[i])) + { + if (buf[i] == ' ') + lspace = i; + i++; + } + if (!lspace || i >= size || lspace >= size - 5 || + memcmp(buf + lspace + 1, "HTTP/", 5)) { o->error = OHTTP; return 0; } - i+= 5; - po = i; - while (i < o->size && !strchr("\r\n", o->buf[i])) + hr->path = odr_strdupn(o, buf + po, lspace - po); + hr->version = odr_strdupn(o, buf + lspace + 6, i - (lspace + 6)); + if (i < size-1 && buf[i] == '\r') i++; - hr->version = (char *) odr_malloc(o, i - po + 1); - memcpy(hr->version, o->buf + po, i - po); - hr->version[i - po] = '\0'; - /* headers */ - if (i < o->size-1 && o->buf[i] == '\r') - i++; - if (o->buf[i] != '\n') + if (buf[i] != '\n') { o->error = OHTTP; return 0; } + /* headers */ return decode_headers_content(o, i, &hr->headers, &hr->content_buf, &hr->content_len); } +static void dump_http_package(ODR o, const char *buf, size_t len) +{ + int i, limit = 8192; + for (i = 0; ; i++) + { + if (i == len) + { + o->op->stream_write(o, o->op->print, ODR_VISIBLESTRING, buf, i); + break; + } + else if (i >= limit) + { + o->op->stream_write(o, o->op->print, ODR_VISIBLESTRING, buf, i); + odr_printf(o, "(truncated from %ld to %d\n", (long) len, i); + break; + } + else if (buf[i] == 0) + { + o->op->stream_write(o, o->op->print, ODR_VISIBLESTRING, buf, i); + odr_printf(o, "(binary data)\n", (long) len); + break; + } + } +} + int yaz_encode_http_response(ODR o, Z_HTTP_Response *hr) { char sbuf[80]; Z_HTTP_Header *h; - int top0 = o->top; + int top0 = o->op->top; sprintf(sbuf, "HTTP/%s %d %s\r\n", hr->version, hr->code, z_HTTP_errmsg(hr->code)); - odr_write(o, (unsigned char *) sbuf, strlen(sbuf)); - /* apply Content-Length if not already applied */ - if (!z_HTTP_header_lookup(hr->headers, - "Content-Length")) - { - char lstr[60]; - sprintf(lstr, "Content-Length: %d\r\n", - hr->content_len); - odr_write(o, (unsigned char *) lstr, strlen(lstr)); - } + odr_write(o, sbuf, strlen(sbuf)); + /* use content_len for Content-Length */ + sprintf(sbuf, "Content-Length: %d\r\n", hr->content_len); + odr_write(o, sbuf, strlen(sbuf)); for (h = hr->headers; h; h = h->next) { - odr_write(o, (unsigned char *) h->name, strlen(h->name)); - odr_write(o, (unsigned char *) ": ", 2); - odr_write(o, (unsigned char *) h->value, strlen(h->value)); - odr_write(o, (unsigned char *) "\r\n", 2); + if (yaz_strcasecmp(h->name, "Content-Length") + && yaz_strcasecmp(h->name, "Transfer-Encoding")) + { /* skip Content-Length if given. content_len rules */ + odr_write(o, h->name, strlen(h->name)); + odr_write(o, ": ", 2); + odr_write(o, h->value, strlen(h->value)); + odr_write(o, "\r\n", 2); + } } - odr_write(o, (unsigned char *) "\r\n", 2); + odr_write(o, "\r\n", 2); if (hr->content_buf) - odr_write(o, (unsigned char *) - hr->content_buf, - hr->content_len); + odr_write(o, hr->content_buf, hr->content_len); if (o->direction == ODR_PRINT) { - odr_printf(o, "-- HTTP response:\n%.*s\n", o->top - top0, - o->buf + top0); - odr_printf(o, "-- \n"); + odr_printf(o, "-- HTTP response:\n"); + dump_http_package(o, o->op->buf + top0, o->op->top - top0); + odr_printf(o, "--\n"); } return 1; } @@ -501,17 +610,14 @@ int yaz_encode_http_response(ODR o, Z_HTTP_Response *hr) int yaz_encode_http_request(ODR o, Z_HTTP_Request *hr) { Z_HTTP_Header *h; - int top0 = o->top; - - odr_write(o, (unsigned char *) hr->method, - strlen(hr->method)); - odr_write(o, (unsigned char *) " ", 1); - odr_write(o, (unsigned char *) hr->path, - strlen(hr->path)); - odr_write(o, (unsigned char *) " HTTP/", 6); - odr_write(o, (unsigned char *) hr->version, - strlen(hr->version)); - odr_write(o, (unsigned char *) "\r\n", 2); + int top0 = o->op->top; + + odr_write(o, hr->method, strlen(hr->method)); + odr_write(o, " ", 1); + odr_write(o, hr->path, strlen(hr->path)); + odr_write(o, " HTTP/", 6); + odr_write(o, hr->version, strlen(hr->version)); + odr_write(o, "\r\n", 2); if (hr->content_len && !z_HTTP_header_lookup(hr->headers, "Content-Length")) @@ -519,32 +625,57 @@ int yaz_encode_http_request(ODR o, Z_HTTP_Request *hr) char lstr[60]; sprintf(lstr, "Content-Length: %d\r\n", hr->content_len); - odr_write(o, (unsigned char *) lstr, strlen(lstr)); + odr_write(o, lstr, strlen(lstr)); } for (h = hr->headers; h; h = h->next) { - odr_write(o, (unsigned char *) h->name, strlen(h->name)); - odr_write(o, (unsigned char *) ": ", 2); - odr_write(o, (unsigned char *) h->value, strlen(h->value)); - odr_write(o, (unsigned char *) "\r\n", 2); + odr_write(o, h->name, strlen(h->name)); + odr_write(o, ": ", 2); + odr_write(o, h->value, strlen(h->value)); + odr_write(o, "\r\n", 2); } - odr_write(o, (unsigned char *) "\r\n", 2); + odr_write(o, "\r\n", 2); if (hr->content_buf) - odr_write(o, (unsigned char *) - hr->content_buf, - hr->content_len); + odr_write(o, hr->content_buf, hr->content_len); if (o->direction == ODR_PRINT) { - odr_printf(o, "-- HTTP request:\n%.*s\n", o->top - top0, - o->buf + top0); - odr_printf(o, "-- \n"); + odr_printf(o, "-- HTTP request:\n"); + dump_http_package(o, o->op->buf + top0, o->op->top - top0); + odr_printf(o, "--\n"); } return 1; } +const char *yaz_check_location(ODR odr, const char *uri, const char *location, + int *host_change) +{ + if (*location == '/') + { /* relative location */ + char *args = 0; + char *nlocation = (char *) odr_malloc(odr, strlen(location) + + strlen(uri) + 3); + strcpy(nlocation, uri); + cs_get_host_args(nlocation, (const char **) &args); + if (!args || !*args) + args = nlocation + strlen(nlocation); + else + args--; + strcpy(args, location); + *host_change = 0; + return nlocation; + } + else + { + /* we don't check if host is the same as before - yet */ + *host_change = 1; + return location; + } +} + /* * Local variables: * c-basic-offset: 4 + * c-file-style: "Stroustrup" * indent-tabs-mode: nil * End: * vim: shiftwidth=4 tabstop=8 expandtab