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

Annotation of /trunk/Src/wptHTTP.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 260 - (hide annotations)
Wed Aug 16 10:01:30 2006 UTC (18 years, 6 months ago) by twoaday
File size: 12139 byte(s)


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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26