32 typedef int socklen_t;
36 #include <sys/socket.h>
39 #include <sys/types.h>
41 #include <yaz/snprintf.h>
58 #include <yaz/yaz-util.h>
59 #include <yaz/comstack.h>
61 #include <yaz/mutex.h>
68 #define MAX_HTTP_HEADER 4096
71 #define strncasecmp _strnicmp
72 #define strcasecmp _stricmp
77 #define HTTP_BUF_SIZE 4096
95 #define CLOSESOCKET(x) closesocket(x)
97 #define CLOSESOCKET(x) close(x)
122 for (; header; header = header->
next)
123 if (!strcasecmp(
name, header->
name))
124 return header->
value;
130 struct http_buf *r = xmalloc(
sizeof(*r));
164 memcpy((*p)->buf, b, tocopy);
177 queue = &(*queue)->
next;
192 while (b && rd <
len)
194 int toread =
len - rd;
207 for (; b; b = b->
next)
217 while ((*b) && rd <
len)
219 int toread =
len - rd;
220 if (toread > (*b)->len)
222 memcpy(
buf + rd, (*b)->buf + (*b)->offset, toread);
224 if (toread < (*b)->len)
227 (*b)->offset += toread;
251 else if (*i ==
'%' && i[1] && i[2])
255 sscanf(i,
"%2x", &v);
270 if (strchr(
" /:", *i))
272 sprintf(o,
"%%%.2X", (
int) *i);
305 for (; h; h = h->
next)
314 strcpy(r->
code,
"200");
324 static const char *
next_crlf(
const char *cp,
size_t *skipped)
326 const char *next_cp = strchr(cp,
'\n');
329 if (next_cp > cp && next_cp[-1] ==
'\r')
330 *skipped = next_cp - cp - 1;
332 *skipped = next_cp - cp;
347 const char *b =
next_crlf(buf, &skipped);
360 if (len + content_len <= sz)
361 return len + content_len;
366 if (!strncasecmp(buf,
"Content-Length:", 15))
368 const char *cp = buf+15;
372 while (*cp && isdigit(*(
const unsigned char *)cp))
373 content_len = content_len*10 + (*cp++ -
'0');
401 memcpy(tmp, buf, len);
402 for (p = tmp; *p && *p !=
' '; p++)
406 for (p2 = p; *p2 && *p2 !=
' ' && p2 - p < 3; p2++)
407 r->
code[p2 - p] = *p2;
408 if (!(p = strstr(tmp,
"\r\n")))
413 if (!(p2 = strstr(p,
"\r\n")))
420 char *
value = strchr(p,
':');
425 while (isspace(*(
const unsigned char *)
value))
446 const char *p2 = args;
451 const char *equal = strchr(p2,
'=');
452 const char *eoa = strchr(p2,
'&');
455 yaz_log(YLOG_WARN,
"Expected '=' in argument");
459 eoa = equal + strlen(equal);
460 else if (equal > eoa)
462 yaz_log(YLOG_WARN,
"Missing '&' in argument");
466 a->
name = nmem_strdupn(nmem, p2, equal - p2);
467 a->
value = nmem_strdupn(nmem, equal+1, eoa - equal - 1);
485 char *start = nmem_malloc(c->
nmem, len+1);
490 yaz_log(YLOG_WARN,
"http_buf_read < len (%d)", len);
500 for (p = buf, p2 = r->
method; *p && *p !=
' ' && p - buf < 19; p++)
504 yaz_log(YLOG_WARN,
"Unexpected HTTP method in request");
509 if (!(buf = strchr(buf,
' ')))
511 yaz_log(YLOG_WARN,
"Missing Request-URI in HTTP request");
515 if (!(p = strchr(buf,
' ')))
517 yaz_log(YLOG_WARN,
"HTTP Request-URI not terminated (too long?)");
521 if ((p2 = strchr(buf,
'?')))
523 r->
path = nmem_strdup(c->
nmem, buf);
532 if (strncmp(buf,
"HTTP/", 5))
540 if (!p || skipped < 3 || skipped > 5)
559 else if (skipped == 0)
567 char *n_v = nmem_malloc(c->
nmem, skipped+1);
570 memcpy(n_v, buf, skipped);
573 if (!(cp = strchr(n_v,
':')))
575 h->
name = nmem_strdupn(c->
nmem, n_v, cp - n_v);
587 if (!strcmp(c->
version,
"1.0"))
590 if (v && !strcmp(v,
"Keep-Alive"))
598 if (v && !strcmp(v,
"close"))
603 if (buf < start + len)
611 !yaz_strcmp_del(
"application/x-www-form-urlencoded",
625 wrbuf_rewind(c->
wrbuf);
632 wrbuf_printf(c->
wrbuf,
"Content-Length: %d\r\n", r->
payload ?
644 yaz_log(YLOG_WARN,
"Sending non-wellformed "
645 "response (bug #1162");
646 yaz_log(YLOG_WARN,
"payload: %s", r->
payload);
650 wrbuf_puts(c->
wrbuf,
"\r\n");
657 FILE *lf = yaz_log_file();
658 yaz_log(YLOG_LOG,
"Response:");
659 fwrite(wrbuf_buf(c->
wrbuf), 1, wrbuf_len(c->
wrbuf), lf);
671 wrbuf_rewind(c->
wrbuf);
680 wrbuf_puts(c->
wrbuf,
"\r\n");
686 yaz_log(YLOG_LOG,
"WRITING TO PROXY:\n%s\n----",
687 wrbuf_cstr(c->
wrbuf));
712 while (hp && hp->
next)
716 hpnew = nmem_malloc(ch->
nmem,
sizeof *hpnew);
734 if (WSAGetLastError() == WSAEWOULDBLOCK)
737 if (errno == EINPROGRESS)
746 unsigned long flags = 1;
747 if (ioctlsocket(sock, FIONBIO, &flags) < 0)
748 yaz_log(YLOG_FATAL|YLOG_ERRNO,
"ioctlsocket");
751 if ((flags = fcntl(sock, F_GETFL, 0)) < 0)
752 yaz_log(YLOG_FATAL|YLOG_ERRNO,
"fcntl");
753 if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0)
754 yaz_log(YLOG_FATAL|YLOG_ERRNO,
"fcntl2");
772 if (!(pe = getprotobyname(
"tcp"))) {
775 if ((sock = socket(PF_INET, SOCK_STREAM, pe->p_proto)) < 0)
777 yaz_log(YLOG_WARN|YLOG_ERRNO,
"socket");
780 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (
char*)
781 &one,
sizeof(one)) < 0)
784 if (connect(sock, (
struct sockaddr *)
790 yaz_log(YLOG_WARN|YLOG_ERRNO,
"Proxy connect");
814 yaz_log(YLOG_WARN,
"Failed to find Host header in proxy");
820 char server_via[128];
826 "X-Pazpar2-Server-Host", ser->
host);
828 "X-Pazpar2-Server-Port", ser->
port);
829 yaz_snprintf(server_via,
sizeof(server_via),
849 yaz_timing_stop(ch->
yt);
852 yaz_log(YLOG_LOG,
"Response: %6.5f %d %s%s%s ",
853 yaz_timing_get_real(ch->
yt),
863 yaz_log(YLOG_WARN,
"Failed to serialize HTTP response");
870 ch->
state = Http_Idle;
881 sprintf(rs->
code,
"%d", no);
885 yaz_snprintf(rs->
payload, 99,
"<error>HTTP Error %d: %s</error>\n",
902 if (res == -1 && errno == EAGAIN)
913 gettimeofday(&tv, 0);
915 (
long long) tv.tv_sec, (
long long) tv.tv_usec,
924 htbuf->
buf[res] =
'\0';
930 if (hc->
state == Http_Busy)
936 nmem_reset(hc->
nmem);
945 gettimeofday(&tv, 0);
947 (
long long) tv.tv_sec, (
long long) tv.tv_usec,
954 yaz_timing_start(hc->
yt);
957 yaz_log(YLOG_WARN,
"Failed to parse request");
962 yaz_log(YLOG_LOG,
"Request: - %d %s %s%s%s",
975 hc->
state = Http_Busy;
991 yaz_log(YLOG_WARN|YLOG_ERRNO,
"write");
1002 gettimeofday(&tv, 0);
1004 (
long long) tv.tv_sec, (
long long) tv.tv_usec,
1039 yaz_log(YLOG_WARN,
"Unexpected event on connection");
1064 yaz_log(YLOG_WARN,
"Proxy read came up short");
1079 htbuf->
buf[res] =
'\0';
1089 if (!(htbuf = pc->
oqueue))
1097 yaz_log(YLOG_WARN|YLOG_ERRNO,
"write");
1101 if (res == htbuf->
len)
1118 yaz_log(YLOG_WARN,
"Unexpected event on connection");
1143 yaz_timing_destroy(&s->
yt);
1156 nmem_destroy(s->
nmem);
1157 wrbuf_destroy(s->
wrbuf);
1168 r->
nmem = nmem_create();
1169 r->
wrbuf = wrbuf_alloc();
1186 yaz_log(YLOG_WARN,
"Invalid HTTP forward address");
1191 r->
yt = yaz_timing_create();
1200 struct sockaddr_storage addr;
1202 socklen_t len =
sizeof addr;
1208 if ((s = accept(fd, (
struct sockaddr *) &addr, &len)) < 0)
1210 yaz_log(YLOG_WARN|YLOG_ERRNO,
"accept");
1213 if (getnameinfo((
struct sockaddr *) &addr, len,
host,
sizeof(
host)-1, 0, 0,
1216 yaz_log(YLOG_WARN|YLOG_ERRNO,
"getnameinfo");
1222 yaz_log(YLOG_DEBUG,
"New command connection");
1224 "http_session_socket");
1232 yaz_log(YLOG_WARN,
"Refusing incoming HTTP connection");
1243 FILE *record_file = 0;
1244 struct addrinfo hints, *af = 0, *ai;
1248 yaz_log(YLOG_LOG,
"HTTP listener %s:%s", server->
host, server->
port);
1251 hints.ai_family = AF_UNSPEC;
1252 hints.ai_socktype = SOCK_STREAM;
1253 hints.ai_protocol = 0;
1254 hints.ai_addrlen = 0;
1255 hints.ai_addr = NULL;
1256 hints.ai_canonname = NULL;
1257 hints.ai_next = NULL;
1259 if (!strcmp(server->
host,
"@"))
1262 hints.ai_flags = AI_PASSIVE;
1263 error = getaddrinfo(0, server->
port, &hints, &af);
1266 error = getaddrinfo(server->
host, server->
port, &hints, &af);
1270 yaz_log(YLOG_FATAL,
"Failed to resolve %s: %s", server->
host,
1271 gai_strerror(
error));
1274 for (ai = af; ai; ai = ai->ai_next)
1276 if (ai->ai_family == AF_INET6)
1278 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
1285 for (ai = af; ai; ai = ai->ai_next)
1287 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
1294 yaz_log(YLOG_FATAL|YLOG_ERRNO,
"socket");
1298 if (ipv6_only >= 0 && ai->ai_family == AF_INET6 &&
1299 setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only,
sizeof(ipv6_only)))
1301 yaz_log(YLOG_FATAL|YLOG_ERRNO,
"setsockopt IPV6_V6ONLY %s:%s %d",
1302 server->
host, server->
port, ipv6_only);
1307 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one,
sizeof(one)))
1309 yaz_log(YLOG_FATAL|YLOG_ERRNO,
"setsockopt SO_REUSEADDR %s:%s",
1315 if (bind(s, ai->ai_addr, ai->ai_addrlen) < 0)
1317 yaz_log(YLOG_FATAL|YLOG_ERRNO,
"bind %s:%s",
1324 if (listen(s, SOMAXCONN) < 0)
1326 yaz_log(YLOG_FATAL|YLOG_ERRNO,
"listen %s:%s",
1334 record_file = fopen(record_fname,
"wb");
1337 yaz_log(YLOG_FATAL|YLOG_ERRNO,
"fopen %s", record_fname);
1346 yaz_log(YLOG_WARN,
"Can not create HTTP binding socket");
1349 fclose(record_file);
1380 WRBUF w = wrbuf_alloc();
1382 yaz_log(YLOG_LOG,
"HTTP backend %s", host);
1384 p = strchr(host,
':');
1388 wrbuf_write(w, host, p - host);
1394 wrbuf_puts(w, host);
1396 if (!(he = gethostbyname(wrbuf_cstr(w))))
1398 fprintf(stderr,
"Failed to lookup '%s'\n", wrbuf_cstr(w));
1406 he->h_addr_list[0], he->h_length);
1485 yaz_mutex_enter(hs->
mutex);
1487 yaz_mutex_leave(hs->
mutex);
1493 yaz_mutex_destroy(&hs->
mutex);
1504 yaz_mutex_enter(hs->
mutex);
1506 yaz_mutex_leave(hs->
mutex);
IOCHAN iochan_create(int fd, IOC_CALLBACK cb, int flags, const char *name)
void iochan_destroy(IOCHAN chan)
int iochan_add(iochan_man_t man, IOCHAN chan, int slack)
#define iochan_settimeout(i, t)
#define iochan_getdata(i)
#define iochan_setdata(i, d)
#define iochan_setflag(i, d)
#define iochan_setflags(i, d)
#define iochan_clearflag(i, d)
static void http_fire_observers(struct http_channel *c)
static void http_buf_destroy(http_server_t hs, struct http_buf *b)
static struct http_buf * http_buf_bybuf(http_server_t hs, char *b, int len)
struct http_response * http_create_response(struct http_channel *c)
static int request_check(struct http_buf *queue)
static void http_buf_enqueue(struct http_buf **queue, struct http_buf *b)
static int http_weshouldproxy(struct http_request *rq)
static struct http_buf * http_buf_bywrbuf(http_server_t hs, WRBUF wrbuf)
static void http_error(struct http_channel *hc, int no, const char *msg)
const char * http_lookup_header(struct http_header *header, const char *name)
void http_remove_observer(http_channel_observer_t obs)
void http_set_proxyaddr(const char *host, struct conf_server *server)
static void http_buf_destroy_queue(http_server_t hs, struct http_buf *b)
static struct http_buf * http_serialize_response(struct http_channel *c, struct http_response *r)
const char * http_headerbyname(struct http_header *h, const char *name)
static void http_server_incref(http_server_t hs)
struct http_request * http_parse_request(struct http_channel *c, struct http_buf **queue, int len)
struct http_header * http_header_append(struct http_channel *ch, struct http_header *hp, const char *name, const char *value)
static void proxy_io(IOCHAN i, int event)
void urlencode(const char *i, char *o)
const char * http_argbyname(struct http_request *r, const char *name)
static void http_channel_destroy(IOCHAN i)
void http_send_response(struct http_channel *ch)
static int log_level_post
int http_init(struct conf_server *server, const char *record_fname)
void http_close_server(struct conf_server *server)
static struct http_buf * http_buf_create(http_server_t hs)
struct http_response * http_parse_response_buf(struct http_channel *c, const char *buf, int len)
static int is_inprogress(void)
void http_addheader(struct http_response *r, const char *name, const char *value)
static int package_check(const char *buf, int sz)
static int http_buf_size(struct http_buf *b)
static struct http_buf * http_serialize_request(struct http_request *r)
static void http_accept(IOCHAN i, int event)
static void urldecode(char *i, char *o)
static int http_parse_arguments(struct http_request *r, NMEM nmem, const char *args)
void http_observer_set_data2(http_channel_observer_t obs, void *data2)
static void enable_nonblock(int sock)
static void http_buf_peek(struct http_buf *b, char *buf, int len)
static const char * next_crlf(const char *cp, size_t *skipped)
static http_server_t http_server_create(void)
http_channel_observer_t http_add_observer(struct http_channel *c, void *data, http_channel_destroy_t des)
static void http_destroy_observers(struct http_channel *c)
struct http_channel * http_channel_observer_chan(http_channel_observer_t obs)
void http_mutex_init(struct conf_server *server)
static int http_proxy(struct http_request *rq)
static struct http_channel * http_channel_create(http_server_t http_server, const char *addr, struct conf_server *server)
static int http_buf_read(http_server_t hs, struct http_buf **b, char *buf, int len)
static void http_io(IOCHAN i, int event)
void http_server_destroy(http_server_t hs)
void(* http_channel_destroy_t)(void *data, struct http_channel *c, void *data2)
http_sessions_t http_sessions_create(void)
void http_sessions_destroy(http_sessions_t hs)
void http_command(struct http_channel *c)
static void error(struct http_response *rs, enum pazpar2_error_code code, const char *addinfo)
struct parameters global_parameters
void pazpar2_mutex_create(YAZ_MUTEX *p, const char *name)
http_server_t http_server
struct http_argument * next
struct http_channel_observer_s * next
struct http_channel * chan
http_channel_destroy_t destroy
struct http_request * request
http_sessions_t http_sessions
struct http_response * response
http_channel_observer_t observers
struct http_proxy * proxy
http_server_t http_server
enum http_channel::@1 state
struct conf_server * server
struct http_channel * channel
struct http_header * headers
struct http_argument * arguments
struct http_channel * channel
struct http_header * headers
struct http_channel * channel
http_sessions_t http_sessions
struct sockaddr_in * proxy_addr