/[winpt]/trunk/Src/wptHTTP.cpp
ViewVC logotype

Annotation of /trunk/Src/wptHTTP.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 310 - (hide annotations)
Sat Apr 7 11:07:20 2007 UTC (17 years, 10 months ago) by twoaday
File size: 12500 byte(s)


1 twoaday 181 /* wptHTTP.cpp - Generic HTTP support
2 twoaday 308 * Copyright (C) 2004, 2005, 2006, 2007 Timo Schulz
3 twoaday 181 *
4     * This file is part of WinPT.
5     *
6     * WinPT is free software; you can redistribute it and/or
7     * modify it under the terms of the GNU General Public License
8     * as published by the Free Software Foundation; either version 2
9     * of the License, or (at your option) any later version.
10     *
11     * WinPT is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14     * General Public License for more details.
15     */
16     #ifdef HAVE_CONFIG_H
17     #include <config.h>
18     #endif
19    
20     #include <stdio.h>
21     #include <stdio.h>
22     #include <stdlib.h>
23     #include <string.h>
24     #include <windows.h>
25     #include <ctype.h>
26     #include <errno.h>
27    
28     #include "wptHTTP.h"
29     #include "wptTypes.h"
30     #include "wptErrors.h"
31    
32    
33     /* Empty constructur to allow advanced requests. */
34     wHTTP::wHTTP (void)
35     {
36     reset ();
37     }
38    
39    
40     /* Constructur to allow alternative syntax. */
41     wHTTP::wHTTP (const char *_host, int _port, const char *_url)
42     {
43     reset ();
44     this->host = strdup (_host);
45     this->port = _port;
46     this->url = strdup (_url);
47     if (!sendRequest (_host, _port, _url))
48     parseResponse (&statcode);
49    
50     }
51    
52    
53     /* Standard constructur. */
54     wHTTP::wHTTP (const char *_url)
55     {
56     reset ();
57 twoaday 308 open (_url);
58     }
59 twoaday 262
60 twoaday 308
61     /* Open a connection to the given URL. */
62     int
63     wHTTP::open (const char *_url)
64     {
65 twoaday 310 /* In case the function is used more than once, we
66     need to release the old structures first. */
67     releaseHeaders ();
68 twoaday 308
69 twoaday 310 safe_free (this->host);
70     safe_free (this->url);
71    
72 twoaday 262 if (extractHostInfo (_url, &this->host, &this->url))
73 twoaday 308 return -1;
74    
75 twoaday 181 if (!sendRequest (this->host, this->port, this->url))
76 twoaday 308 return parseResponse (&statcode);
77    
78     return 0;
79 twoaday 181 }
80    
81    
82     /* Reset object contents for first use. */
83     void
84     wHTTP::reset (void)
85     {
86     ver = 1;
87     port = 80;
88     host = url = NULL;
89     req_headers = resp_headers = NULL;
90     fd = statcode = 0;
91     method = HTTP_GET;
92     nleft = -1;
93 twoaday 200 error = 0;
94 twoaday 181 }
95    
96    
97     /* Perform a HTTP 'HEAD' request. */
98     int
99     wHTTP::head (const char *_url)
100     {
101 twoaday 262 int err;
102    
103 twoaday 260 safe_free (host);
104     safe_free (url);
105 twoaday 181
106 twoaday 262 err = extractHostInfo (_url, &this->host, &this->url);
107     if (err)
108     return err;
109 twoaday 310
110 twoaday 181 method = HTTP_HEAD;
111 twoaday 310 if (!sendRequest (this->host, this->port, this->url))
112 twoaday 181 parseResponse (&statcode);
113    
114     return 0;
115     }
116    
117    
118     /* Perform a HTTP 'GET' request. */
119     int
120     wHTTP::get (const char *_url)
121     {
122 twoaday 262 int err;
123    
124 twoaday 310 safe_free (this->host);
125     safe_free (this->url);
126 twoaday 181
127 twoaday 262 err = extractHostInfo (_url, &this->host, &this->url);
128     if (err)
129     return err;
130 twoaday 181
131     method = HTTP_GET;
132     if (!sendRequest (this->host, this->port, this->url))
133     parseResponse (&statcode);
134    
135     return 0;
136     }
137    
138    
139     /* Return HTTP status code. */
140     int
141     wHTTP::getStatusCode (void)
142     {
143     return statcode;
144     }
145    
146    
147     /* Return MIME content type. */
148     const char*
149     wHTTP::getContentType (void)
150     {
151     const char *type = NULL;
152    
153     findHeader (resp_headers, "Content-Type", &type);
154     return type;
155     }
156    
157    
158     /* Return content length. */
159     unsigned int
160     wHTTP::getContentLength (void)
161     {
162     const char *len = NULL;
163    
164     if (findHeader (resp_headers, "Content-Length", &len))
165     return strtoul (len, NULL, 10);
166     return 0;
167     }
168    
169    
170     void
171     wHTTP::addHeader (http_head_t *root, const char *val)
172     {
173     http_head_t n, t;
174    
175     t = (http_head_t)calloc (1, sizeof *t+strlen (val)+2);
176     if (!t)
177     BUG (0);
178     strcpy (t->d, val);
179    
180     if (!*root)
181     *root = t;
182     else {
183     for (n = *root; n->next; n=n->next)
184     ;
185     n->next = t;
186     }
187     }
188    
189    
190     bool
191     wHTTP::findHeader (http_head_t root,
192     const char *name, const char **val)
193     {
194     http_head_t n;
195     char *p;
196    
197     *val = NULL;
198     for (n = root; n; n = n->next) {
199     if (strlen (n->d) >= strlen (name) &&
200     strstr (n->d, name)) {
201     p = strchr (n->d, ':');
202     *val = p? n->d + (p - n->d + 1 + 1) : NULL;
203     return true;
204     }
205     }
206     return false;
207     }
208    
209    
210     int
211     wHTTP::connect (const char *_host, int _port)
212     {
213     struct hostent *hp;
214     struct sockaddr_in srv;
215     unsigned long addr = 0;
216     int i = 1;
217    
218     memset (&srv, 0, sizeof srv);
219     srv.sin_port = htons ((unsigned short)_port);
220     srv.sin_family = AF_INET;
221    
222     if (isalpha (*_host)) {
223     hp = gethostbyname (_host);
224 twoaday 200 if (!hp) {
225     this->error = (int)WSAGetLastError ();
226 twoaday 181 return WPTERR_WINSOCK_CONNECT;
227 twoaday 200 }
228 twoaday 181 memcpy (&srv.sin_addr, hp->h_addr, hp->h_length);
229     }
230     else {
231     addr = inet_addr (_host);
232 twoaday 200 if (addr == INADDR_NONE) {
233     this->error = (int)WSAGetLastError ();
234 twoaday 181 return WPTERR_WINSOCK_CONNECT;
235 twoaday 200 }
236 twoaday 181 memcpy (&srv.sin_addr, &addr, sizeof addr);
237     }
238    
239     fd = socket (AF_INET, SOCK_STREAM, 0);
240 twoaday 200 if (fd < 0) {
241     this->error = (int)WSAGetLastError ();
242 twoaday 181 return WPTERR_WINSOCK_SOCKET;
243 twoaday 200 }
244 twoaday 181
245     if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR,
246     (const char*)&i, sizeof i)) {
247     closesocket (fd);
248     fd = 0;
249 twoaday 200 this->error = (int)WSAGetLastError ();
250 twoaday 181 return WPTERR_WINSOCK_SOCKET;
251     }
252    
253     if (::connect (fd, (struct sockaddr *)&srv, sizeof srv)) {
254     closesocket (fd);
255     fd = 0;
256 twoaday 200 this->error = (int)WSAGetLastError ();
257 twoaday 181 return WPTERR_WINSOCK_CONNECT;
258     }
259    
260     return 0;
261     }
262    
263    
264     int
265     wHTTP::isDataAvailable (int _fd)
266     {
267     struct timeval tv;
268     fd_set inp;
269     int n;
270    
271     FD_ZERO (&inp);
272     FD_SET (_fd, &inp);
273     tv.tv_sec = 1;
274     tv.tv_usec = 0;
275    
276     n = select (_fd+1, &inp, NULL, NULL, &tv);
277     if (n && FD_ISSET (_fd, &inp))
278     return n;
279     return 0;
280     }
281    
282    
283     int
284     wHTTP::readLine (char *buf, unsigned int nbuf,
285     int nonblock, int *nn, int *eof)
286     {
287     char c;
288     int n, i;
289    
290     if (nn)
291     *nn = 0;
292     if (eof)
293     *eof = 0;
294     i = 0;
295     do {
296     if (nonblock == 1 && isDataAvailable (fd) == 0) {
297     buf[i++] = '\0';
298     i = -1;
299     break;
300     }
301     n = recv (fd, &c, 1, 0);
302 twoaday 260 if (n == -1) {
303     this->error = (int)WSAGetLastError ();
304 twoaday 181 break;
305 twoaday 260 }
306 twoaday 181 if (n == 0 || nbuf == 0 || c == '\n') {
307     if (n == 0) {
308     if (eof)
309     *eof = 1;
310     buf[i++] = '\0';
311     }
312     else {
313     buf[i++] = c;
314     buf[i] = '\0';
315     }
316     break;
317     }
318     else {
319     buf[i++] = c;
320     nbuf--;
321     }
322     } while (n > 0 && i != -1);
323    
324     if (nn)
325     *nn = i;
326     return 0;
327     }
328    
329    
330    
331     /* Extract the host from the given url @url. Return the host in
332     @host and also return the resource part of the url in @new_url. */
333     int
334     wHTTP::extractHostInfo (const char *_url, char **_host, char **new_url)
335 twoaday 262 {
336     char tmpbuf[2*MAX_PATH+1];
337 twoaday 181 char *p;
338    
339     *_host = NULL;
340     *new_url = NULL;
341    
342     /* XXX: do not use static buffers. */
343     memset (tmpbuf, 0, sizeof (tmpbuf));
344 twoaday 271 strncpy (tmpbuf, _url, DIM (tmpbuf)-1);
345 twoaday 181
346     p = "http://";
347     if (strlen (_url) < 10 || strncmp (_url, p, 7))
348     return WPTERR_GENERAL;
349     _url += strlen (p);
350     p = strtok (tmpbuf+7, "/");
351     if (!p)
352     return WPTERR_GENERAL;
353 twoaday 262 *_host = strdup (p);
354 twoaday 181 p = strchr (_url, '/');
355 twoaday 262 if (!p) /* document were given so we assume the root '/'. */
356     *new_url = strdup ("/");
357     else
358     *new_url = strdup (p);
359 twoaday 181 return 0;
360     }
361    
362    
363     /* Add a request header. */
364     int
365     wHTTP::addRequestHeader (const char *name, const char *val)
366     {
367     char *p;
368     char *fmt = "%s: %s";
369    
370 twoaday 214 p = (char*) malloc (strlen (name)+strlen (val)+strlen (fmt)+4);
371 twoaday 181 sprintf (p, fmt, name, val);
372     addHeader (&req_headers, p);
373 twoaday 260 safe_free (p);
374 twoaday 181
375     return 0;
376     }
377    
378    
379     void
380     wHTTP::setVersion (int _ver)
381     {
382     if (_ver > 9)
383     _ver = 0;
384     this->ver = _ver;
385     }
386    
387    
388     int
389     wHTTP::addRequestHeader (const char *name, unsigned int val)
390     {
391     char buf[32];
392    
393     sprintf (buf, "%lu", (DWORD)val);
394     return addRequestHeader (name, buf);
395     }
396    
397    
398     /* Prepare the request resource @url and send
399     it to host @host (port @port). */
400     int
401     wHTTP::sendRequest (const char *_host, int _port, const char *_url)
402     {
403     const char *id[] = {"GET", "HEAD", "PUT", "POST"};
404     http_head_t h;
405     const char *h_head;
406     char *p;
407     int n;
408     int rc;
409    
410     if (!this->fd) {
411     rc = connect (_host, _port);
412     if (rc)
413     return rc;
414     }
415    
416     if (*_url == '/')
417 twoaday 214 _url++;
418 twoaday 181 if (_url == NULL)
419     _url = "";
420    
421     addRequestHeader ("Host", _host);
422     if (ver < 1)
423     addRequestHeader ("Connection", "close");
424    
425     n = 0;
426     for (h = req_headers; h; h = h->next)
427     n += strlen (h->d) + 2 + 1;
428     h_head = "%s /%s HTTP/1.%d\r\n";
429 twoaday 214 n += strlen (id[method]) + strlen (h_head)
430     + strlen (_url) + n + 2 + 1 + 4;
431     p = (char*)calloc (1, n+1);
432 twoaday 181 if (!p)
433     BUG (0);
434 twoaday 214 sprintf (p, h_head, id[method], _url, ver);
435 twoaday 181 for (h = req_headers; h; h = h->next) {
436     strcat (p, h->d);
437     strcat (p, "\r\n");
438     }
439     strcat (p, "\r\n");
440     n = send (fd, p, strlen (p), 0);
441 twoaday 260 safe_free (p);
442 twoaday 181 return n > 0? 0 : WPTERR_GENERAL;
443     }
444    
445    
446     /* Parse all response resp_headers. */
447     int
448     wHTTP::parseHeaders (http_head_t *r_head)
449     {
450     char buf[300];
451     int nn;
452     int rc;
453    
454     if (!r_head)
455     return WPTERR_GENERAL;
456    
457     do {
458 twoaday 262 rc = readLine (buf, DIM (buf)-1, 1, &nn, NULL);
459 twoaday 181 if (rc)
460     return rc;
461     if (nn == 2)
462     break; /* reach empty line */
463     addHeader (r_head, buf);
464     } while (rc == 0);
465     return 0;
466     }
467    
468    
469     /* Read data from the response and write it to @out. */
470     int
471     wHTTP::readData (FILE *out)
472     {
473     const char *val;
474     char buf[1024+1];
475     int nlen = 0, nn = 0, n, eof=0;
476     int rc = 0;
477    
478     if (this->fd == 0 || this->resp_headers == NULL)
479     return -1;
480    
481     nlen = getContentLength ();
482     if (nlen == 0) {
483     if (!findHeader (resp_headers, "Connection", &val) ||
484     strnicmp (val, "close", 4))
485     return WPTERR_GENERAL;
486     }
487    
488     val = getContentType ();
489     if (!val)
490     return WPTERR_GENERAL;
491     if (strnicmp (val, "text", 4)) { /* binary */
492     do {
493     n = recv (fd, buf, nlen > 1024? 1024 : nlen, 0);
494 twoaday 260 if (n == -1) {
495     this->error = (int)WSAGetLastError ();
496 twoaday 181 return SOCKET_ERROR;
497 twoaday 260 }
498 twoaday 181 if (n == 0)
499     break;
500     nlen -= n;
501     fwrite (buf, 1, n, out);
502     } while (nlen > 0);
503     return 0;
504     }
505    
506     do {
507 twoaday 262 rc = readLine (buf, DIM (buf)-1, 1, &n, &eof);
508 twoaday 181 if (rc)
509     return rc;
510     if (n > 0)
511     fwrite (buf, 1, n, out);
512     if (nlen > 0) {
513     nn += n;
514     if (nlen == nn)
515     break;
516     }
517     } while (eof == 0);
518    
519     return 0;
520     }
521    
522    
523     /* Parse the HTTP response line. */
524     int
525     wHTTP::parseResponse (int *_statcode)
526     {
527     http_head_t n;
528     const char *tmp, *p;
529     char buf[300];
530     int code = 0, nn = 0;
531     int rc;
532    
533     *_statcode = 0;
534     rc = readLine (buf, 299, 1, &nn, NULL);
535     if (rc)
536     return rc;
537    
538     tmp = "HTTP/1.";
539     if (strlen (buf) < 8 || strncmp (buf, tmp, strlen (tmp)))
540     return WPTERR_GENERAL;
541     p = buf;
542     p += strlen (tmp)+1 + 1; /* skip HTTP/1.x and WS */
543     *_statcode = atoi (p);
544     if (tmp == 0)
545     return WPTERR_GENERAL;
546     p += 3 + 1; /* number + WS */
547    
548     if (code == HTTP_STAT_400 ||
549     code == HTTP_STAT_403 ||
550     code == HTTP_STAT_404 ||
551     code == HTTP_STAT_405)
552     return WPTERR_GENERAL;
553    
554     while (resp_headers) {
555     n = resp_headers->next;
556     free (resp_headers);
557     resp_headers = n;
558     }
559     resp_headers = NULL;
560 twoaday 310 return parseHeaders (&resp_headers);
561 twoaday 181 }
562    
563    
564 twoaday 308 /* Release all used structures. */
565     void
566 twoaday 310 wHTTP::releaseHeaders (void)
567 twoaday 181 {
568     http_head_t h;
569    
570 twoaday 310 while (resp_headers != NULL) {
571 twoaday 181 h = resp_headers->next;
572     free (resp_headers);
573     resp_headers = h;
574     }
575 twoaday 308 resp_headers = NULL;
576 twoaday 310 while (req_headers != NULL) {
577 twoaday 181 h = req_headers->next;
578     free (req_headers);
579     req_headers = h;
580     }
581 twoaday 308 req_headers = NULL;
582     }
583    
584    
585     /* Destroy HTTP object. */
586     wHTTP::~wHTTP (void)
587     {
588 twoaday 310 releaseHeaders();
589     safe_free (host);
590     safe_free (url);
591 twoaday 200 if (fd != 0) {
592 twoaday 181 closesocket (fd);
593 twoaday 200 fd = 0;
594     }
595 twoaday 181 }
596    
597    
598 twoaday 200 /* Read @buflen bytes from the http stream into the buffer @buf. */
599 twoaday 181 int
600     wHTTP::read (void *buf, unsigned int buflen)
601     {
602     int n;
603    
604     if (this->fd == 0 || this->resp_headers == NULL)
605     return -1;
606     if (method != HTTP_GET)
607     return -1;
608    
609     if (nleft == -1)
610     nleft = getContentLength ();
611     if ((int)buflen > nleft)
612     buflen = nleft;
613     if (nleft == 0)
614     return -1;
615    
616     n = recv (fd, (char*)buf, (int)buflen, 0);
617     if (n > 0) {
618     nleft -= n;
619     if (nleft < 0) nleft = 0;
620     }
621     return n;
622     }
623    
624    
625 twoaday 200 /* Write the buffer @buf to the http stream. */
626 twoaday 181 int
627     wHTTP::write (const void *buf, unsigned buflen)
628     {
629     if (this->fd == 0)
630     return -1;
631     if (method == HTTP_GET || method == HTTP_HEAD)
632     return -1;
633     return send (fd, (const char*)buf, (int)buflen, 0);
634     }
635 twoaday 200
636    
637 twoaday 260 /* Return the Winsock specific error code. */
638 twoaday 200 int wHTTP::getErrorCode (void)
639     {
640     return error;
641     }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26