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

Annotation of /trunk/Src/wptHTTP.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 351 - (hide annotations)
Thu Dec 1 20:10:25 2011 UTC (13 years, 3 months ago) by twoaday
File size: 12653 byte(s)


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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26