blob: 33d0b7ae3051dd4ceafd43c9d0cc6f717525adc1 [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 Veillard361d8452000-04-03 19:48:13 +000024#include "xmlversion.h"
Daniel Veillard3c558c31999-12-22 11:30:41 +000025
Daniel Veillard361d8452000-04-03 19:48:13 +000026#ifdef LIBXML_HTTP_ENABLED
Daniel Veillard4ecf39f1999-09-22 12:14:03 +000027#include <stdio.h>
28#include <string.h>
29
30#ifdef HAVE_STDLIB_H
31#include <stdlib.h>
32#endif
33#ifdef HAVE_UNISTD_H
34#include <unistd.h>
35#endif
36#ifdef HAVE_SYS_SOCKET_H
37#include <sys/socket.h>
38#endif
39#ifdef HAVE_NETINET_IN_H
40#include <netinet/in.h>
41#endif
42#ifdef HAVE_ARPA_INET_H
43#include <arpa/inet.h>
44#endif
45#ifdef HAVE_NETDB_H
46#include <netdb.h>
47#endif
48#ifdef HAVE_FCNTL_H
49#include <fcntl.h>
50#endif
51#ifdef HAVE_ERRNO_H
52#include <errno.h>
53#endif
54#ifdef HAVE_SYS_TIME_H
55#include <sys/time.h>
56#endif
57#ifdef HAVE_SYS_SELECT_H
58#include <sys/select.h>
59#endif
Daniel Veillard5feb8492000-02-02 17:15:36 +000060#ifdef HAVE_STRINGS_H
61#include <strings.h>
62#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +000063
Daniel Veillard361d8452000-04-03 19:48:13 +000064#include <libxml/xmlmemory.h>
65#include <libxml/nanohttp.h>
Daniel Veillard4ecf39f1999-09-22 12:14:03 +000066
67#ifdef STANDALONE
68#define DEBUG_HTTP
69#endif
70
71#define XML_NANO_HTTP_MAX_REDIR 10
72
73#define XML_NANO_HTTP_CHUNK 4096
74
75#define XML_NANO_HTTP_CLOSED 0
76#define XML_NANO_HTTP_WRITE 1
77#define XML_NANO_HTTP_READ 2
78#define XML_NANO_HTTP_NONE 4
79
80typedef struct xmlNanoHTTPCtxt {
81 char *protocol; /* the protocol name */
82 char *hostname; /* the host name */
83 int port; /* the port */
84 char *path; /* the path within the URL */
85 int fd; /* the file descriptor for the socket */
86 int state; /* WRITE / READ / CLOSED */
87 char *out; /* buffer sent (zero terminated) */
88 char *outptr; /* index within the buffer sent */
89 char *in; /* the receiving buffer */
90 char *content; /* the start of the content */
91 char *inptr; /* the next byte to read from network */
92 char *inrptr; /* the next byte to give back to the client */
93 int inlen; /* len of the input buffer */
94 int last; /* return code for last operation */
95 int returnValue; /* the protocol return value */
96 char *contentType; /* the MIME type for the input */
97 char *location; /* the new URL in case of redirect */
98} xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;
99
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000100static int initialized = 0;
101static char *proxy = NULL; /* the proxy name if any */
102static int proxyPort; /* the proxy port if any */
103
104/**
105 * xmlNanoHTTPInit:
106 *
107 * Initialize the HTTP protocol layer.
108 * Currently it just checks for proxy informations
109 */
110
111void
112xmlNanoHTTPInit(void) {
113 const char *env;
114
115 if (initialized)
116 return;
117
118 if (proxy == NULL) {
119 proxyPort = 80;
120 env = getenv("no_proxy");
121 if (env != NULL)
122 goto done;
123 env = getenv("http_proxy");
124 if (env != NULL) {
125 xmlNanoHTTPScanProxy(env);
126 goto done;
127 }
128 env = getenv("HTTP_PROXY");
129 if (env != NULL) {
130 xmlNanoHTTPScanProxy(env);
131 goto done;
132 }
133 }
134done:
135 initialized = 1;
136}
137
138/**
139 * xmlNanoHTTPClenup:
140 *
141 * Cleanup the HTTP protocol layer.
142 */
143
144void
145xmlNanoHTTPCleanup(void) {
146 if (proxy != NULL)
147 xmlFree(proxy);
148 initialized = 0;
149 return;
150}
151
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000152/**
153 * xmlNanoHTTPScanURL:
154 * @ctxt: an HTTP context
155 * @URL: The URL used to initialize the context
156 *
157 * (Re)Initialize an HTTP context by parsing the URL and finding
158 * the protocol host port and path it indicates.
159 */
160
161static void
162xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) {
163 const char *cur = URL;
164 char buf[4096];
165 int index = 0;
166 int port = 0;
167
168 if (ctxt->protocol != NULL) {
169 xmlFree(ctxt->protocol);
170 ctxt->protocol = NULL;
171 }
172 if (ctxt->hostname != NULL) {
173 xmlFree(ctxt->hostname);
174 ctxt->hostname = NULL;
175 }
176 if (ctxt->path != NULL) {
177 xmlFree(ctxt->path);
178 ctxt->path = NULL;
179 }
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000180 if (URL == NULL) return;
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000181 buf[index] = 0;
182 while (*cur != 0) {
183 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
184 buf[index] = 0;
185 ctxt->protocol = xmlMemStrdup(buf);
186 index = 0;
187 cur += 3;
188 break;
189 }
190 buf[index++] = *cur++;
191 }
192 if (*cur == 0) return;
193
194 buf[index] = 0;
195 while (1) {
196 if (cur[0] == ':') {
197 buf[index] = 0;
198 ctxt->hostname = xmlMemStrdup(buf);
199 index = 0;
200 cur += 1;
201 while ((*cur >= '0') && (*cur <= '9')) {
202 port *= 10;
203 port += *cur - '0';
204 cur++;
205 }
206 if (port != 0) ctxt->port = port;
207 while ((cur[0] != '/') && (*cur != 0))
208 cur++;
209 break;
210 }
211 if ((*cur == '/') || (*cur == 0)) {
212 buf[index] = 0;
213 ctxt->hostname = xmlMemStrdup(buf);
214 index = 0;
215 break;
216 }
217 buf[index++] = *cur++;
218 }
219 if (*cur == 0)
220 ctxt->path = xmlMemStrdup("/");
221 else {
Daniel Veillard726e8792000-01-30 20:04:29 +0000222 index = 0;
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000223 buf[index] = 0;
Daniel Veillard726e8792000-01-30 20:04:29 +0000224 while (*cur != 0)
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000225 buf[index++] = *cur++;
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000226 buf[index] = 0;
227 ctxt->path = xmlMemStrdup(buf);
228 }
229}
230
231/**
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000232 * xmlNanoHTTPScanProxy:
233 * @URL: The proxy URL used to initialize the proxy context
234 *
235 * (Re)Initialize the HTTP Proxy context by parsing the URL and finding
236 * the protocol host port it indicates.
237 * Should be like http://myproxy/ or http://myproxy:3128/
238 * A NULL URL cleans up proxy informations.
239 */
240
241void
242xmlNanoHTTPScanProxy(const char *URL) {
243 const char *cur = URL;
244 char buf[4096];
245 int index = 0;
246 int port = 0;
247
248 if (proxy != NULL) {
249 xmlFree(proxy);
250 proxy = NULL;
251 }
252 if (proxyPort != 0) {
253 proxyPort = 0;
254 }
255#ifdef DEBUG_HTTP
256 if (URL == NULL)
257 printf("Removing HTTP proxy info\n");
258 else
259 printf("Using HTTP proxy %s\n", URL);
260#endif
261 if (URL == NULL) return;
262 buf[index] = 0;
263 while (*cur != 0) {
264 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
265 buf[index] = 0;
266 index = 0;
267 cur += 3;
268 break;
269 }
270 buf[index++] = *cur++;
271 }
272 if (*cur == 0) return;
273
274 buf[index] = 0;
275 while (1) {
276 if (cur[0] == ':') {
277 buf[index] = 0;
278 proxy = xmlMemStrdup(buf);
279 index = 0;
280 cur += 1;
281 while ((*cur >= '0') && (*cur <= '9')) {
282 port *= 10;
283 port += *cur - '0';
284 cur++;
285 }
286 if (port != 0) proxyPort = port;
287 while ((cur[0] != '/') && (*cur != 0))
288 cur++;
289 break;
290 }
291 if ((*cur == '/') || (*cur == 0)) {
292 buf[index] = 0;
293 proxy = xmlMemStrdup(buf);
294 index = 0;
295 break;
296 }
297 buf[index++] = *cur++;
298 }
299}
300
301/**
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000302 * xmlNanoHTTPNewCtxt:
303 * @URL: The URL used to initialize the context
304 *
305 * Allocate and initialize a new HTTP context.
306 *
307 * Returns an HTTP context or NULL in case of error.
308 */
309
310static xmlNanoHTTPCtxtPtr
311xmlNanoHTTPNewCtxt(const char *URL) {
312 xmlNanoHTTPCtxtPtr ret;
313
314 ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt));
315 if (ret == NULL) return(NULL);
316
317 memset(ret, 0, sizeof(xmlNanoHTTPCtxt));
318 ret->port = 80;
319 ret->returnValue = 0;
320
321 xmlNanoHTTPScanURL(ret, URL);
322
323 return(ret);
324}
325
326/**
327 * xmlNanoHTTPFreeCtxt:
328 * @ctxt: an HTTP context
329 *
330 * Frees the context after closing the connection.
331 */
332
333static void
334xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) {
335 if (ctxt == NULL) return;
336 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
337 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
338 if (ctxt->path != NULL) xmlFree(ctxt->path);
339 if (ctxt->out != NULL) xmlFree(ctxt->out);
340 if (ctxt->in != NULL) xmlFree(ctxt->in);
341 if (ctxt->contentType != NULL) xmlFree(ctxt->contentType);
342 if (ctxt->location != NULL) xmlFree(ctxt->location);
343 ctxt->state = XML_NANO_HTTP_NONE;
344 if (ctxt->fd >= 0) close(ctxt->fd);
345 ctxt->fd = -1;
346 xmlFree(ctxt);
347}
348
349/**
350 * xmlNanoHTTPSend:
351 * @ctxt: an HTTP context
352 *
353 * Send the input needed to initiate the processing on the server side
354 */
355
356static void
357xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt) {
358 if (ctxt->state & XML_NANO_HTTP_WRITE)
359 ctxt->last = write(ctxt->fd, ctxt->outptr, strlen(ctxt->outptr));
360}
361
362/**
363 * xmlNanoHTTPRecv:
364 * @ctxt: an HTTP context
365 *
366 * Read information coming from the HTTP connection.
367 * This is a blocking call (but it blocks in select(), not read()).
368 *
369 * Returns the number of byte read or -1 in case of error.
370 */
371
372static int
373xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt) {
374 fd_set rfd;
375 struct timeval tv;
376
377
378 while (ctxt->state & XML_NANO_HTTP_READ) {
379 if (ctxt->in == NULL) {
380 ctxt->in = (char *) xmlMalloc(65000 * sizeof(char));
381 if (ctxt->in == NULL) {
382 ctxt->last = -1;
383 return(-1);
384 }
385 ctxt->inlen = 65000;
386 ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in;
387 }
388 if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) {
389 int delta = ctxt->inrptr - ctxt->in;
390 int len = ctxt->inptr - ctxt->inrptr;
391
392 memmove(ctxt->in, ctxt->inrptr, len);
393 ctxt->inrptr -= delta;
394 ctxt->content -= delta;
395 ctxt->inptr -= delta;
396 }
397 if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) {
398 int d_inptr = ctxt->inptr - ctxt->in;
399 int d_content = ctxt->content - ctxt->in;
400 int d_inrptr = ctxt->inrptr - ctxt->in;
401
402 ctxt->inlen *= 2;
403 ctxt->in = (char *) xmlRealloc(ctxt->in, ctxt->inlen);
404 if (ctxt->in == NULL) {
405 ctxt->last = -1;
406 return(-1);
407 }
408 ctxt->inptr = ctxt->in + d_inptr;
409 ctxt->content = ctxt->in + d_content;
410 ctxt->inrptr = ctxt->in + d_inrptr;
411 }
412 ctxt->last = read(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK);
413 if (ctxt->last > 0) {
414 ctxt->inptr += ctxt->last;
415 return(ctxt->last);
416 }
417 if (ctxt->last == 0) {
418 return(0);
419 }
420#ifdef EWOULDBLOCK
421 if ((ctxt->last == -1) && (errno != EWOULDBLOCK)) {
422 return(0);
423 }
424#endif
425 tv.tv_sec=10;
426 tv.tv_usec=0;
427 FD_ZERO(&rfd);
428 FD_SET(ctxt->fd, &rfd);
429
430 if(select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1)
431 return(0);
432 }
433 return(0);
434}
435
436/**
437 * xmlNanoHTTPReadLine:
438 * @ctxt: an HTTP context
439 *
440 * Read one line in the HTTP server output, usually for extracting
441 * the HTTP protocol informations from the answer header.
442 *
443 * Returns a newly allocated string with a copy of the line, or NULL
444 * which indicate the end of the input.
445 */
446
447static char *
448xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) {
449 char buf[4096];
450 char *bp=buf;
451
452 while(bp - buf < 4095) {
453 if(ctxt->inrptr == ctxt->inptr) {
454 if (xmlNanoHTTPRecv(ctxt) == 0) {
455 if (bp == buf)
456 return(NULL);
457 else
458 *bp = 0;
459 return(xmlMemStrdup(buf));
460 }
461 }
462 *bp = *ctxt->inrptr++;
463 if(*bp == '\n') {
464 *bp = 0;
465 return(xmlMemStrdup(buf));
466 }
467 if(*bp != '\r')
468 bp++;
469 }
470 buf[4095] = 0;
471 return(xmlMemStrdup(buf));
472}
473
474
475/**
476 * xmlNanoHTTPScanAnswer:
477 * @ctxt: an HTTP context
478 * @line: an HTTP header line
479 *
480 * Try to extract useful informations from the server answer.
481 * We currently parse and process:
482 * - The HTTP revision/ return code
483 * - The Content-Type
484 * - The Location for redirrect processing.
485 *
486 * Returns -1 in case of failure, the file descriptor number otherwise
487 */
488
489static void
490xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) {
491 const char *cur = line;
492
493 if (line == NULL) return;
494
495 if (!strncmp(line, "HTTP/", 5)) {
496 int version = 0;
497 int ret = 0;
498
499 cur += 5;
500 while ((*cur >= '0') && (*cur <= '9')) {
501 version *= 10;
502 version += *cur - '0';
503 cur++;
504 }
505 if (*cur == '.') {
506 cur++;
507 if ((*cur >= '0') && (*cur <= '9')) {
508 version *= 10;
509 version += *cur - '0';
510 cur++;
511 }
512 while ((*cur >= '0') && (*cur <= '9'))
513 cur++;
514 } else
515 version *= 10;
516 if ((*cur != ' ') && (*cur != '\t')) return;
517 while ((*cur == ' ') || (*cur == '\t')) cur++;
518 if ((*cur < '0') || (*cur > '9')) return;
519 while ((*cur >= '0') && (*cur <= '9')) {
520 ret *= 10;
521 ret += *cur - '0';
522 cur++;
523 }
524 if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return;
525 ctxt->returnValue = ret;
526 } else if (!strncmp(line, "Content-Type:", 13)) {
527 cur += 13;
528 while ((*cur == ' ') || (*cur == '\t')) cur++;
529 if (ctxt->contentType != NULL)
530 xmlFree(ctxt->contentType);
531 ctxt->contentType = xmlMemStrdup(cur);
532 } else if (!strncmp(line, "ContentType:", 12)) {
533 cur += 12;
534 if (ctxt->contentType != NULL) return;
535 while ((*cur == ' ') || (*cur == '\t')) cur++;
536 ctxt->contentType = xmlMemStrdup(cur);
537 } else if (!strncmp(line, "content-type:", 13)) {
538 cur += 13;
539 if (ctxt->contentType != NULL) return;
540 while ((*cur == ' ') || (*cur == '\t')) cur++;
541 ctxt->contentType = xmlMemStrdup(cur);
542 } else if (!strncmp(line, "contenttype:", 12)) {
543 cur += 12;
544 if (ctxt->contentType != NULL) return;
545 while ((*cur == ' ') || (*cur == '\t')) cur++;
546 ctxt->contentType = xmlMemStrdup(cur);
547 } else if (!strncmp(line, "Location:", 9)) {
548 cur += 9;
549 while ((*cur == ' ') || (*cur == '\t')) cur++;
550 if (ctxt->location != NULL)
551 xmlFree(ctxt->location);
552 ctxt->location = xmlMemStrdup(cur);
553 } else if (!strncmp(line, "location:", 9)) {
554 cur += 9;
555 if (ctxt->location != NULL) return;
556 while ((*cur == ' ') || (*cur == '\t')) cur++;
557 ctxt->location = xmlMemStrdup(cur);
558 }
559}
560
561/**
562 * xmlNanoHTTPConnectAttempt:
563 * @ia: an internet adress structure
564 * @port: the port number
565 *
566 * Attempt a connection to the given IP:port endpoint. It forces
567 * non-blocking semantic on the socket, and allow 60 seconds for
568 * the host to answer.
569 *
570 * Returns -1 in case of failure, the file descriptor number otherwise
571 */
572
573static int
574xmlNanoHTTPConnectAttempt(struct in_addr ia, int port)
575{
576 int s=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
577 struct sockaddr_in sin;
578 fd_set wfd;
579 struct timeval tv;
580 int status;
581
582 if(s==-1) {
583#ifdef DEBUG_HTTP
584 perror("socket");
585#endif
586 return(-1);
587 }
588
589#ifdef _WINSOCKAPI_
590 {
591 long levents = FD_READ | FD_WRITE | FD_ACCEPT |
592 FD_CONNECT | FD_CLOSE ;
593 int rv = 0 ;
594 u_long one = 1;
595
596 status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0;
597 }
598#else /* _WINSOCKAPI_ */
599#if defined(VMS)
600 {
601 int enable = 1;
602 status = IOCTL(s, FIONBIO, &enable);
603 }
604#else /* VMS */
605 if((status = fcntl(s, F_GETFL, 0)) != -1) {
606#ifdef O_NONBLOCK
607 status |= O_NONBLOCK;
608#else /* O_NONBLOCK */
609#ifdef F_NDELAY
610 status |= F_NDELAY;
611#endif /* F_NDELAY */
612#endif /* !O_NONBLOCK */
613 status = fcntl(s, F_SETFL, status);
614 }
615 if(status < 0) {
616#ifdef DEBUG_HTTP
617 perror("nonblocking");
618#endif
619 close(s);
620 return(-1);
621 }
622#endif /* !VMS */
623#endif /* !_WINSOCKAPI_ */
624
625
626 sin.sin_family = AF_INET;
627 sin.sin_addr = ia;
628 sin.sin_port = htons(port);
629
630 if((connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1) &&
631 (errno != EINPROGRESS)) {
632 perror("connect");
633 close(s);
634 return(-1);
635 }
636
637 tv.tv_sec = 60; /* We use 60 second timeouts for now */
638 tv.tv_usec = 0;
639
640 FD_ZERO(&wfd);
641 FD_SET(s, &wfd);
642
643 switch(select(s+1, NULL, &wfd, NULL, &tv))
644 {
645 case 0:
646 /* Time out */
647 close(s);
648 return(-1);
649 case -1:
650 /* Ermm.. ?? */
651#ifdef DEBUG_HTTP
652 perror("select");
653#endif
654 close(s);
655 return(-1);
656 }
Daniel Veillardbe803962000-06-28 23:40:59 +0000657
658 if ( FD_ISSET(s, &wfd) ) {
659 socklen_t len;
660 len = sizeof(status);
661 if (getsockopt(s, SOL_SOCKET, SO_ERROR, &status, &len) < 0 ) {
662 /* Solaris error code */
663 return (-1);
664 }
665 if ( status ) {
666 close (s);
667 errno = status;
668 return (-1);
669 }
670 } else {
671 /* pbm */
672 return (-1);
673 }
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000674
675 return(s);
676}
677
678/**
679 * xmlNanoHTTPConnectHost:
680 * @host: the host name
681 * @port: the port number
682 *
683 * Attempt a connection to the given host:port endpoint. It tries
684 * the multiple IP provided by the DNS if available.
685 *
686 * Returns -1 in case of failure, the file descriptor number otherwise
687 */
688
689static int
690xmlNanoHTTPConnectHost(const char *host, int port)
691{
692 struct hostent *h;
693 int i;
694 int s;
695
696 h=gethostbyname(host);
697 if(h==NULL)
698 {
699#ifdef DEBUG_HTTP
700 fprintf(stderr,"unable to resolve '%s'.\n", host);
701#endif
702 return(-1);
703 }
704
705 for(i=0; h->h_addr_list[i]; i++)
706 {
707 struct in_addr ia;
708 memcpy(&ia, h->h_addr_list[i],4);
709 s = xmlNanoHTTPConnectAttempt(ia, port);
710 if(s != -1)
711 return(s);
712 }
713
714#ifdef DEBUG_HTTP
715 fprintf(stderr, "unable to connect to '%s'.\n", host);
716#endif
717 return(-1);
718}
719
720
721/**
722 * xmlNanoHTTPOpen:
723 * @URL: The URL to load
724 * @contentType: if available the Content-Type information will be
725 * returned at that location
726 *
727 * This function try to open a connection to the indicated resource
728 * via HTTP GET.
729 *
730 * Returns NULL in case of failure, otherwise a request handler.
731 * The contentType, if provided must be freed by the caller
732 */
733
Daniel Veillard06047432000-04-24 11:33:38 +0000734void*
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000735xmlNanoHTTPOpen(const char *URL, char **contentType) {
736 xmlNanoHTTPCtxtPtr ctxt;
737 char buf[4096];
738 int ret;
739 char *p;
740 int head;
741 int nbRedirects = 0;
742 char *redirURL = NULL;
743
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000744 xmlNanoHTTPInit();
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000745 if (contentType != NULL) *contentType = NULL;
746
747retry:
748 if (redirURL == NULL)
749 ctxt = xmlNanoHTTPNewCtxt(URL);
750 else {
751 ctxt = xmlNanoHTTPNewCtxt(redirURL);
752 xmlFree(redirURL);
753 redirURL = NULL;
754 }
755
756 if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
757 xmlNanoHTTPFreeCtxt(ctxt);
758 if (redirURL != NULL) xmlFree(redirURL);
759 return(NULL);
760 }
761 if (ctxt->hostname == NULL) {
762 xmlNanoHTTPFreeCtxt(ctxt);
763 return(NULL);
764 }
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000765 if (proxy)
766 ret = xmlNanoHTTPConnectHost(proxy, proxyPort);
767 else
768 ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000769 if (ret < 0) {
770 xmlNanoHTTPFreeCtxt(ctxt);
771 return(NULL);
772 }
773 ctxt->fd = ret;
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000774 if (proxy) {
Daniel Veillardcf461992000-03-14 18:30:20 +0000775#ifdef HAVE_SNPRINTF
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000776 if (ctxt->port != 80)
777 snprintf(buf, sizeof(buf),
778 "GET http://%s:%d%s HTTP/1.0\r\nHost: %s\r\n\r\n",
779 ctxt->hostname, ctxt->port, ctxt->path, ctxt->hostname);
780 else
781 snprintf(buf, sizeof(buf),"GET http://%s%s HTTP/1.0\r\nHost: %s\r\n\r\n",
782 ctxt->hostname, ctxt->path, ctxt->hostname);
Daniel Veillard335849b1999-09-23 23:08:42 +0000783#else
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000784 if (ctxt->port != 80)
785 sprintf(buf,
786 "GET http://%s:%d%s HTTP/1.0\r\nHost: %s\r\n\r\n",
787 ctxt->hostname, ctxt->port, ctxt->path, ctxt->hostname);
788 else
789 sprintf(buf, "GET http://%s%s HTTP/1.0\r\nHost: %s\r\n\r\n",
790 ctxt->hostname, ctxt->path, ctxt->hostname);
Daniel Veillard335849b1999-09-23 23:08:42 +0000791#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000792#ifdef DEBUG_HTTP
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000793 if (ctxt->port != 80)
794 printf("-> Proxy GET http://%s:%d%s HTTP/1.0\n-> Host: %s\n\n",
795 ctxt->hostname, ctxt->port, ctxt->path, ctxt->hostname);
796 else
797 printf("-> Proxy GET http://%s%s HTTP/1.0\n-> Host: %s\n\n",
798 ctxt->hostname, ctxt->path, ctxt->hostname);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000799#endif
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000800 } else {
801#ifdef HAVE_SNPRINTF
802 snprintf(buf, sizeof(buf),"GET %s HTTP/1.0\r\nHost: %s\r\n\r\n",
803 ctxt->path, ctxt->hostname);
804#else
805 sprintf(buf, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n",
806 ctxt->path, ctxt->hostname);
807#endif
808#ifdef DEBUG_HTTP
809 printf("-> GET %s HTTP/1.0\n-> Host: %s\n\n",
810 ctxt->path, ctxt->hostname);
811#endif
812 }
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000813 ctxt->outptr = ctxt->out = xmlMemStrdup(buf);
814 ctxt->state = XML_NANO_HTTP_WRITE;
815 xmlNanoHTTPSend(ctxt);
816 ctxt->state = XML_NANO_HTTP_READ;
817 head = 1;
818
819 while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
820 if (head && (*p == 0)) {
821 head = 0;
822 ctxt->content = ctxt->inrptr;
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000823 xmlFree(p);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000824 break;
825 }
826 xmlNanoHTTPScanAnswer(ctxt, p);
827
828#ifdef DEBUG_HTTP
829 if (p != NULL) printf("<- %s\n", p);
830#endif
831 if (p != NULL) xmlFree(p);
832 }
833
834 if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
835 (ctxt->returnValue < 400)) {
836#ifdef DEBUG_HTTP
837 printf("\nRedirect to: %s\n", ctxt->location);
838#endif
839 while (xmlNanoHTTPRecv(ctxt)) ;
840 if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
841 nbRedirects++;
842 redirURL = xmlMemStrdup(ctxt->location);
843 xmlNanoHTTPFreeCtxt(ctxt);
844 goto retry;
845 }
846 xmlNanoHTTPFreeCtxt(ctxt);
847#ifdef DEBUG_HTTP
848 printf("Too many redirrects, aborting ...\n");
849#endif
850 return(NULL);
851
852 }
853
854 if ((contentType != NULL) && (ctxt->contentType != NULL))
855 *contentType = xmlMemStrdup(ctxt->contentType);
856
857#ifdef DEBUG_HTTP
858 if (ctxt->contentType != NULL)
859 printf("\nCode %d, content-type '%s'\n\n",
860 ctxt->returnValue, ctxt->contentType);
861 else
862 printf("\nCode %d, no content-type\n\n",
863 ctxt->returnValue);
864#endif
865
866 return((void *) ctxt);
867}
868
869/**
870 * xmlNanoHTTPRead:
871 * @ctx: the HTTP context
872 * @dest: a buffer
873 * @len: the buffer length
874 *
875 * This function tries to read @len bytes from the existing HTTP connection
876 * and saves them in @dest. This is a blocking call.
877 *
878 * Returns the number of byte read. 0 is an indication of an end of connection.
879 * -1 indicates a parameter error.
880 */
881int
882xmlNanoHTTPRead(void *ctx, void *dest, int len) {
883 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
884
885 if (ctx == NULL) return(-1);
886 if (dest == NULL) return(-1);
887 if (len <= 0) return(0);
888
889 while (ctxt->inptr - ctxt->inrptr < len) {
890 if (xmlNanoHTTPRecv(ctxt) == 0) break;
891 }
892 if (ctxt->inptr - ctxt->inrptr < len)
893 len = ctxt->inptr - ctxt->inrptr;
894 memcpy(dest, ctxt->inrptr, len);
895 ctxt->inrptr += len;
896 return(len);
897}
898
899/**
900 * xmlNanoHTTPClose:
901 * @ctx: the HTTP context
902 *
903 * This function closes an HTTP context, it ends up the connection and
904 * free all data related to it.
905 */
906void
907xmlNanoHTTPClose(void *ctx) {
908 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
909
910 if (ctx == NULL) return;
911
912 xmlNanoHTTPFreeCtxt(ctxt);
913}
914
Daniel Veillard00fdf371999-10-08 09:40:39 +0000915#ifndef DEBUG_HTTP
916#define DEBUG_HTTP
917#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000918/**
919 * xmlNanoHTTPMethod:
920 * @URL: The URL to load
921 * @method: the HTTP method to use
922 * @input: the input string if any
923 * @contentType: the Content-Type information IN and OUT
924 * @headers: the extra headers
925 *
926 * This function try to open a connection to the indicated resource
927 * via HTTP using the given @method, adding the given extra headers
928 * and the input buffer for the request content.
929 *
930 * Returns NULL in case of failure, otherwise a request handler.
931 * The contentType, if provided must be freed by the caller
932 */
933
Daniel Veillard06047432000-04-24 11:33:38 +0000934void*
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000935xmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
936 char **contentType, const char *headers) {
937 xmlNanoHTTPCtxtPtr ctxt;
938 char buf[20000];
939 int ret;
940 char *p;
941 int head;
942 int nbRedirects = 0;
943 char *redirURL = NULL;
944
945 if (URL == NULL) return(NULL);
946 if (method == NULL) method = "GET";
947 if (contentType != NULL) *contentType = NULL;
948
949retry:
950 if (redirURL == NULL)
951 ctxt = xmlNanoHTTPNewCtxt(URL);
952 else {
953 ctxt = xmlNanoHTTPNewCtxt(redirURL);
954 xmlFree(redirURL);
955 redirURL = NULL;
956 }
957
958 if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
959 xmlNanoHTTPFreeCtxt(ctxt);
960 if (redirURL != NULL) xmlFree(redirURL);
961 return(NULL);
962 }
963 if (ctxt->hostname == NULL) {
964 xmlNanoHTTPFreeCtxt(ctxt);
965 return(NULL);
966 }
967 ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
968 if (ret < 0) {
969 xmlNanoHTTPFreeCtxt(ctxt);
970 return(NULL);
971 }
972 ctxt->fd = ret;
973
974 if (input == NULL) {
975 if (headers == NULL) {
976 if ((contentType == NULL) || (*contentType == NULL)) {
Daniel Veillard335849b1999-09-23 23:08:42 +0000977#ifdef HAVE_SNPRINTF
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000978 snprintf(buf, sizeof(buf),
979 "%s %s HTTP/1.0\r\nHost: %s\r\n\r\n",
980 method, ctxt->path, ctxt->hostname);
Daniel Veillard335849b1999-09-23 23:08:42 +0000981#else
982 sprintf(buf,
983 "%s %s HTTP/1.0\r\nHost: %s\r\n\r\n",
984 method, ctxt->path, ctxt->hostname);
985#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000986 } else {
Daniel Veillard335849b1999-09-23 23:08:42 +0000987#ifdef HAVE_SNPRINTF
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000988 snprintf(buf, sizeof(buf),
989 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n\r\n",
990 method, ctxt->path, ctxt->hostname, *contentType);
Daniel Veillard335849b1999-09-23 23:08:42 +0000991#else
992 sprintf(buf,
993 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n\r\n",
994 method, ctxt->path, ctxt->hostname, *contentType);
995#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000996 }
997 } else {
998 if ((contentType == NULL) || (*contentType == NULL)) {
Daniel Veillard335849b1999-09-23 23:08:42 +0000999#ifdef HAVE_SNPRINTF
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001000 snprintf(buf, sizeof(buf),
1001 "%s %s HTTP/1.0\r\nHost: %s\r\n%s\r\n",
1002 method, ctxt->path, ctxt->hostname, headers);
Daniel Veillard335849b1999-09-23 23:08:42 +00001003#else
1004 sprintf(buf,
1005 "%s %s HTTP/1.0\r\nHost: %s\r\n%s\r\n",
1006 method, ctxt->path, ctxt->hostname, headers);
1007#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001008 } else {
Daniel Veillard335849b1999-09-23 23:08:42 +00001009#ifdef HAVE_SNPRINTF
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001010 snprintf(buf, sizeof(buf),
1011 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n%s\r\n",
1012 method, ctxt->path, ctxt->hostname, *contentType,
1013 headers);
Daniel Veillard335849b1999-09-23 23:08:42 +00001014#else
1015 sprintf(buf,
1016 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n%s\r\n",
1017 method, ctxt->path, ctxt->hostname, *contentType,
1018 headers);
1019#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001020 }
1021 }
1022 } else {
1023 int len = strlen(input);
1024 if (headers == NULL) {
1025 if ((contentType == NULL) || (*contentType == NULL)) {
Daniel Veillard335849b1999-09-23 23:08:42 +00001026#ifdef HAVE_SNPRINTF
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001027 snprintf(buf, sizeof(buf),
1028 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s",
1029 method, ctxt->path, ctxt->hostname, len, input);
Daniel Veillard335849b1999-09-23 23:08:42 +00001030#else
1031 sprintf(buf,
1032 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s",
1033 method, ctxt->path, ctxt->hostname, len, input);
1034#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001035 } else {
Daniel Veillard335849b1999-09-23 23:08:42 +00001036#ifdef HAVE_SNPRINTF
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001037 snprintf(buf, sizeof(buf),
1038"%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n%s",
1039 method, ctxt->path, ctxt->hostname, *contentType, len,
1040 input);
Daniel Veillard335849b1999-09-23 23:08:42 +00001041#else
1042 sprintf(buf,
1043"%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n%s",
1044 method, ctxt->path, ctxt->hostname, *contentType, len,
1045 input);
1046#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001047 }
1048 } else {
1049 if ((contentType == NULL) || (*contentType == NULL)) {
Daniel Veillard335849b1999-09-23 23:08:42 +00001050#ifdef HAVE_SNPRINTF
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001051 snprintf(buf, sizeof(buf),
1052 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n%s\r\n%s",
1053 method, ctxt->path, ctxt->hostname, len,
1054 headers, input);
Daniel Veillard335849b1999-09-23 23:08:42 +00001055#else
1056 sprintf(buf,
1057 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n%s\r\n%s",
1058 method, ctxt->path, ctxt->hostname, len,
1059 headers, input);
1060#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001061 } else {
Daniel Veillard335849b1999-09-23 23:08:42 +00001062#ifdef HAVE_SNPRINTF
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001063 snprintf(buf, sizeof(buf),
1064"%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n%s\r\n%s",
1065 method, ctxt->path, ctxt->hostname, *contentType,
1066 len, headers, input);
Daniel Veillard335849b1999-09-23 23:08:42 +00001067#else
1068 sprintf(buf,
1069"%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n%s\r\n%s",
1070 method, ctxt->path, ctxt->hostname, *contentType,
1071 len, headers, input);
1072#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001073 }
1074 }
1075 }
1076#ifdef DEBUG_HTTP
1077 printf("-> %s", buf);
1078#endif
1079 ctxt->outptr = ctxt->out = xmlMemStrdup(buf);
1080 ctxt->state = XML_NANO_HTTP_WRITE;
1081 xmlNanoHTTPSend(ctxt);
1082 ctxt->state = XML_NANO_HTTP_READ;
1083 head = 1;
1084
1085 while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
1086 if (head && (*p == 0)) {
1087 head = 0;
1088 ctxt->content = ctxt->inrptr;
1089 if (p != NULL) xmlFree(p);
1090 break;
1091 }
1092 xmlNanoHTTPScanAnswer(ctxt, p);
1093
1094#ifdef DEBUG_HTTP
1095 if (p != NULL) printf("<- %s\n", p);
1096#endif
1097 if (p != NULL) xmlFree(p);
1098 }
1099
1100 if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
1101 (ctxt->returnValue < 400)) {
1102#ifdef DEBUG_HTTP
1103 printf("\nRedirect to: %s\n", ctxt->location);
1104#endif
1105 while (xmlNanoHTTPRecv(ctxt)) ;
1106 if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
1107 nbRedirects++;
1108 redirURL = xmlMemStrdup(ctxt->location);
1109 xmlNanoHTTPFreeCtxt(ctxt);
1110 goto retry;
1111 }
1112 xmlNanoHTTPFreeCtxt(ctxt);
1113#ifdef DEBUG_HTTP
1114 printf("Too many redirrects, aborting ...\n");
1115#endif
1116 return(NULL);
1117
1118 }
1119
1120 if ((contentType != NULL) && (ctxt->contentType != NULL))
1121 *contentType = xmlMemStrdup(ctxt->contentType);
1122 else if (contentType != NULL)
1123 *contentType = NULL;
1124
1125#ifdef DEBUG_HTTP
1126 if (ctxt->contentType != NULL)
1127 printf("\nCode %d, content-type '%s'\n\n",
1128 ctxt->returnValue, ctxt->contentType);
1129 else
1130 printf("\nCode %d, no content-type\n\n",
1131 ctxt->returnValue);
1132#endif
1133
1134 return((void *) ctxt);
1135}
1136
1137/**
1138 * xmlNanoHTTPFetch:
1139 * @URL: The URL to load
1140 * @filename: the filename where the content should be saved
1141 * @contentType: if available the Content-Type information will be
1142 * returned at that location
1143 *
1144 * This function try to fetch the indicated resource via HTTP GET
1145 * and save it's content in the file.
1146 *
1147 * Returns -1 in case of failure, 0 incase of success. The contentType,
1148 * if provided must be freed by the caller
1149 */
1150int
1151xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
1152 void *ctxt;
1153 char buf[4096];
1154 int fd;
1155 int len;
1156
1157 ctxt = xmlNanoHTTPOpen(URL, contentType);
1158 if (ctxt == NULL) return(-1);
1159
1160 if (!strcmp(filename, "-"))
1161 fd = 0;
1162 else {
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001163 fd = open(filename, O_CREAT | O_WRONLY, 00644);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001164 if (fd < 0) {
1165 xmlNanoHTTPClose(ctxt);
1166 if ((contentType != NULL) && (*contentType != NULL)) {
1167 xmlFree(*contentType);
1168 *contentType = NULL;
1169 }
1170 return(-1);
1171 }
1172 }
1173
1174 while ((len = xmlNanoHTTPRead(ctxt, buf, sizeof(buf))) > 0) {
1175 write(fd, buf, len);
1176 }
1177
1178 xmlNanoHTTPClose(ctxt);
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001179 close(fd);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001180 return(0);
1181}
1182
1183/**
1184 * xmlNanoHTTPSave:
Daniel Veillard00fdf371999-10-08 09:40:39 +00001185 * @ctxt: the HTTP context
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001186 * @filename: the filename where the content should be saved
1187 *
1188 * This function saves the output of the HTTP transaction to a file
1189 * It closes and free the context at the end
1190 *
1191 * Returns -1 in case of failure, 0 incase of success.
1192 */
1193int
1194xmlNanoHTTPSave(void *ctxt, const char *filename) {
1195 char buf[4096];
1196 int fd;
1197 int len;
1198
1199 if (ctxt == NULL) return(-1);
1200
1201 if (!strcmp(filename, "-"))
1202 fd = 0;
1203 else {
1204 fd = open(filename, O_CREAT | O_WRONLY);
1205 if (fd < 0) {
1206 xmlNanoHTTPClose(ctxt);
1207 return(-1);
1208 }
1209 }
1210
1211 while ((len = xmlNanoHTTPRead(ctxt, buf, sizeof(buf))) > 0) {
1212 write(fd, buf, len);
1213 }
1214
1215 xmlNanoHTTPClose(ctxt);
1216 return(0);
1217}
1218
1219/**
1220 * xmlNanoHTTPReturnCode:
1221 * @ctx: the HTTP context
1222 *
1223 * Returns the HTTP return code for the request.
1224 */
1225int
1226xmlNanoHTTPReturnCode(void *ctx) {
1227 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1228
1229 if (ctxt == NULL) return(-1);
1230
1231 return(ctxt->returnValue);
1232}
1233
1234#ifdef STANDALONE
1235int main(int argc, char **argv) {
1236 char *contentType = NULL;
1237
1238 if (argv[1] != NULL) {
1239 if (argv[2] != NULL)
1240 xmlNanoHTTPFetch(argv[1], argv[2], &contentType);
1241 else
1242 xmlNanoHTTPFetch(argv[1], "-", &contentType);
1243 if (contentType != NULL) xmlFree(contentType);
1244 } else {
1245 printf("%s: minimal HTTP GET implementation\n", argv[0]);
1246 printf("\tusage %s [ URL [ filename ] ]\n", argv[0]);
1247 }
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001248 xmlNanoHTTPCleanup();
1249 xmlMemoryDump();
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001250 return(0);
1251}
1252#endif /* STANDALONE */
Daniel Veillard361d8452000-04-03 19:48:13 +00001253#else /* !LIBXML_HTTP_ENABLED */
1254#ifdef STANDALONE
1255#include <stdio.h>
1256int main(int argc, char **argv) {
1257 printf("%s : HTTP support not compiled in\n", argv[0]);
1258 return(0);
1259}
1260#endif /* STANDALONE */
1261#endif /* LIBXML_HTTP_ENABLED */