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

Annotation of /trunk/Src/wptHTTP.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26