pazpar2  1.6.30
http.c
Go to the documentation of this file.
1 /* This file is part of Pazpar2.
2  Copyright (C) 2006-2013 Index Data
3 
4 Pazpar2 is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8 
9 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 for more details.
13 
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 
18 */
19 
20 #if HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #if HAVE_SYS_TIME_H
25 #include <sys/time.h>
26 #endif
27 
28 #include <stdio.h>
29 #ifdef WIN32
30 #include <winsock2.h>
31 #include <ws2tcpip.h>
32 typedef int socklen_t;
33 #endif
34 
35 #if HAVE_SYS_SOCKET_H
36 #include <sys/socket.h>
37 #endif
38 
39 #include <sys/types.h>
40 
41 #include <yaz/snprintf.h>
42 #if HAVE_UNISTD_H
43 #include <unistd.h>
44 #endif
45 
46 #include <stdlib.h>
47 #include <string.h>
48 #include <ctype.h>
49 #include <fcntl.h>
50 #if HAVE_NETDB_H
51 #include <netdb.h>
52 #endif
53 
54 #include <errno.h>
55 #include <assert.h>
56 #include <string.h>
57 
58 #include <yaz/yaz-util.h>
59 #include <yaz/comstack.h>
60 #include <yaz/nmem.h>
61 #include <yaz/mutex.h>
62 
63 #include "ppmutex.h"
64 #include "session.h"
65 #include "http.h"
66 #include "parameters.h"
67 
68 #define MAX_HTTP_HEADER 4096
69 
70 #ifdef WIN32
71 #define strncasecmp _strnicmp
72 #define strcasecmp _stricmp
73 #endif
74 
75 struct http_buf
76 {
77 #define HTTP_BUF_SIZE 4096
78  char buf[4096];
79  int offset;
80  int len;
81  struct http_buf *next;
82 };
83 
84 
85 static void proxy_io(IOCHAN i, int event);
87  const char *addr,
88  struct conf_server *server);
89 static void http_channel_destroy(IOCHAN i);
91 static void http_server_incref(http_server_t hs);
92 
93 #ifdef WIN32
94 #define CLOSESOCKET(x) closesocket(x)
95 #else
96 #define CLOSESOCKET(x) close(x)
97 #endif
98 
100 {
101  YAZ_MUTEX mutex;
105  struct sockaddr_in *proxy_addr;
106  FILE *record_file;
107 };
108 
110  void *data;
111  void *data2;
115 };
116 
117 
118 const char *http_lookup_header(struct http_header *header,
119  const char *name)
120 {
121  for (; header; header = header->next)
122  if (!strcasecmp(name, header->name))
123  return header->value;
124  return 0;
125 }
126 
128 {
129  struct http_buf *r = xmalloc(sizeof(*r));
130  r->offset = 0;
131  r->len = 0;
132  r->next = 0;
133  return r;
134 }
135 
136 static void http_buf_destroy(http_server_t hs, struct http_buf *b)
137 {
138  xfree(b);
139 }
140 
142 {
143  struct http_buf *p;
144  while (b)
145  {
146  p = b->next;
147  http_buf_destroy(hs, b);
148  b = p;
149  }
150 }
151 
152 static struct http_buf *http_buf_bybuf(http_server_t hs, char *b, int len)
153 {
154  struct http_buf *res = 0;
155  struct http_buf **p = &res;
156 
157  while (len)
158  {
159  int tocopy = len;
160  if (tocopy > HTTP_BUF_SIZE)
161  tocopy = HTTP_BUF_SIZE;
162  *p = http_buf_create(hs);
163  memcpy((*p)->buf, b, tocopy);
164  (*p)->len = tocopy;
165  len -= tocopy;
166  b += tocopy;
167  p = &(*p)->next;
168  }
169  return res;
170 }
171 
172 // Add a (chain of) buffers to the end of an existing queue.
173 static void http_buf_enqueue(struct http_buf **queue, struct http_buf *b)
174 {
175  while (*queue)
176  queue = &(*queue)->next;
177  *queue = b;
178 }
179 
180 static struct http_buf *http_buf_bywrbuf(http_server_t hs, WRBUF wrbuf)
181 {
182  // Heavens to Betsy (buf)!
183  return http_buf_bybuf(hs, wrbuf_buf(wrbuf), wrbuf_len(wrbuf));
184 }
185 
186 // Non-destructively collapse chain of buffers into a string (max *len)
187 // Return
188 static void http_buf_peek(struct http_buf *b, char *buf, int len)
189 {
190  int rd = 0;
191  while (b && rd < len)
192  {
193  int toread = len - rd;
194  if (toread > b->len)
195  toread = b->len;
196  memcpy(buf + rd, b->buf + b->offset, toread);
197  rd += toread;
198  b = b->next;
199  }
200  buf[rd] = '\0';
201 }
202 
203 static int http_buf_size(struct http_buf *b)
204 {
205  int sz = 0;
206  for (; b; b = b->next)
207  sz += b->len;
208  return sz;
209 }
210 
211 // Ddestructively munch up to len from head of queue.
213  struct http_buf **b, char *buf, int len)
214 {
215  int rd = 0;
216  while ((*b) && rd < len)
217  {
218  int toread = len - rd;
219  if (toread > (*b)->len)
220  toread = (*b)->len;
221  memcpy(buf + rd, (*b)->buf + (*b)->offset, toread);
222  rd += toread;
223  if (toread < (*b)->len)
224  {
225  (*b)->len -= toread;
226  (*b)->offset += toread;
227  break;
228  }
229  else
230  {
231  struct http_buf *n = (*b)->next;
232  http_buf_destroy(hs, *b);
233  *b = n;
234  }
235  }
236  buf[rd] = '\0';
237  return rd;
238 }
239 
240 // Buffers may overlap.
241 static void urldecode(char *i, char *o)
242 {
243  while (*i)
244  {
245  if (*i == '+')
246  {
247  *(o++) = ' ';
248  i++;
249  }
250  else if (*i == '%' && i[1] && i[2])
251  {
252  int v;
253  i++;
254  sscanf(i, "%2x", &v);
255  *o++ = v;
256  i += 2;
257  }
258  else
259  *(o++) = *(i++);
260  }
261  *o = '\0';
262 }
263 
264 // Warning: Buffers may not overlap
265 void urlencode(const char *i, char *o)
266 {
267  while (*i)
268  {
269  if (strchr(" /:", *i))
270  {
271  sprintf(o, "%%%.2X", (int) *i);
272  o += 3;
273  }
274  else
275  *(o++) = *i;
276  i++;
277  }
278  *o = '\0';
279 }
280 
281 void http_addheader(struct http_response *r, const char *name, const char *value)
282 {
283  struct http_channel *c = r->channel;
284  struct http_header *h = nmem_malloc(c->nmem, sizeof *h);
285  h->name = nmem_strdup(c->nmem, name);
286  h->value = nmem_strdup(c->nmem, value);
287  h->next = r->headers;
288  r->headers = h;
289 }
290 
291 const char *http_argbyname(struct http_request *r, const char *name)
292 {
293  struct http_argument *p;
294  if (!name)
295  return 0;
296  for (p = r->arguments; p; p = p->next)
297  if (!strcmp(p->name, name))
298  return p->value;
299  return 0;
300 }
301 
302 const char *http_headerbyname(struct http_header *h, const char *name)
303 {
304  for (; h; h = h->next)
305  if (!strcmp(h->name, name))
306  return h->value;
307  return 0;
308 }
309 
311 {
312  struct http_response *r = nmem_malloc(c->nmem, sizeof(*r));
313  strcpy(r->code, "200");
314  r->msg = "OK";
315  r->channel = c;
316  r->headers = 0;
317  r->payload = 0;
318  r->content_type = "text/xml";
319  return r;
320 }
321 
322 
323 static const char *next_crlf(const char *cp, size_t *skipped)
324 {
325  const char *next_cp = strchr(cp, '\n');
326  if (next_cp)
327  {
328  if (next_cp > cp && next_cp[-1] == '\r')
329  *skipped = next_cp - cp - 1;
330  else
331  *skipped = next_cp - cp;
332  next_cp++;
333  }
334  return next_cp;
335 }
336 
337 // Check if buf contains a package (minus payload)
338 static int package_check(const char *buf, int sz)
339 {
340  int content_len = 0;
341  int len = 0;
342 
343  while (*buf)
344  {
345  size_t skipped = 0;
346  const char *b = next_crlf(buf, &skipped);
347 
348  if (!b)
349  {
350  // we did not find CRLF.. See if buffer is too large..
351  if (sz >= MAX_HTTP_HEADER-1)
352  return MAX_HTTP_HEADER-1; // yes. Return that (will fail later)
353  break;
354  }
355  len += (b - buf);
356  if (skipped == 0)
357  {
358  // CRLF CRLF , i.e. end of header
359  if (len + content_len <= sz)
360  return len + content_len;
361  break;
362  }
363  buf = b;
364  // following first skip of \r\n so that we don't consider Method
365  if (!strncasecmp(buf, "Content-Length:", 15))
366  {
367  const char *cp = buf+15;
368  while (*cp == ' ')
369  cp++;
370  content_len = 0;
371  while (*cp && isdigit(*(const unsigned char *)cp))
372  content_len = content_len*10 + (*cp++ - '0');
373  if (content_len < 0) /* prevent negative offsets */
374  content_len = 0;
375  }
376  }
377  return 0; // incomplete request
378 }
379 
380 // Check if we have a request. Return 0 or length
381 static int request_check(struct http_buf *queue)
382 {
383  char tmp[MAX_HTTP_HEADER];
384 
385  // only peek at the header..
386  http_buf_peek(queue, tmp, MAX_HTTP_HEADER-1);
387  // still we only return non-zero if the complete request is received..
388  return package_check(tmp, http_buf_size(queue));
389 }
390 
391 struct http_response *http_parse_response_buf(struct http_channel *c, const char *buf, int len)
392 {
393  char tmp[MAX_HTTP_HEADER];
394  struct http_response *r = http_create_response(c);
395  char *p, *p2;
396  struct http_header **hp = &r->headers;
397 
398  if (len >= MAX_HTTP_HEADER)
399  return 0;
400  memcpy(tmp, buf, len);
401  for (p = tmp; *p && *p != ' '; p++) // Skip HTTP version
402  ;
403  p++;
404  // Response code
405  for (p2 = p; *p2 && *p2 != ' ' && p2 - p < 3; p2++)
406  r->code[p2 - p] = *p2;
407  if (!(p = strstr(tmp, "\r\n")))
408  return 0;
409  p += 2;
410  while (*p)
411  {
412  if (!(p2 = strstr(p, "\r\n")))
413  return 0;
414  if (p == p2) // End of headers
415  break;
416  else
417  {
418  struct http_header *h = *hp = nmem_malloc(c->nmem, sizeof(*h));
419  char *value = strchr(p, ':');
420  if (!value)
421  return 0;
422  *(value++) = '\0';
423  h->name = nmem_strdup(c->nmem, p);
424  while (isspace(*(const unsigned char *) value))
425  value++;
426  if (value >= p2) // Empty header;
427  {
428  h->value = "";
429  p = p2 + 2;
430  continue;
431  }
432  *p2 = '\0';
433  h->value = nmem_strdup(c->nmem, value);
434  h->next = 0;
435  hp = &h->next;
436  p = p2 + 2;
437  }
438  }
439  return r;
440 }
441 
442 static int http_parse_arguments(struct http_request *r, NMEM nmem,
443  const char *args)
444 {
445  const char *p2 = args;
446 
447  while (*p2)
448  {
449  struct http_argument *a;
450  const char *equal = strchr(p2, '=');
451  const char *eoa = strchr(p2, '&');
452  if (!equal)
453  {
454  yaz_log(YLOG_WARN, "Expected '=' in argument");
455  return -1;
456  }
457  if (!eoa)
458  eoa = equal + strlen(equal); // last argument
459  else if (equal > eoa)
460  {
461  yaz_log(YLOG_WARN, "Missing '&' in argument");
462  return -1;
463  }
464  a = nmem_malloc(nmem, sizeof(struct http_argument));
465  a->name = nmem_strdupn(nmem, p2, equal - p2);
466  a->value = nmem_strdupn(nmem, equal+1, eoa - equal - 1);
467  urldecode(a->name, a->name);
468  urldecode(a->value, a->value);
469  a->next = r->arguments;
470  r->arguments = a;
471  p2 = eoa;
472  while (*p2 == '&')
473  p2++;
474  }
475  return 0;
476 }
477 
479  struct http_buf **queue,
480  int len)
481 {
482  struct http_request *r = nmem_malloc(c->nmem, sizeof(*r));
483  char *p, *p2;
484  char *start = nmem_malloc(c->nmem, len+1);
485  char *buf = start;
486 
487  if (http_buf_read(c->http_server, queue, buf, len) < len)
488  {
489  yaz_log(YLOG_WARN, "http_buf_read < len (%d)", len);
490  return 0;
491  }
492  r->search = "";
493  r->channel = c;
494  r->arguments = 0;
495  r->headers = 0;
496  r->content_buf = 0;
497  r->content_len = 0;
498  // Parse first line
499  for (p = buf, p2 = r->method; *p && *p != ' ' && p - buf < 19; p++)
500  *(p2++) = *p;
501  if (*p != ' ')
502  {
503  yaz_log(YLOG_WARN, "Unexpected HTTP method in request");
504  return 0;
505  }
506  *p2 = '\0';
507 
508  if (!(buf = strchr(buf, ' ')))
509  {
510  yaz_log(YLOG_WARN, "Missing Request-URI in HTTP request");
511  return 0;
512  }
513  buf++;
514  if (!(p = strchr(buf, ' ')))
515  {
516  yaz_log(YLOG_WARN, "HTTP Request-URI not terminated (too long?)");
517  return 0;
518  }
519  *(p++) = '\0';
520  if ((p2 = strchr(buf, '?'))) // Do we have arguments?
521  *(p2++) = '\0';
522  r->path = nmem_strdup(c->nmem, buf);
523  if (p2)
524  {
525  r->search = nmem_strdup(c->nmem, p2);
526  // Parse Arguments
527  http_parse_arguments(r, c->nmem, p2);
528  }
529  buf = p;
530 
531  if (strncmp(buf, "HTTP/", 5))
532  strcpy(r->http_version, "1.0");
533  else
534  {
535  size_t skipped;
536  buf += 5; // strlen("HTTP/")
537 
538  p = (char*) next_crlf(buf, &skipped);
539  if (!p || skipped < 3 || skipped > 5)
540  return 0;
541 
542  memcpy(r->http_version, buf, skipped);
543  r->http_version[skipped] = '\0';
544  buf = p;
545  }
546  strcpy(c->version, r->http_version);
547 
548  r->headers = 0;
549  while (*buf)
550  {
551  size_t skipped;
552 
553  p = (char *) next_crlf(buf, &skipped);
554  if (!p)
555  {
556  return 0;
557  }
558  else if (skipped == 0)
559  {
560  buf = p;
561  break;
562  }
563  else
564  {
565  char *cp;
566  char *n_v = nmem_malloc(c->nmem, skipped+1);
567  struct http_header *h = nmem_malloc(c->nmem, sizeof(*h));
568 
569  memcpy(n_v, buf, skipped);
570  n_v[skipped] = '\0';
571 
572  if (!(cp = strchr(n_v, ':')))
573  return 0;
574  h->name = nmem_strdupn(c->nmem, n_v, cp - n_v);
575  cp++;
576  while (isspace(*cp))
577  cp++;
578  h->value = nmem_strdup(c->nmem, cp);
579  h->next = r->headers;
580  r->headers = h;
581  buf = p;
582  }
583  }
584 
585  // determine if we do keep alive
586  if (!strcmp(c->version, "1.0"))
587  {
588  const char *v = http_lookup_header(r->headers, "Connection");
589  if (v && !strcmp(v, "Keep-Alive"))
590  c->keep_alive = 1;
591  else
592  c->keep_alive = 0;
593  }
594  else
595  {
596  const char *v = http_lookup_header(r->headers, "Connection");
597  if (v && !strcmp(v, "close"))
598  c->keep_alive = 0;
599  else
600  c->keep_alive = 1;
601  }
602  if (buf < start + len)
603  {
604  const char *content_type = http_lookup_header(r->headers,
605  "Content-Type");
606  r->content_len = start + len - buf;
607  r->content_buf = buf;
608 
609  if (!yaz_strcmp_del("application/x-www-form-urlencoded",
610  content_type, "; "))
611  {
613  }
614  }
615  return r;
616 }
617 
619  struct http_response *r)
620 {
621  struct http_header *h;
622 
623  wrbuf_rewind(c->wrbuf);
624 
625  wrbuf_printf(c->wrbuf, "HTTP/%s %s %s\r\n", c->version, r->code, r->msg);
626  for (h = r->headers; h; h = h->next)
627  wrbuf_printf(c->wrbuf, "%s: %s\r\n", h->name, h->value);
628  if (r->payload)
629  {
630  wrbuf_printf(c->wrbuf, "Content-Length: %d\r\n", r->payload ?
631  (int) strlen(r->payload) : 0);
632  wrbuf_printf(c->wrbuf, "Content-Type: %s\r\n", r->content_type);
633  if (!strcmp(r->content_type, "text/xml"))
634  {
635  xmlDoc *doc = xmlParseMemory(r->payload, strlen(r->payload));
636  if (doc)
637  {
638  xmlFreeDoc(doc);
639  }
640  else
641  {
642  yaz_log(YLOG_WARN, "Sending non-wellformed "
643  "response (bug #1162");
644  yaz_log(YLOG_WARN, "payload: %s", r->payload);
645  }
646  }
647  }
648  wrbuf_puts(c->wrbuf, "\r\n");
649 
650  if (r->payload)
651  wrbuf_puts(c->wrbuf, r->payload);
652 
654  {
655  FILE *lf = yaz_log_file();
656  yaz_log(YLOG_LOG, "Response:");
657  fwrite(wrbuf_buf(c->wrbuf), 1, wrbuf_len(c->wrbuf), lf);
658  }
659  return http_buf_bywrbuf(c->http_server, c->wrbuf);
660 }
661 
662 // Serialize a HTTP request
664 {
665  struct http_channel *c = r->channel;
666  struct http_header *h;
667 
668  wrbuf_rewind(c->wrbuf);
669  wrbuf_printf(c->wrbuf, "%s %s%s%s", r->method, r->path,
670  *r->search ? "?" : "", r->search);
671 
672  wrbuf_printf(c->wrbuf, " HTTP/%s\r\n", r->http_version);
673 
674  for (h = r->headers; h; h = h->next)
675  wrbuf_printf(c->wrbuf, "%s: %s\r\n", h->name, h->value);
676 
677  wrbuf_puts(c->wrbuf, "\r\n");
678 
679  if (r->content_buf)
680  wrbuf_write(c->wrbuf, r->content_buf, r->content_len);
681 
682 #if 0
683  yaz_log(YLOG_LOG, "WRITING TO PROXY:\n%s\n----",
684  wrbuf_cstr(c->wrbuf));
685 #endif
686  return http_buf_bywrbuf(c->http_server, c->wrbuf);
687 }
688 
689 
690 static int http_weshouldproxy(struct http_request *rq)
691 {
692  struct http_channel *c = rq->channel;
693  if (c->server->http_server->proxy_addr && !strstr(rq->path, "search.pz2"))
694  return 1;
695  return 0;
696 }
697 
698 
700  struct http_header * hp,
701  const char *name,
702  const char *value)
703 {
704  struct http_header *hpnew = 0;
705 
706  if (!hp | !ch)
707  return 0;
708 
709  while (hp && hp->next)
710  hp = hp->next;
711 
712  if(name && strlen(name)&& value && strlen(value)){
713  hpnew = nmem_malloc(ch->nmem, sizeof *hpnew);
714  hpnew->name = nmem_strdup(ch->nmem, name);
715  hpnew->value = nmem_strdup(ch->nmem, value);
716 
717  hpnew->next = 0;
718  hp->next = hpnew;
719  hp = hp->next;
720 
721  return hpnew;
722  }
723 
724  return hp;
725 }
726 
727 
728 static int is_inprogress(void)
729 {
730 #ifdef WIN32
731  if (WSAGetLastError() == WSAEWOULDBLOCK)
732  return 1;
733 #else
734  if (errno == EINPROGRESS)
735  return 1;
736 #endif
737  return 0;
738 }
739 
740 static void enable_nonblock(int sock)
741 {
742  int flags;
743 #ifdef WIN32
744  flags = (flags & CS_FLAGS_BLOCKING) ? 0 : 1;
745  if (ioctlsocket(sock, FIONBIO, &flags) < 0)
746  yaz_log(YLOG_FATAL|YLOG_ERRNO, "ioctlsocket");
747 #else
748  if ((flags = fcntl(sock, F_GETFL, 0)) < 0)
749  yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl");
750  if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0)
751  yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl2");
752 #endif
753 }
754 
755 static int http_proxy(struct http_request *rq)
756 {
757  struct http_channel *c = rq->channel;
758  struct http_proxy *p = c->proxy;
759  struct http_header *hp;
760  struct http_buf *requestbuf;
761  char server_port[16] = "";
762  struct conf_server *ser = c->server;
763 
764  if (!p) // This is a new connection. Create a proxy channel
765  {
766  int sock;
767  struct protoent *pe;
768  int one = 1;
769 
770  if (!(pe = getprotobyname("tcp"))) {
771  abort();
772  }
773  if ((sock = socket(PF_INET, SOCK_STREAM, pe->p_proto)) < 0)
774  {
775  yaz_log(YLOG_WARN|YLOG_ERRNO, "socket");
776  return -1;
777  }
778  if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)
779  &one, sizeof(one)) < 0)
780  abort();
781  enable_nonblock(sock);
782  if (connect(sock, (struct sockaddr *)
784  sizeof(*c->server->http_server->proxy_addr)) < 0)
785  {
786  if (!is_inprogress())
787  {
788  yaz_log(YLOG_WARN|YLOG_ERRNO, "Proxy connect");
789  return -1;
790  }
791  }
792  p = xmalloc(sizeof(struct http_proxy));
793  p->oqueue = 0;
794  p->channel = c;
795  p->first_response = 1;
796  c->proxy = p;
797  // We will add EVENT_OUTPUT below
798  p->iochan = iochan_create(sock, proxy_io, EVENT_INPUT, "http_proxy");
799  iochan_setdata(p->iochan, p);
800 
801  iochan_add(ser->iochan_man, p->iochan);
802  }
803 
804  // Do _not_ modify Host: header, just checking it's existence
805 
806  if (!http_lookup_header(rq->headers, "Host"))
807  {
808  yaz_log(YLOG_WARN, "Failed to find Host header in proxy");
809  return -1;
810  }
811 
812  // Add new header about paraz2 version, host, remote client address, etc.
813  {
814  char server_via[128];
815 
816  hp = rq->headers;
817  hp = http_header_append(c, hp,
818  "X-Pazpar2-Version", PACKAGE_VERSION);
819  hp = http_header_append(c, hp,
820  "X-Pazpar2-Server-Host", ser->host);
821  sprintf(server_port, "%d", ser->port);
822  hp = http_header_append(c, hp,
823  "X-Pazpar2-Server-Port", server_port);
824  yaz_snprintf(server_via, sizeof(server_via),
825  "1.1 %s:%s (%s/%s)",
826  ser->host ? ser->host : "@",
827  server_port, PACKAGE_NAME, PACKAGE_VERSION);
828  hp = http_header_append(c, hp, "Via" , server_via);
829  hp = http_header_append(c, hp, "X-Forwarded-For", c->addr);
830  }
831 
832  requestbuf = http_serialize_request(rq);
833 
834  http_buf_enqueue(&p->oqueue, requestbuf);
836  return 0;
837 }
838 
840 {
841  struct http_response *rs = ch->response;
842  struct http_buf *hb;
843 
844  assert(rs);
845  hb = http_serialize_response(ch, rs);
846  if (!hb)
847  {
848  yaz_log(YLOG_WARN, "Failed to serialize HTTP response");
850  }
851  else
852  {
853  http_buf_enqueue(&ch->oqueue, hb);
855  ch->state = Http_Idle;
856  }
857 }
858 
859 static void http_error(struct http_channel *hc, int no, const char *msg)
860 {
861  struct http_response *rs = http_create_response(hc);
862 
863  hc->response = rs;
864  hc->keep_alive = 0; // not keeping this HTTP session alive
865 
866  sprintf(rs->code, "%d", no);
867 
868  rs->msg = nmem_strdup(hc->nmem, msg);
869  rs->payload = nmem_malloc(hc->nmem, 100);
870  yaz_snprintf(rs->payload, 99, "<error>HTTP Error %d: %s</error>\n",
871  no, msg);
872  http_send_response(hc);
873 }
874 
875 static void http_io(IOCHAN i, int event)
876 {
877  struct http_channel *hc = iochan_getdata(i);
878  while (event)
879  {
880  if (event == EVENT_INPUT)
881  {
882  int res, reqlen;
883  struct http_buf *htbuf;
884 
885  htbuf = http_buf_create(hc->http_server);
886  res = recv(iochan_getfd(i), htbuf->buf, HTTP_BUF_SIZE -1, 0);
887  if (res == -1 && errno == EAGAIN)
888  {
889  http_buf_destroy(hc->http_server, htbuf);
890  return;
891  }
892  if (res <= 0)
893  {
894 #if HAVE_SYS_TIME_H
895  if (hc->http_server->record_file)
896  {
897  struct timeval tv;
898  gettimeofday(&tv, 0);
899  fprintf(hc->http_server->record_file, "r %lld %lld %lld 0\n",
900  (long long) tv.tv_sec, (long long) tv.tv_usec,
901  (long long) iochan_getfd(i));
902  }
903 #endif
904  http_buf_destroy(hc->http_server, htbuf);
905  fflush(hc->http_server->record_file);
907  return;
908  }
909  htbuf->buf[res] = '\0';
910  htbuf->len = res;
911  http_buf_enqueue(&hc->iqueue, htbuf);
912 
913  while (1)
914  {
915  if (hc->state == Http_Busy)
916  return;
917  reqlen = request_check(hc->iqueue);
918  if (reqlen <= 2)
919  return;
920  // we have a complete HTTP request
921  nmem_reset(hc->nmem);
922 #if HAVE_SYS_TIME_H
923  if (hc->http_server->record_file)
924  {
925  struct timeval tv;
926  int sz = 0;
927  struct http_buf *hb;
928  for (hb = hc->iqueue; hb; hb = hb->next)
929  sz += hb->len;
930  gettimeofday(&tv, 0);
931  fprintf(hc->http_server->record_file, "r %lld %lld %lld %d\n",
932  (long long) tv.tv_sec, (long long) tv.tv_usec,
933  (long long) iochan_getfd(i), sz);
934  for (hb = hc->iqueue; hb; hb = hb->next)
935  fwrite(hb->buf, 1, hb->len, hc->http_server->record_file);
936  fflush(hc->http_server->record_file);
937  }
938  #endif
939  if (!(hc->request = http_parse_request(hc, &hc->iqueue, reqlen)))
940  {
941  yaz_log(YLOG_WARN, "Failed to parse request");
942  http_error(hc, 400, "Bad Request");
943  return;
944  }
945  hc->response = 0;
946  yaz_log(YLOG_LOG, "Request: %s %s%s%s", hc->request->method,
947  hc->request->path,
948  *hc->request->search ? "?" : "",
949  hc->request->search);
950  if (hc->request->content_buf)
951  yaz_log(YLOG_LOG, "%s", hc->request->content_buf);
952  if (http_weshouldproxy(hc->request))
953  http_proxy(hc->request);
954  else
955  {
956  // Execute our business logic!
957  hc->state = Http_Busy;
958  http_command(hc);
959  }
960  }
961  }
962  else if (event == EVENT_OUTPUT)
963  {
964  event = 0;
965  if (hc->oqueue)
966  {
967  struct http_buf *wb = hc->oqueue;
968  int res;
969  res = send(iochan_getfd(hc->iochan),
970  wb->buf + wb->offset, wb->len, 0);
971  if (res <= 0)
972  {
973  yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
975  return;
976  }
977  if (res == wb->len)
978  {
979 #if HAVE_SYS_TIME_H
980  if (hc->http_server->record_file)
981  {
982  struct timeval tv;
983  int sz = wb->offset + wb->len;
984  gettimeofday(&tv, 0);
985  fprintf(hc->http_server->record_file, "w %lld %lld %lld %d\n",
986  (long long) tv.tv_sec, (long long) tv.tv_usec,
987  (long long) iochan_getfd(i), sz);
988  fwrite(wb->buf, 1, wb->offset + wb->len,
989  hc->http_server->record_file);
990  fputc('\n', hc->http_server->record_file);
991  fflush(hc->http_server->record_file);
992  }
993  #endif
994  hc->oqueue = hc->oqueue->next;
995  http_buf_destroy(hc->http_server, wb);
996  }
997  else
998  {
999  wb->len -= res;
1000  wb->offset += res;
1001  }
1002  if (!hc->oqueue)
1003  {
1004  if (!hc->keep_alive)
1005  {
1007  return;
1008  }
1009  else
1010  {
1012  if (hc->iqueue)
1013  event = EVENT_INPUT;
1014  }
1015  }
1016  }
1017  if (!hc->oqueue && hc->proxy && !hc->proxy->iochan)
1018  http_channel_destroy(i); // Server closed; we're done
1019  }
1020  else
1021  {
1022  yaz_log(YLOG_WARN, "Unexpected event on connection");
1024  event = 0;
1025  }
1026  }
1027 }
1028 
1029 // Handles I/O on a client connection to a backend web server (proxy mode)
1030 static void proxy_io(IOCHAN pi, int event)
1031 {
1032  struct http_proxy *pc = iochan_getdata(pi);
1033  struct http_channel *hc = pc->channel;
1034 
1035  switch (event)
1036  {
1037  int res;
1038  struct http_buf *htbuf;
1039 
1040  case EVENT_INPUT:
1041  htbuf = http_buf_create(hc->http_server);
1042  res = recv(iochan_getfd(pi), htbuf->buf, HTTP_BUF_SIZE -1, 0);
1043  if (res == 0 || (res < 0 && !is_inprogress()))
1044  {
1045  if (hc->oqueue)
1046  {
1047  yaz_log(YLOG_WARN, "Proxy read came up short");
1048  // Close channel and alert client HTTP channel that we're gone
1049  http_buf_destroy(hc->http_server, htbuf);
1051  iochan_destroy(pi);
1052  pc->iochan = 0;
1053  }
1054  else
1055  {
1057  return;
1058  }
1059  }
1060  else
1061  {
1062  htbuf->buf[res] = '\0';
1063  htbuf->offset = 0;
1064  htbuf->len = res;
1065  // Write any remaining payload
1066  if (htbuf->len - htbuf->offset > 0)
1067  http_buf_enqueue(&hc->oqueue, htbuf);
1068  }
1070  break;
1071  case EVENT_OUTPUT:
1072  if (!(htbuf = pc->oqueue))
1073  {
1075  return;
1076  }
1077  res = send(iochan_getfd(pi), htbuf->buf + htbuf->offset, htbuf->len, 0);
1078  if (res <= 0)
1079  {
1080  yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
1082  return;
1083  }
1084  if (res == htbuf->len)
1085  {
1086  struct http_buf *np = htbuf->next;
1087  http_buf_destroy(hc->http_server, htbuf);
1088  pc->oqueue = np;
1089  }
1090  else
1091  {
1092  htbuf->len -= res;
1093  htbuf->offset += res;
1094  }
1095 
1096  if (!pc->oqueue) {
1097  iochan_setflags(pi, EVENT_INPUT); // Turns off output flag
1098  }
1099  break;
1100  default:
1101  yaz_log(YLOG_WARN, "Unexpected event on connection");
1103  break;
1104  }
1105 }
1106 
1107 static void http_fire_observers(struct http_channel *c);
1108 static void http_destroy_observers(struct http_channel *c);
1109 
1110 // Cleanup channel
1112 {
1113  struct http_channel *s = iochan_getdata(i);
1115 
1116  if (s->proxy)
1117  {
1118  if (s->proxy->iochan)
1119  {
1122  }
1124  xfree(s->proxy);
1125  }
1130 
1131  http_server = s->http_server; /* save it for destroy (decref) */
1132 
1133  http_server_destroy(http_server);
1134 
1136 
1137  iochan_destroy(i);
1138  nmem_destroy(s->nmem);
1139  wrbuf_destroy(s->wrbuf);
1140  xfree(s);
1141 }
1142 
1144  const char *addr,
1145  struct conf_server *server)
1146 {
1147  struct http_channel *r;
1148 
1149  r = xmalloc(sizeof(struct http_channel));
1150  r->nmem = nmem_create();
1151  r->wrbuf = wrbuf_alloc();
1152 
1153  http_server_incref(hs);
1154  r->http_server = hs;
1155  r->http_sessions = hs->http_sessions;
1156  assert(r->http_sessions);
1157  r->server = server;
1158  r->proxy = 0;
1159  r->iochan = 0;
1160  r->iqueue = r->oqueue = 0;
1161  r->state = Http_Idle;
1162  r->keep_alive = 0;
1163  r->request = 0;
1164  r->response = 0;
1165  strcpy(r->version, "1.0");
1166  if (!addr)
1167  {
1168  yaz_log(YLOG_WARN, "Invalid HTTP forward address");
1169  exit(1);
1170  }
1171  strcpy(r->addr, addr);
1172  r->observers = 0;
1173  return r;
1174 }
1175 
1176 
1177 /* Accept a new command connection */
1178 static void http_accept(IOCHAN i, int event)
1179 {
1180  char host[256];
1181  struct sockaddr addr;
1182  int fd = iochan_getfd(i);
1183  socklen_t len;
1184  int s;
1185  IOCHAN c;
1186  struct http_channel *ch;
1187  struct conf_server *server = iochan_getdata(i);
1188 
1189  len = sizeof addr;
1190  if ((s = accept(fd, &addr, &len)) < 0)
1191  {
1192  yaz_log(YLOG_WARN|YLOG_ERRNO, "accept");
1193  return;
1194  }
1195  if (getnameinfo(&addr, len, host, sizeof(host)-1, 0, 0, NI_NUMERICHOST))
1196  {
1197  yaz_log(YLOG_WARN|YLOG_ERRNO, "getnameinfo");
1198  CLOSESOCKET(s);
1199  return;
1200  }
1201  enable_nonblock(s);
1202 
1203  yaz_log(YLOG_DEBUG, "New command connection");
1205  "http_session_socket");
1206 
1207 
1208  ch = http_channel_create(server->http_server, host, server);
1209  ch->iochan = c;
1210  iochan_setdata(c, ch);
1211  iochan_add(server->iochan_man, c);
1212 }
1213 
1214 /* Create a http-channel listener, syntax [host:]port */
1215 int http_init(const char *addr, struct conf_server *server,
1216  const char *record_fname)
1217 {
1218  IOCHAN c;
1219  int l;
1220  int one = 1;
1221  const char *pp;
1222  FILE *record_file = 0;
1223  struct addrinfo hints, *ai = 0;
1224  int error;
1225  int ipv6_only = -1;
1226 
1227  yaz_log(YLOG_LOG, "HTTP listener %s", addr);
1228 
1229  hints.ai_flags = 0;
1230  hints.ai_family = AF_UNSPEC;
1231  hints.ai_socktype = SOCK_STREAM;
1232  hints.ai_protocol = 0;
1233  hints.ai_addrlen = 0;
1234  hints.ai_addr = NULL;
1235  hints.ai_canonname = NULL;
1236  hints.ai_next = NULL;
1237 
1238  pp = strchr(addr, ':');
1239  if (pp)
1240  {
1241  WRBUF w = wrbuf_alloc();
1242  wrbuf_write(w, addr, pp - addr);
1243  if (!strcmp(wrbuf_cstr(w), "@"))
1244  { /* IPV4 + IPV6 .. same as YAZ */
1245  ipv6_only = 0;
1246  hints.ai_flags = AI_PASSIVE;
1247  hints.ai_family = AF_INET6;
1248  error = getaddrinfo(0, pp + 1, &hints, &ai);
1249  }
1250  else
1251  error = getaddrinfo(wrbuf_cstr(w), pp + 1, &hints, &ai);
1252  wrbuf_destroy(w);
1253  }
1254  else
1255  {
1256  /* default for no host given is IPV4 */
1257  hints.ai_flags = AI_PASSIVE;
1258  hints.ai_family = AF_INET;
1259  error = getaddrinfo(0, addr, &hints, &ai);
1260  }
1261  if (error)
1262  {
1263  yaz_log(YLOG_FATAL, "Failed to resolve %s: %s", addr,
1264  gai_strerror(error));
1265  return 1;
1266  }
1267  l = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
1268  if (l < 0)
1269  {
1270  yaz_log(YLOG_FATAL|YLOG_ERRNO, "socket");
1271  freeaddrinfo(ai);
1272  return 1;
1273  }
1274  if (ipv6_only >= 0 &&
1275  setsockopt(l, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only)))
1276  {
1277  yaz_log(YLOG_FATAL|YLOG_ERRNO, "setsockopt IPV6_V6ONLY %s %d", addr,
1278  ipv6_only);
1279  freeaddrinfo(ai);
1280  CLOSESOCKET(l);
1281  return 1;
1282  }
1283  if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)))
1284  {
1285  yaz_log(YLOG_FATAL|YLOG_ERRNO, "setsockopt SO_REUSEADDR %s", addr);
1286  freeaddrinfo(ai);
1287  CLOSESOCKET(l);
1288  return 1;
1289  }
1290  if (bind(l, ai->ai_addr, ai->ai_addrlen) < 0)
1291  {
1292  yaz_log(YLOG_FATAL|YLOG_ERRNO, "bind %s", addr);
1293  freeaddrinfo(ai);
1294  CLOSESOCKET(l);
1295  return 1;
1296  }
1297  freeaddrinfo(ai);
1298  if (listen(l, SOMAXCONN) < 0)
1299  {
1300  yaz_log(YLOG_FATAL|YLOG_ERRNO, "listen %s", addr);
1301  CLOSESOCKET(l);
1302  return 1;
1303  }
1304 
1305  if (record_fname)
1306  {
1307  record_file = fopen(record_fname, "wb");
1308  if (!record_file)
1309  {
1310  yaz_log(YLOG_FATAL|YLOG_ERRNO, "fopen %s", record_fname);
1311  CLOSESOCKET(l);
1312  return 1;
1313  }
1314  }
1315  server->http_server = http_server_create();
1316 
1317  server->http_server->record_file = record_file;
1318  server->http_server->listener_socket = l;
1319 
1320  c = iochan_create(l, http_accept, EVENT_INPUT | EVENT_EXCEPT, "http_server");
1321  iochan_setdata(c, server);
1322 
1323  iochan_add(server->iochan_man, c);
1324  return 0;
1325 }
1326 
1327 void http_close_server(struct conf_server *server)
1328 {
1329  /* break the event_loop (select) by closing down the HTTP listener sock */
1330  if (server->http_server->listener_socket)
1331  {
1332 #ifdef WIN32
1333  closesocket(server->http_server->listener_socket);
1334 #else
1335  close(server->http_server->listener_socket);
1336 #endif
1337  }
1338 }
1339 
1340 void http_set_proxyaddr(const char *host, struct conf_server *server)
1341 {
1342  const char *p;
1343  short port;
1344  struct hostent *he;
1345  WRBUF w = wrbuf_alloc();
1346 
1347  yaz_log(YLOG_LOG, "HTTP backend %s", host);
1348 
1349  p = strchr(host, ':');
1350  if (p)
1351  {
1352  port = atoi(p + 1);
1353  wrbuf_write(w, host, p - host);
1354  wrbuf_puts(w, "");
1355  }
1356  else
1357  {
1358  port = 80;
1359  wrbuf_puts(w, host);
1360  }
1361  if (!(he = gethostbyname(wrbuf_cstr(w))))
1362  {
1363  fprintf(stderr, "Failed to lookup '%s'\n", wrbuf_cstr(w));
1364  exit(1);
1365  }
1366  wrbuf_destroy(w);
1367 
1368  server->http_server->proxy_addr = xmalloc(sizeof(struct sockaddr_in));
1369  server->http_server->proxy_addr->sin_family = he->h_addrtype;
1370  memcpy(&server->http_server->proxy_addr->sin_addr.s_addr,
1371  he->h_addr_list[0], he->h_length);
1372  server->http_server->proxy_addr->sin_port = htons(port);
1373 }
1374 
1375 static void http_fire_observers(struct http_channel *c)
1376 {
1378  while (p)
1379  {
1380  p->destroy(p->data, c, p->data2);
1381  p = p->next;
1382  }
1383 }
1384 
1385 static void http_destroy_observers(struct http_channel *c)
1386 {
1387  while (c->observers)
1388  {
1390  c->observers = obs->next;
1391  xfree(obs);
1392  }
1393 }
1394 
1397 {
1398  http_channel_observer_t obs = xmalloc(sizeof(*obs));
1399  obs->chan = c;
1400  obs->data = data;
1401  obs->data2 = 0;
1402  obs->destroy= des;
1403  obs->next = c->observers;
1404  c->observers = obs;
1405  return obs;
1406 }
1407 
1409 {
1410  struct http_channel *c = obs->chan;
1411  http_channel_observer_t found, *p = &c->observers;
1412  while (*p != obs)
1413  p = &(*p)->next;
1414  found = *p;
1415  assert(found);
1416  *p = (*p)->next;
1417  xfree(found);
1418 }
1419 
1421 {
1422  return obs->chan;
1423 }
1424 
1426 {
1427  obs->data2 = data2;
1428 }
1429 
1431 {
1432  http_server_t hs = xmalloc(sizeof(*hs));
1433  hs->mutex = 0;
1434  hs->proxy_addr = 0;
1435  hs->ref_count = 1;
1436  hs->http_sessions = 0;
1437 
1438  hs->record_file = 0;
1439  return hs;
1440 }
1441 
1443 {
1444  if (hs)
1445  {
1446  int r;
1447 
1448  yaz_mutex_enter(hs->mutex); /* OK: hs->mutex may be NULL */
1449  r = --(hs->ref_count);
1450  yaz_mutex_leave(hs->mutex);
1451 
1452  if (r == 0)
1453  {
1455  xfree(hs->proxy_addr);
1456  yaz_mutex_destroy(&hs->mutex);
1457  if (hs->record_file)
1458  fclose(hs->record_file);
1459  xfree(hs);
1460  }
1461  }
1462 }
1463 
1465 {
1466  assert(hs);
1467  yaz_mutex_enter(hs->mutex);
1468  (hs->ref_count)++;
1469  yaz_mutex_leave(hs->mutex);
1470 }
1471 
1473 {
1474  assert(server);
1475 
1476  assert(server->http_server->mutex == 0);
1477  pazpar2_mutex_create(&server->http_server->mutex, "http_server");
1479 }
1480 
1481 /*
1482  * Local variables:
1483  * c-basic-offset: 4
1484  * c-file-style: "Stroustrup"
1485  * indent-tabs-mode: nil
1486  * End:
1487  * vim: shiftwidth=4 tabstop=8 expandtab
1488  */
1489