2 * $Id: http.c,v 1.1 2006-11-21 18:46:43 quinn Exp $
6 #include <sys/socket.h>
15 #include <yaz/yaz-util.h>
16 #include <yaz/comstack.h>
24 #include "http_command.h"
26 extern IOCHAN channel_list;
28 void http_addheader(struct http_response *r, const char *name, const char *value)
30 struct http_channel *c = r->channel;
31 struct http_header *h = nmem_malloc(c->nmem, sizeof *h);
32 h->name = nmem_strdup(c->nmem, name);
33 h->value = nmem_strdup(c->nmem, value);
38 char *argbyname(struct http_request *r, char *name)
40 struct http_argument *p;
41 for (p = r->arguments; p; p = p->next)
42 if (!strcmp(p->name, name))
47 char *headerbyname(struct http_request *r, char *name)
49 struct http_header *p;
50 for (p = r->headers; p; p = p->next)
51 if (!strcmp(p->name, name))
56 struct http_response *http_create_response(struct http_channel *c)
58 struct http_response *r = nmem_malloc(c->nmem, sizeof(*r));
59 strcpy(r->code, "200");
67 // Check if we have a complete request. Return 0 or length (including trailing newline)
68 // FIXME: Does not deal gracefully with requests carrying payload
69 // but this is kind of OK since we will reject anything other than an empty GET
70 static int request_check(const char *buf)
74 while (*buf) // Check if we have a sequence of lines terminated by an empty line
76 char *b = strstr(buf, "\r\n");
89 struct http_request *http_parse_request(struct http_channel *c, char *buf)
91 struct http_request *r = nmem_malloc(c->nmem, sizeof(*r));
96 if (!strncmp(buf, "GET ", 4))
97 r->method = Method_GET;
100 yaz_log(YLOG_WARN, "Unexpected HTTP method in request");
103 if (!(buf = strchr(buf, ' ')))
105 yaz_log(YLOG_WARN, "Syntax error in request (1)");
109 if (!(p = strchr(buf, ' ')))
111 yaz_log(YLOG_WARN, "Syntax error in request (2)");
115 if ((p2 = strchr(buf, '?'))) // Do we have arguments?
117 r->path = nmem_strdup(c->nmem, buf);
123 struct http_argument *a;
124 char *equal = strchr(p2, '=');
125 char *eoa = strchr(p2, '&');
128 yaz_log(YLOG_WARN, "Expected '=' in argument");
132 eoa = equal + strlen(equal); // last argument
135 a = nmem_malloc(c->nmem, sizeof(struct http_argument));
137 a->name = nmem_strdup(c->nmem, p2);
138 a->value = nmem_strdup(c->nmem, equal);
139 a->next = r->arguments;
146 if (strncmp(buf, "HTTP/", 5))
147 strcpy(r->http_version, "1.0");
151 if (!(p = strstr(buf, "\r\n")))
154 strcpy(r->http_version, buf);
157 strcpy(c->version, r->http_version);
159 r->headers = 0; // We might want to parse these someday
165 static char *http_serialize_response(struct http_channel *c, struct http_response *r)
167 wrbuf_rewind(c->wrbuf);
168 struct http_header *h;
170 wrbuf_printf(c->wrbuf, "HTTP/1.1 %s %s\r\n", r->code, r->msg);
171 for (h = r->headers; h; h = h->next)
172 wrbuf_printf(c->wrbuf, "%s: %s\r\n", h->name, h->value);
173 wrbuf_printf(c->wrbuf, "Content-length: %d\r\n", r->payload ? strlen(r->payload) : 0);
174 wrbuf_printf(c->wrbuf, "Content-type: text/xml\r\n");
175 wrbuf_puts(c->wrbuf, "\r\n");
178 wrbuf_puts(c->wrbuf, r->payload);
180 wrbuf_putc(c->wrbuf, '\0');
181 return wrbuf_buf(c->wrbuf);
185 static void http_destroy(IOCHAN i)
187 struct http_channel *s = iochan_getdata(i);
189 yaz_log(YLOG_DEBUG, "Destroying http channel");
190 nmem_destroy(s->nmem);
191 wrbuf_free(s->wrbuf, 1);
193 close(iochan_getfd(i));
197 static void http_io(IOCHAN i, int event)
199 struct http_channel *hc = iochan_getdata(i);
200 struct http_request *request;
201 struct http_response *response;
208 yaz_log(YLOG_DEBUG, "HTTP Input event");
210 res = read(iochan_getfd(i), hc->ibuf + hc->read, IBUF_SIZE - (hc->read + 1));
213 yaz_log(YLOG_WARN|YLOG_ERRNO, "HTTP read");
217 yaz_log(YLOG_DEBUG, "HTTP read %d octets", res);
219 hc->ibuf[hc->read] = '\0';
221 if ((res = request_check(hc->ibuf)) <= 2)
223 yaz_log(YLOG_DEBUG, "We don't have a complete HTTP request yet");
226 yaz_log(YLOG_DEBUG, "We think we have a complete HTTP request (len %d): \n%s", res, hc->ibuf);
227 nmem_reset(hc->nmem);
228 if (!(request = http_parse_request(hc, hc->ibuf)))
230 yaz_log(YLOG_WARN, "Failed to parse request");
234 response = http_command(request);
240 // FIXME -- do something to cause the response to be sent to the client
241 if (!(hc->obuf = http_serialize_response(hc, response)))
246 yaz_log(YLOG_DEBUG, "Response ready:\n%s", hc->obuf);
249 iochan_setflags(i, EVENT_OUTPUT); // Turns off input selecting
253 yaz_log(YLOG_DEBUG, "HTTP output event");
254 res = write(iochan_getfd(hc->iochan), hc->obuf + hc->writ,
255 strlen(hc->obuf + hc->writ));
258 yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
263 if (!hc->obuf[hc->writ]) {
264 yaz_log(YLOG_DEBUG, "Writing finished");
265 if (!strcmp(hc->version, "1.0"))
267 yaz_log(YLOG_DEBUG, "Closing 1.0 connection");
271 iochan_setflags(i, EVENT_INPUT); // Turns off output flag
275 yaz_log(YLOG_WARN, "Unexpected event on connection");
280 /* Accept a new command connection */
281 static void http_accept(IOCHAN i, int event)
283 struct sockaddr_in addr;
284 int fd = iochan_getfd(i);
289 struct http_channel *ch;
292 if ((s = accept(fd, (struct sockaddr *) &addr, &len)) < 0)
294 yaz_log(YLOG_WARN|YLOG_ERRNO, "accept");
297 if ((flags = fcntl(s, F_GETFL, 0)) < 0)
298 yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl");
299 if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0)
300 yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl2");
302 yaz_log(YLOG_LOG, "New command connection");
303 c = iochan_create(s, http_io, EVENT_INPUT | EVENT_EXCEPT);
305 ch = xmalloc(sizeof(*ch));
307 ch->nmem = nmem_create();
308 ch->wrbuf = wrbuf_alloc();
310 iochan_setdata(c, ch);
312 c->next = channel_list;
317 /* Create a http-channel listener */
318 void http_init(int port)
323 struct sockaddr_in myaddr;
326 yaz_log(YLOG_LOG, "HTTP port is %d", port);
327 if (!(p = getprotobyname("tcp"))) {
330 if ((l = socket(PF_INET, SOCK_STREAM, p->p_proto)) < 0)
331 yaz_log(YLOG_FATAL|YLOG_ERRNO, "socket");
332 if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, (char*)
333 &one, sizeof(one)) < 0)
336 bzero(&myaddr, sizeof myaddr);
337 myaddr.sin_family = AF_INET;
338 myaddr.sin_addr.s_addr = INADDR_ANY;
339 myaddr.sin_port = htons(port);
340 if (bind(l, (struct sockaddr *) &myaddr, sizeof myaddr) < 0)
341 yaz_log(YLOG_FATAL|YLOG_ERRNO, "bind");
342 if (listen(l, SOMAXCONN) < 0)
343 yaz_log(YLOG_FATAL|YLOG_ERRNO, "listen");
345 c = iochan_create(l, http_accept, EVENT_INPUT | EVENT_EXCEPT);
346 c->next = channel_list;
354 * indent-tabs-mode: nil
356 * vim: shiftwidth=4 tabstop=8 expandtab