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

Annotation of /trunk/Src/wptHTTP.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 308 - (hide annotations)
Fri Mar 23 14:26:30 2007 UTC (17 years, 11 months ago) by twoaday
File size: 12308 byte(s)
More cleanups.


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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26