blob: 4b27225ab96446f8d55b443d17e18dd538846904 [file] [log] [blame]
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001/*
2 * nanohttp.c: minimalist HTTP GET implementation to fetch external subsets.
3 * focuses on size, streamability, reentrancy and portability
4 *
5 * This is clearly not a general purpose HTTP implementation
6 * If you look for one, check:
7 * http://www.w3.org/Library/
8 *
9 * See Copyright for the status of this software.
10 *
11 * Daniel.Veillard@w3.org
12 */
13
14/* TODO add compression support, Send the Accept- , and decompress on the
15 fly with ZLIB if found at compile-time */
16
Daniel Veillard3c558c31999-12-22 11:30:41 +000017#ifdef WIN32
Daniel Veillard0142b842000-01-14 14:45:24 +000018#define INCLUDE_WINSOCK
Daniel Veillard3c558c31999-12-22 11:30:41 +000019#include "win32config.h"
20#else
Daniel Veillard4ecf39f1999-09-22 12:14:03 +000021#include "config.h"
22#endif
23
Daniel Veillard3c558c31999-12-22 11:30:41 +000024
Daniel Veillard4ecf39f1999-09-22 12:14:03 +000025#include <stdio.h>
26#include <string.h>
27
28#ifdef HAVE_STDLIB_H
29#include <stdlib.h>
30#endif
31#ifdef HAVE_UNISTD_H
32#include <unistd.h>
33#endif
34#ifdef HAVE_SYS_SOCKET_H
35#include <sys/socket.h>
36#endif
37#ifdef HAVE_NETINET_IN_H
38#include <netinet/in.h>
39#endif
40#ifdef HAVE_ARPA_INET_H
41#include <arpa/inet.h>
42#endif
43#ifdef HAVE_NETDB_H
44#include <netdb.h>
45#endif
46#ifdef HAVE_FCNTL_H
47#include <fcntl.h>
48#endif
49#ifdef HAVE_ERRNO_H
50#include <errno.h>
51#endif
52#ifdef HAVE_SYS_TIME_H
53#include <sys/time.h>
54#endif
55#ifdef HAVE_SYS_SELECT_H
56#include <sys/select.h>
57#endif
58
59#include "xmlmemory.h"
Daniel Veillard00fdf371999-10-08 09:40:39 +000060#include "nanohttp.h"
Daniel Veillard4ecf39f1999-09-22 12:14:03 +000061
62#ifdef STANDALONE
63#define DEBUG_HTTP
64#endif
65
66#define XML_NANO_HTTP_MAX_REDIR 10
67
68#define XML_NANO_HTTP_CHUNK 4096
69
70#define XML_NANO_HTTP_CLOSED 0
71#define XML_NANO_HTTP_WRITE 1
72#define XML_NANO_HTTP_READ 2
73#define XML_NANO_HTTP_NONE 4
74
75typedef struct xmlNanoHTTPCtxt {
76 char *protocol; /* the protocol name */
77 char *hostname; /* the host name */
78 int port; /* the port */
79 char *path; /* the path within the URL */
80 int fd; /* the file descriptor for the socket */
81 int state; /* WRITE / READ / CLOSED */
82 char *out; /* buffer sent (zero terminated) */
83 char *outptr; /* index within the buffer sent */
84 char *in; /* the receiving buffer */
85 char *content; /* the start of the content */
86 char *inptr; /* the next byte to read from network */
87 char *inrptr; /* the next byte to give back to the client */
88 int inlen; /* len of the input buffer */
89 int last; /* return code for last operation */
90 int returnValue; /* the protocol return value */
91 char *contentType; /* the MIME type for the input */
92 char *location; /* the new URL in case of redirect */
93} xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;
94
95/**
96 * xmlNanoHTTPScanURL:
97 * @ctxt: an HTTP context
98 * @URL: The URL used to initialize the context
99 *
100 * (Re)Initialize an HTTP context by parsing the URL and finding
101 * the protocol host port and path it indicates.
102 */
103
104static void
105xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) {
106 const char *cur = URL;
107 char buf[4096];
108 int index = 0;
109 int port = 0;
110
111 if (ctxt->protocol != NULL) {
112 xmlFree(ctxt->protocol);
113 ctxt->protocol = NULL;
114 }
115 if (ctxt->hostname != NULL) {
116 xmlFree(ctxt->hostname);
117 ctxt->hostname = NULL;
118 }
119 if (ctxt->path != NULL) {
120 xmlFree(ctxt->path);
121 ctxt->path = NULL;
122 }
123 buf[index] = 0;
124 while (*cur != 0) {
125 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
126 buf[index] = 0;
127 ctxt->protocol = xmlMemStrdup(buf);
128 index = 0;
129 cur += 3;
130 break;
131 }
132 buf[index++] = *cur++;
133 }
134 if (*cur == 0) return;
135
136 buf[index] = 0;
137 while (1) {
138 if (cur[0] == ':') {
139 buf[index] = 0;
140 ctxt->hostname = xmlMemStrdup(buf);
141 index = 0;
142 cur += 1;
143 while ((*cur >= '0') && (*cur <= '9')) {
144 port *= 10;
145 port += *cur - '0';
146 cur++;
147 }
148 if (port != 0) ctxt->port = port;
149 while ((cur[0] != '/') && (*cur != 0))
150 cur++;
151 break;
152 }
153 if ((*cur == '/') || (*cur == 0)) {
154 buf[index] = 0;
155 ctxt->hostname = xmlMemStrdup(buf);
156 index = 0;
157 break;
158 }
159 buf[index++] = *cur++;
160 }
161 if (*cur == 0)
162 ctxt->path = xmlMemStrdup("/");
163 else {
164 buf[index] = 0;
165 while (*cur != 0) {
166 if ((cur[0] == '#') || (cur[0] == '?'))
167 break;
168 buf[index++] = *cur++;
169 }
170 buf[index] = 0;
171 ctxt->path = xmlMemStrdup(buf);
172 }
173}
174
175/**
176 * xmlNanoHTTPNewCtxt:
177 * @URL: The URL used to initialize the context
178 *
179 * Allocate and initialize a new HTTP context.
180 *
181 * Returns an HTTP context or NULL in case of error.
182 */
183
184static xmlNanoHTTPCtxtPtr
185xmlNanoHTTPNewCtxt(const char *URL) {
186 xmlNanoHTTPCtxtPtr ret;
187
188 ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt));
189 if (ret == NULL) return(NULL);
190
191 memset(ret, 0, sizeof(xmlNanoHTTPCtxt));
192 ret->port = 80;
193 ret->returnValue = 0;
194
195 xmlNanoHTTPScanURL(ret, URL);
196
197 return(ret);
198}
199
200/**
201 * xmlNanoHTTPFreeCtxt:
202 * @ctxt: an HTTP context
203 *
204 * Frees the context after closing the connection.
205 */
206
207static void
208xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) {
209 if (ctxt == NULL) return;
210 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
211 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
212 if (ctxt->path != NULL) xmlFree(ctxt->path);
213 if (ctxt->out != NULL) xmlFree(ctxt->out);
214 if (ctxt->in != NULL) xmlFree(ctxt->in);
215 if (ctxt->contentType != NULL) xmlFree(ctxt->contentType);
216 if (ctxt->location != NULL) xmlFree(ctxt->location);
217 ctxt->state = XML_NANO_HTTP_NONE;
218 if (ctxt->fd >= 0) close(ctxt->fd);
219 ctxt->fd = -1;
220 xmlFree(ctxt);
221}
222
223/**
224 * xmlNanoHTTPSend:
225 * @ctxt: an HTTP context
226 *
227 * Send the input needed to initiate the processing on the server side
228 */
229
230static void
231xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt) {
232 if (ctxt->state & XML_NANO_HTTP_WRITE)
233 ctxt->last = write(ctxt->fd, ctxt->outptr, strlen(ctxt->outptr));
234}
235
236/**
237 * xmlNanoHTTPRecv:
238 * @ctxt: an HTTP context
239 *
240 * Read information coming from the HTTP connection.
241 * This is a blocking call (but it blocks in select(), not read()).
242 *
243 * Returns the number of byte read or -1 in case of error.
244 */
245
246static int
247xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt) {
248 fd_set rfd;
249 struct timeval tv;
250
251
252 while (ctxt->state & XML_NANO_HTTP_READ) {
253 if (ctxt->in == NULL) {
254 ctxt->in = (char *) xmlMalloc(65000 * sizeof(char));
255 if (ctxt->in == NULL) {
256 ctxt->last = -1;
257 return(-1);
258 }
259 ctxt->inlen = 65000;
260 ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in;
261 }
262 if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) {
263 int delta = ctxt->inrptr - ctxt->in;
264 int len = ctxt->inptr - ctxt->inrptr;
265
266 memmove(ctxt->in, ctxt->inrptr, len);
267 ctxt->inrptr -= delta;
268 ctxt->content -= delta;
269 ctxt->inptr -= delta;
270 }
271 if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) {
272 int d_inptr = ctxt->inptr - ctxt->in;
273 int d_content = ctxt->content - ctxt->in;
274 int d_inrptr = ctxt->inrptr - ctxt->in;
275
276 ctxt->inlen *= 2;
277 ctxt->in = (char *) xmlRealloc(ctxt->in, ctxt->inlen);
278 if (ctxt->in == NULL) {
279 ctxt->last = -1;
280 return(-1);
281 }
282 ctxt->inptr = ctxt->in + d_inptr;
283 ctxt->content = ctxt->in + d_content;
284 ctxt->inrptr = ctxt->in + d_inrptr;
285 }
286 ctxt->last = read(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK);
287 if (ctxt->last > 0) {
288 ctxt->inptr += ctxt->last;
289 return(ctxt->last);
290 }
291 if (ctxt->last == 0) {
292 return(0);
293 }
294#ifdef EWOULDBLOCK
295 if ((ctxt->last == -1) && (errno != EWOULDBLOCK)) {
296 return(0);
297 }
298#endif
299 tv.tv_sec=10;
300 tv.tv_usec=0;
301 FD_ZERO(&rfd);
302 FD_SET(ctxt->fd, &rfd);
303
304 if(select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1)
305 return(0);
306 }
307 return(0);
308}
309
310/**
311 * xmlNanoHTTPReadLine:
312 * @ctxt: an HTTP context
313 *
314 * Read one line in the HTTP server output, usually for extracting
315 * the HTTP protocol informations from the answer header.
316 *
317 * Returns a newly allocated string with a copy of the line, or NULL
318 * which indicate the end of the input.
319 */
320
321static char *
322xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) {
323 char buf[4096];
324 char *bp=buf;
325
326 while(bp - buf < 4095) {
327 if(ctxt->inrptr == ctxt->inptr) {
328 if (xmlNanoHTTPRecv(ctxt) == 0) {
329 if (bp == buf)
330 return(NULL);
331 else
332 *bp = 0;
333 return(xmlMemStrdup(buf));
334 }
335 }
336 *bp = *ctxt->inrptr++;
337 if(*bp == '\n') {
338 *bp = 0;
339 return(xmlMemStrdup(buf));
340 }
341 if(*bp != '\r')
342 bp++;
343 }
344 buf[4095] = 0;
345 return(xmlMemStrdup(buf));
346}
347
348
349/**
350 * xmlNanoHTTPScanAnswer:
351 * @ctxt: an HTTP context
352 * @line: an HTTP header line
353 *
354 * Try to extract useful informations from the server answer.
355 * We currently parse and process:
356 * - The HTTP revision/ return code
357 * - The Content-Type
358 * - The Location for redirrect processing.
359 *
360 * Returns -1 in case of failure, the file descriptor number otherwise
361 */
362
363static void
364xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) {
365 const char *cur = line;
366
367 if (line == NULL) return;
368
369 if (!strncmp(line, "HTTP/", 5)) {
370 int version = 0;
371 int ret = 0;
372
373 cur += 5;
374 while ((*cur >= '0') && (*cur <= '9')) {
375 version *= 10;
376 version += *cur - '0';
377 cur++;
378 }
379 if (*cur == '.') {
380 cur++;
381 if ((*cur >= '0') && (*cur <= '9')) {
382 version *= 10;
383 version += *cur - '0';
384 cur++;
385 }
386 while ((*cur >= '0') && (*cur <= '9'))
387 cur++;
388 } else
389 version *= 10;
390 if ((*cur != ' ') && (*cur != '\t')) return;
391 while ((*cur == ' ') || (*cur == '\t')) cur++;
392 if ((*cur < '0') || (*cur > '9')) return;
393 while ((*cur >= '0') && (*cur <= '9')) {
394 ret *= 10;
395 ret += *cur - '0';
396 cur++;
397 }
398 if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return;
399 ctxt->returnValue = ret;
400 } else if (!strncmp(line, "Content-Type:", 13)) {
401 cur += 13;
402 while ((*cur == ' ') || (*cur == '\t')) cur++;
403 if (ctxt->contentType != NULL)
404 xmlFree(ctxt->contentType);
405 ctxt->contentType = xmlMemStrdup(cur);
406 } else if (!strncmp(line, "ContentType:", 12)) {
407 cur += 12;
408 if (ctxt->contentType != NULL) return;
409 while ((*cur == ' ') || (*cur == '\t')) cur++;
410 ctxt->contentType = xmlMemStrdup(cur);
411 } else if (!strncmp(line, "content-type:", 13)) {
412 cur += 13;
413 if (ctxt->contentType != NULL) return;
414 while ((*cur == ' ') || (*cur == '\t')) cur++;
415 ctxt->contentType = xmlMemStrdup(cur);
416 } else if (!strncmp(line, "contenttype:", 12)) {
417 cur += 12;
418 if (ctxt->contentType != NULL) return;
419 while ((*cur == ' ') || (*cur == '\t')) cur++;
420 ctxt->contentType = xmlMemStrdup(cur);
421 } else if (!strncmp(line, "Location:", 9)) {
422 cur += 9;
423 while ((*cur == ' ') || (*cur == '\t')) cur++;
424 if (ctxt->location != NULL)
425 xmlFree(ctxt->location);
426 ctxt->location = xmlMemStrdup(cur);
427 } else if (!strncmp(line, "location:", 9)) {
428 cur += 9;
429 if (ctxt->location != NULL) return;
430 while ((*cur == ' ') || (*cur == '\t')) cur++;
431 ctxt->location = xmlMemStrdup(cur);
432 }
433}
434
435/**
436 * xmlNanoHTTPConnectAttempt:
437 * @ia: an internet adress structure
438 * @port: the port number
439 *
440 * Attempt a connection to the given IP:port endpoint. It forces
441 * non-blocking semantic on the socket, and allow 60 seconds for
442 * the host to answer.
443 *
444 * Returns -1 in case of failure, the file descriptor number otherwise
445 */
446
447static int
448xmlNanoHTTPConnectAttempt(struct in_addr ia, int port)
449{
450 int s=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
451 struct sockaddr_in sin;
452 fd_set wfd;
453 struct timeval tv;
454 int status;
455
456 if(s==-1) {
457#ifdef DEBUG_HTTP
458 perror("socket");
459#endif
460 return(-1);
461 }
462
463#ifdef _WINSOCKAPI_
464 {
465 long levents = FD_READ | FD_WRITE | FD_ACCEPT |
466 FD_CONNECT | FD_CLOSE ;
467 int rv = 0 ;
468 u_long one = 1;
469
470 status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0;
471 }
472#else /* _WINSOCKAPI_ */
473#if defined(VMS)
474 {
475 int enable = 1;
476 status = IOCTL(s, FIONBIO, &enable);
477 }
478#else /* VMS */
479 if((status = fcntl(s, F_GETFL, 0)) != -1) {
480#ifdef O_NONBLOCK
481 status |= O_NONBLOCK;
482#else /* O_NONBLOCK */
483#ifdef F_NDELAY
484 status |= F_NDELAY;
485#endif /* F_NDELAY */
486#endif /* !O_NONBLOCK */
487 status = fcntl(s, F_SETFL, status);
488 }
489 if(status < 0) {
490#ifdef DEBUG_HTTP
491 perror("nonblocking");
492#endif
493 close(s);
494 return(-1);
495 }
496#endif /* !VMS */
497#endif /* !_WINSOCKAPI_ */
498
499
500 sin.sin_family = AF_INET;
501 sin.sin_addr = ia;
502 sin.sin_port = htons(port);
503
504 if((connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1) &&
505 (errno != EINPROGRESS)) {
506 perror("connect");
507 close(s);
508 return(-1);
509 }
510
511 tv.tv_sec = 60; /* We use 60 second timeouts for now */
512 tv.tv_usec = 0;
513
514 FD_ZERO(&wfd);
515 FD_SET(s, &wfd);
516
517 switch(select(s+1, NULL, &wfd, NULL, &tv))
518 {
519 case 0:
520 /* Time out */
521 close(s);
522 return(-1);
523 case -1:
524 /* Ermm.. ?? */
525#ifdef DEBUG_HTTP
526 perror("select");
527#endif
528 close(s);
529 return(-1);
530 }
531
532 return(s);
533}
534
535/**
536 * xmlNanoHTTPConnectHost:
537 * @host: the host name
538 * @port: the port number
539 *
540 * Attempt a connection to the given host:port endpoint. It tries
541 * the multiple IP provided by the DNS if available.
542 *
543 * Returns -1 in case of failure, the file descriptor number otherwise
544 */
545
546static int
547xmlNanoHTTPConnectHost(const char *host, int port)
548{
549 struct hostent *h;
550 int i;
551 int s;
552
553 h=gethostbyname(host);
554 if(h==NULL)
555 {
556#ifdef DEBUG_HTTP
557 fprintf(stderr,"unable to resolve '%s'.\n", host);
558#endif
559 return(-1);
560 }
561
562 for(i=0; h->h_addr_list[i]; i++)
563 {
564 struct in_addr ia;
565 memcpy(&ia, h->h_addr_list[i],4);
566 s = xmlNanoHTTPConnectAttempt(ia, port);
567 if(s != -1)
568 return(s);
569 }
570
571#ifdef DEBUG_HTTP
572 fprintf(stderr, "unable to connect to '%s'.\n", host);
573#endif
574 return(-1);
575}
576
577
578/**
579 * xmlNanoHTTPOpen:
580 * @URL: The URL to load
581 * @contentType: if available the Content-Type information will be
582 * returned at that location
583 *
584 * This function try to open a connection to the indicated resource
585 * via HTTP GET.
586 *
587 * Returns NULL in case of failure, otherwise a request handler.
588 * The contentType, if provided must be freed by the caller
589 */
590
591void *
592xmlNanoHTTPOpen(const char *URL, char **contentType) {
593 xmlNanoHTTPCtxtPtr ctxt;
594 char buf[4096];
595 int ret;
596 char *p;
597 int head;
598 int nbRedirects = 0;
599 char *redirURL = NULL;
600
601 if (contentType != NULL) *contentType = NULL;
602
603retry:
604 if (redirURL == NULL)
605 ctxt = xmlNanoHTTPNewCtxt(URL);
606 else {
607 ctxt = xmlNanoHTTPNewCtxt(redirURL);
608 xmlFree(redirURL);
609 redirURL = NULL;
610 }
611
612 if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
613 xmlNanoHTTPFreeCtxt(ctxt);
614 if (redirURL != NULL) xmlFree(redirURL);
615 return(NULL);
616 }
617 if (ctxt->hostname == NULL) {
618 xmlNanoHTTPFreeCtxt(ctxt);
619 return(NULL);
620 }
621 ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
622 if (ret < 0) {
623 xmlNanoHTTPFreeCtxt(ctxt);
624 return(NULL);
625 }
626 ctxt->fd = ret;
Daniel Veillard335849b1999-09-23 23:08:42 +0000627#ifdef HAVE_SNPRINTF
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000628 snprintf(buf, sizeof(buf),"GET %s HTTP/1.0\r\nHost: %s\r\n\r\n",
629 ctxt->path, ctxt->hostname);
Daniel Veillard335849b1999-09-23 23:08:42 +0000630#else
631 sprintf(buf, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n",
632 ctxt->path, ctxt->hostname);
633#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000634#ifdef DEBUG_HTTP
635 printf("-> GET %s HTTP/1.0\n-> Host: %s\n\n",
636 ctxt->path, ctxt->hostname);
637#endif
638 ctxt->outptr = ctxt->out = xmlMemStrdup(buf);
639 ctxt->state = XML_NANO_HTTP_WRITE;
640 xmlNanoHTTPSend(ctxt);
641 ctxt->state = XML_NANO_HTTP_READ;
642 head = 1;
643
644 while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
645 if (head && (*p == 0)) {
646 head = 0;
647 ctxt->content = ctxt->inrptr;
648 break;
649 }
650 xmlNanoHTTPScanAnswer(ctxt, p);
651
652#ifdef DEBUG_HTTP
653 if (p != NULL) printf("<- %s\n", p);
654#endif
655 if (p != NULL) xmlFree(p);
656 }
657
658 if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
659 (ctxt->returnValue < 400)) {
660#ifdef DEBUG_HTTP
661 printf("\nRedirect to: %s\n", ctxt->location);
662#endif
663 while (xmlNanoHTTPRecv(ctxt)) ;
664 if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
665 nbRedirects++;
666 redirURL = xmlMemStrdup(ctxt->location);
667 xmlNanoHTTPFreeCtxt(ctxt);
668 goto retry;
669 }
670 xmlNanoHTTPFreeCtxt(ctxt);
671#ifdef DEBUG_HTTP
672 printf("Too many redirrects, aborting ...\n");
673#endif
674 return(NULL);
675
676 }
677
678 if ((contentType != NULL) && (ctxt->contentType != NULL))
679 *contentType = xmlMemStrdup(ctxt->contentType);
680
681#ifdef DEBUG_HTTP
682 if (ctxt->contentType != NULL)
683 printf("\nCode %d, content-type '%s'\n\n",
684 ctxt->returnValue, ctxt->contentType);
685 else
686 printf("\nCode %d, no content-type\n\n",
687 ctxt->returnValue);
688#endif
689
690 return((void *) ctxt);
691}
692
693/**
694 * xmlNanoHTTPRead:
695 * @ctx: the HTTP context
696 * @dest: a buffer
697 * @len: the buffer length
698 *
699 * This function tries to read @len bytes from the existing HTTP connection
700 * and saves them in @dest. This is a blocking call.
701 *
702 * Returns the number of byte read. 0 is an indication of an end of connection.
703 * -1 indicates a parameter error.
704 */
705int
706xmlNanoHTTPRead(void *ctx, void *dest, int len) {
707 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
708
709 if (ctx == NULL) return(-1);
710 if (dest == NULL) return(-1);
711 if (len <= 0) return(0);
712
713 while (ctxt->inptr - ctxt->inrptr < len) {
714 if (xmlNanoHTTPRecv(ctxt) == 0) break;
715 }
716 if (ctxt->inptr - ctxt->inrptr < len)
717 len = ctxt->inptr - ctxt->inrptr;
718 memcpy(dest, ctxt->inrptr, len);
719 ctxt->inrptr += len;
720 return(len);
721}
722
723/**
724 * xmlNanoHTTPClose:
725 * @ctx: the HTTP context
726 *
727 * This function closes an HTTP context, it ends up the connection and
728 * free all data related to it.
729 */
730void
731xmlNanoHTTPClose(void *ctx) {
732 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
733
734 if (ctx == NULL) return;
735
736 xmlNanoHTTPFreeCtxt(ctxt);
737}
738
Daniel Veillard00fdf371999-10-08 09:40:39 +0000739#ifndef DEBUG_HTTP
740#define DEBUG_HTTP
741#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000742/**
743 * xmlNanoHTTPMethod:
744 * @URL: The URL to load
745 * @method: the HTTP method to use
746 * @input: the input string if any
747 * @contentType: the Content-Type information IN and OUT
748 * @headers: the extra headers
749 *
750 * This function try to open a connection to the indicated resource
751 * via HTTP using the given @method, adding the given extra headers
752 * and the input buffer for the request content.
753 *
754 * Returns NULL in case of failure, otherwise a request handler.
755 * The contentType, if provided must be freed by the caller
756 */
757
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000758void *
759xmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
760 char **contentType, const char *headers) {
761 xmlNanoHTTPCtxtPtr ctxt;
762 char buf[20000];
763 int ret;
764 char *p;
765 int head;
766 int nbRedirects = 0;
767 char *redirURL = NULL;
768
769 if (URL == NULL) return(NULL);
770 if (method == NULL) method = "GET";
771 if (contentType != NULL) *contentType = NULL;
772
773retry:
774 if (redirURL == NULL)
775 ctxt = xmlNanoHTTPNewCtxt(URL);
776 else {
777 ctxt = xmlNanoHTTPNewCtxt(redirURL);
778 xmlFree(redirURL);
779 redirURL = NULL;
780 }
781
782 if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
783 xmlNanoHTTPFreeCtxt(ctxt);
784 if (redirURL != NULL) xmlFree(redirURL);
785 return(NULL);
786 }
787 if (ctxt->hostname == NULL) {
788 xmlNanoHTTPFreeCtxt(ctxt);
789 return(NULL);
790 }
791 ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
792 if (ret < 0) {
793 xmlNanoHTTPFreeCtxt(ctxt);
794 return(NULL);
795 }
796 ctxt->fd = ret;
797
798 if (input == NULL) {
799 if (headers == NULL) {
800 if ((contentType == NULL) || (*contentType == NULL)) {
Daniel Veillard335849b1999-09-23 23:08:42 +0000801#ifdef HAVE_SNPRINTF
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000802 snprintf(buf, sizeof(buf),
803 "%s %s HTTP/1.0\r\nHost: %s\r\n\r\n",
804 method, ctxt->path, ctxt->hostname);
Daniel Veillard335849b1999-09-23 23:08:42 +0000805#else
806 sprintf(buf,
807 "%s %s HTTP/1.0\r\nHost: %s\r\n\r\n",
808 method, ctxt->path, ctxt->hostname);
809#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000810 } else {
Daniel Veillard335849b1999-09-23 23:08:42 +0000811#ifdef HAVE_SNPRINTF
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000812 snprintf(buf, sizeof(buf),
813 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n\r\n",
814 method, ctxt->path, ctxt->hostname, *contentType);
Daniel Veillard335849b1999-09-23 23:08:42 +0000815#else
816 sprintf(buf,
817 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n\r\n",
818 method, ctxt->path, ctxt->hostname, *contentType);
819#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000820 }
821 } else {
822 if ((contentType == NULL) || (*contentType == NULL)) {
Daniel Veillard335849b1999-09-23 23:08:42 +0000823#ifdef HAVE_SNPRINTF
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000824 snprintf(buf, sizeof(buf),
825 "%s %s HTTP/1.0\r\nHost: %s\r\n%s\r\n",
826 method, ctxt->path, ctxt->hostname, headers);
Daniel Veillard335849b1999-09-23 23:08:42 +0000827#else
828 sprintf(buf,
829 "%s %s HTTP/1.0\r\nHost: %s\r\n%s\r\n",
830 method, ctxt->path, ctxt->hostname, headers);
831#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000832 } else {
Daniel Veillard335849b1999-09-23 23:08:42 +0000833#ifdef HAVE_SNPRINTF
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000834 snprintf(buf, sizeof(buf),
835 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n%s\r\n",
836 method, ctxt->path, ctxt->hostname, *contentType,
837 headers);
Daniel Veillard335849b1999-09-23 23:08:42 +0000838#else
839 sprintf(buf,
840 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n%s\r\n",
841 method, ctxt->path, ctxt->hostname, *contentType,
842 headers);
843#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000844 }
845 }
846 } else {
847 int len = strlen(input);
848 if (headers == NULL) {
849 if ((contentType == NULL) || (*contentType == NULL)) {
Daniel Veillard335849b1999-09-23 23:08:42 +0000850#ifdef HAVE_SNPRINTF
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000851 snprintf(buf, sizeof(buf),
852 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s",
853 method, ctxt->path, ctxt->hostname, len, input);
Daniel Veillard335849b1999-09-23 23:08:42 +0000854#else
855 sprintf(buf,
856 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s",
857 method, ctxt->path, ctxt->hostname, len, input);
858#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000859 } else {
Daniel Veillard335849b1999-09-23 23:08:42 +0000860#ifdef HAVE_SNPRINTF
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000861 snprintf(buf, sizeof(buf),
862"%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n%s",
863 method, ctxt->path, ctxt->hostname, *contentType, len,
864 input);
Daniel Veillard335849b1999-09-23 23:08:42 +0000865#else
866 sprintf(buf,
867"%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n%s",
868 method, ctxt->path, ctxt->hostname, *contentType, len,
869 input);
870#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000871 }
872 } else {
873 if ((contentType == NULL) || (*contentType == NULL)) {
Daniel Veillard335849b1999-09-23 23:08:42 +0000874#ifdef HAVE_SNPRINTF
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000875 snprintf(buf, sizeof(buf),
876 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n%s\r\n%s",
877 method, ctxt->path, ctxt->hostname, len,
878 headers, input);
Daniel Veillard335849b1999-09-23 23:08:42 +0000879#else
880 sprintf(buf,
881 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n%s\r\n%s",
882 method, ctxt->path, ctxt->hostname, len,
883 headers, input);
884#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000885 } else {
Daniel Veillard335849b1999-09-23 23:08:42 +0000886#ifdef HAVE_SNPRINTF
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000887 snprintf(buf, sizeof(buf),
888"%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n%s\r\n%s",
889 method, ctxt->path, ctxt->hostname, *contentType,
890 len, headers, input);
Daniel Veillard335849b1999-09-23 23:08:42 +0000891#else
892 sprintf(buf,
893"%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n%s\r\n%s",
894 method, ctxt->path, ctxt->hostname, *contentType,
895 len, headers, input);
896#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000897 }
898 }
899 }
900#ifdef DEBUG_HTTP
901 printf("-> %s", buf);
902#endif
903 ctxt->outptr = ctxt->out = xmlMemStrdup(buf);
904 ctxt->state = XML_NANO_HTTP_WRITE;
905 xmlNanoHTTPSend(ctxt);
906 ctxt->state = XML_NANO_HTTP_READ;
907 head = 1;
908
909 while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
910 if (head && (*p == 0)) {
911 head = 0;
912 ctxt->content = ctxt->inrptr;
913 if (p != NULL) xmlFree(p);
914 break;
915 }
916 xmlNanoHTTPScanAnswer(ctxt, p);
917
918#ifdef DEBUG_HTTP
919 if (p != NULL) printf("<- %s\n", p);
920#endif
921 if (p != NULL) xmlFree(p);
922 }
923
924 if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
925 (ctxt->returnValue < 400)) {
926#ifdef DEBUG_HTTP
927 printf("\nRedirect to: %s\n", ctxt->location);
928#endif
929 while (xmlNanoHTTPRecv(ctxt)) ;
930 if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
931 nbRedirects++;
932 redirURL = xmlMemStrdup(ctxt->location);
933 xmlNanoHTTPFreeCtxt(ctxt);
934 goto retry;
935 }
936 xmlNanoHTTPFreeCtxt(ctxt);
937#ifdef DEBUG_HTTP
938 printf("Too many redirrects, aborting ...\n");
939#endif
940 return(NULL);
941
942 }
943
944 if ((contentType != NULL) && (ctxt->contentType != NULL))
945 *contentType = xmlMemStrdup(ctxt->contentType);
946 else if (contentType != NULL)
947 *contentType = NULL;
948
949#ifdef DEBUG_HTTP
950 if (ctxt->contentType != NULL)
951 printf("\nCode %d, content-type '%s'\n\n",
952 ctxt->returnValue, ctxt->contentType);
953 else
954 printf("\nCode %d, no content-type\n\n",
955 ctxt->returnValue);
956#endif
957
958 return((void *) ctxt);
959}
960
961/**
962 * xmlNanoHTTPFetch:
963 * @URL: The URL to load
964 * @filename: the filename where the content should be saved
965 * @contentType: if available the Content-Type information will be
966 * returned at that location
967 *
968 * This function try to fetch the indicated resource via HTTP GET
969 * and save it's content in the file.
970 *
971 * Returns -1 in case of failure, 0 incase of success. The contentType,
972 * if provided must be freed by the caller
973 */
974int
975xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
976 void *ctxt;
977 char buf[4096];
978 int fd;
979 int len;
980
981 ctxt = xmlNanoHTTPOpen(URL, contentType);
982 if (ctxt == NULL) return(-1);
983
984 if (!strcmp(filename, "-"))
985 fd = 0;
986 else {
987 fd = open(filename, O_CREAT | O_WRONLY);
988 if (fd < 0) {
989 xmlNanoHTTPClose(ctxt);
990 if ((contentType != NULL) && (*contentType != NULL)) {
991 xmlFree(*contentType);
992 *contentType = NULL;
993 }
994 return(-1);
995 }
996 }
997
998 while ((len = xmlNanoHTTPRead(ctxt, buf, sizeof(buf))) > 0) {
999 write(fd, buf, len);
1000 }
1001
1002 xmlNanoHTTPClose(ctxt);
1003 return(0);
1004}
1005
1006/**
1007 * xmlNanoHTTPSave:
Daniel Veillard00fdf371999-10-08 09:40:39 +00001008 * @ctxt: the HTTP context
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001009 * @filename: the filename where the content should be saved
1010 *
1011 * This function saves the output of the HTTP transaction to a file
1012 * It closes and free the context at the end
1013 *
1014 * Returns -1 in case of failure, 0 incase of success.
1015 */
1016int
1017xmlNanoHTTPSave(void *ctxt, const char *filename) {
1018 char buf[4096];
1019 int fd;
1020 int len;
1021
1022 if (ctxt == NULL) return(-1);
1023
1024 if (!strcmp(filename, "-"))
1025 fd = 0;
1026 else {
1027 fd = open(filename, O_CREAT | O_WRONLY);
1028 if (fd < 0) {
1029 xmlNanoHTTPClose(ctxt);
1030 return(-1);
1031 }
1032 }
1033
1034 while ((len = xmlNanoHTTPRead(ctxt, buf, sizeof(buf))) > 0) {
1035 write(fd, buf, len);
1036 }
1037
1038 xmlNanoHTTPClose(ctxt);
1039 return(0);
1040}
1041
1042/**
1043 * xmlNanoHTTPReturnCode:
1044 * @ctx: the HTTP context
1045 *
1046 * Returns the HTTP return code for the request.
1047 */
1048int
1049xmlNanoHTTPReturnCode(void *ctx) {
1050 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1051
1052 if (ctxt == NULL) return(-1);
1053
1054 return(ctxt->returnValue);
1055}
1056
1057#ifdef STANDALONE
1058int main(int argc, char **argv) {
1059 char *contentType = NULL;
1060
1061 if (argv[1] != NULL) {
1062 if (argv[2] != NULL)
1063 xmlNanoHTTPFetch(argv[1], argv[2], &contentType);
1064 else
1065 xmlNanoHTTPFetch(argv[1], "-", &contentType);
1066 if (contentType != NULL) xmlFree(contentType);
1067 } else {
1068 printf("%s: minimal HTTP GET implementation\n", argv[0]);
1069 printf("\tusage %s [ URL [ filename ] ]\n", argv[0]);
1070 }
1071 return(0);
1072}
1073#endif /* STANDALONE */