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

Contents of /trunk/Src/wptHTTP.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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


1 /* wptHTTP.cpp - Generic HTTP support
2 * Copyright (C) 2004, 2005, 2006, 2007 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 open (_url);
58 }
59
60
61 /* Open a connection to the given URL. */
62 int
63 wHTTP::open (const char *_url)
64 {
65 /* In case the function is used more than once, we
66 need to release the old structures first. */
67 releaseHeaders ();
68
69 safe_free (this->host);
70 safe_free (this->url);
71
72 if (extractHostInfo (_url, &this->host, &this->url))
73 return -1;
74
75 if (!sendRequest (this->host, this->port, this->url))
76 return parseResponse (&statcode);
77
78 return 0;
79 }
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 error = 0;
94 }
95
96
97 /* Perform a HTTP 'HEAD' request. */
98 int
99 wHTTP::head (const char *_url)
100 {
101 int err;
102
103 safe_free (host);
104 safe_free (url);
105
106 err = extractHostInfo (_url, &this->host, &this->url);
107 if (err)
108 return err;
109
110 method = HTTP_HEAD;
111 if (!sendRequest (this->host, this->port, this->url))
112 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 int err;
123
124 safe_free (this->host);
125 safe_free (this->url);
126
127 err = extractHostInfo (_url, &this->host, &this->url);
128 if (err)
129 return err;
130
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 if (!hp) {
225 this->error = (int)WSAGetLastError ();
226 return WPTERR_WINSOCK_CONNECT;
227 }
228 memcpy (&srv.sin_addr, hp->h_addr, hp->h_length);
229 }
230 else {
231 addr = inet_addr (_host);
232 if (addr == INADDR_NONE) {
233 this->error = (int)WSAGetLastError ();
234 return WPTERR_WINSOCK_CONNECT;
235 }
236 memcpy (&srv.sin_addr, &addr, sizeof addr);
237 }
238
239 fd = socket (AF_INET, SOCK_STREAM, 0);
240 if (fd < 0) {
241 this->error = (int)WSAGetLastError ();
242 return WPTERR_WINSOCK_SOCKET;
243 }
244
245 if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR,
246 (const char*)&i, sizeof i)) {
247 closesocket (fd);
248 fd = 0;
249 this->error = (int)WSAGetLastError ();
250 return WPTERR_WINSOCK_SOCKET;
251 }
252
253 if (::connect (fd, (struct sockaddr *)&srv, sizeof srv)) {
254 closesocket (fd);
255 fd = 0;
256 this->error = (int)WSAGetLastError ();
257 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 if (n == -1) {
303 this->error = (int)WSAGetLastError ();
304 break;
305 }
306 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 {
336 char tmpbuf[2*MAX_PATH+1];
337 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 strncpy (tmpbuf, _url, DIM (tmpbuf)-1);
345
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 *_host = strdup (p);
354 p = strchr (_url, '/');
355 if (!p) /* document were given so we assume the root '/'. */
356 *new_url = strdup ("/");
357 else
358 *new_url = strdup (p);
359 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 p = (char*) malloc (strlen (name)+strlen (val)+strlen (fmt)+4);
371 sprintf (p, fmt, name, val);
372 addHeader (&req_headers, p);
373 safe_free (p);
374
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 _url++;
418 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 n += strlen (id[method]) + strlen (h_head)
430 + strlen (_url) + n + 2 + 1 + 4;
431 p = (char*)calloc (1, n+1);
432 if (!p)
433 BUG (0);
434 sprintf (p, h_head, id[method], _url, ver);
435 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 safe_free (p);
442 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 rc = readLine (buf, DIM (buf)-1, 1, &nn, NULL);
459 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 if (n == -1) {
495 this->error = (int)WSAGetLastError ();
496 return SOCKET_ERROR;
497 }
498 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 rc = readLine (buf, DIM (buf)-1, 1, &n, &eof);
508 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 return parseHeaders (&resp_headers);
561 }
562
563
564 /* Release all used structures. */
565 void
566 wHTTP::releaseHeaders (void)
567 {
568 http_head_t h;
569
570 while (resp_headers != NULL) {
571 h = resp_headers->next;
572 free (resp_headers);
573 resp_headers = h;
574 }
575 resp_headers = NULL;
576 while (req_headers != NULL) {
577 h = req_headers->next;
578 free (req_headers);
579 req_headers = h;
580 }
581 req_headers = NULL;
582 }
583
584
585 /* Destroy HTTP object. */
586 wHTTP::~wHTTP (void)
587 {
588 releaseHeaders();
589 safe_free (host);
590 safe_free (url);
591 if (fd != 0) {
592 closesocket (fd);
593 fd = 0;
594 }
595 }
596
597
598 /* Read @buflen bytes from the http stream into the buffer @buf. */
599 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 /* Write the buffer @buf to the http stream. */
626 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
636
637 /* Return the Winsock specific error code. */
638 int wHTTP::getErrorCode (void)
639 {
640 return error;
641 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26