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

Contents of /trunk/Src/wptHTTP.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 271 - (show annotations)
Sun Nov 5 08:57:45 2006 UTC (18 years, 3 months ago) by twoaday
File size: 12078 byte(s)


1 /* 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
58 if (extractHostInfo (_url, &this->host, &this->url))
59 return;
60
61 /* FIXME: we should throw an exception in case of an error. */
62 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 error = 0;
79 }
80
81
82 /* Perform a HTTP 'HEAD' request. */
83 int
84 wHTTP::head (const char *_url)
85 {
86 int err;
87
88 safe_free (host);
89 safe_free (url);
90
91 err = extractHostInfo (_url, &this->host, &this->url);
92 if (err)
93 return err;
94 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 int err;
108
109 safe_free (host);
110 safe_free (url);
111
112 err = extractHostInfo (_url, &this->host, &this->url);
113 if (err)
114 return err;
115
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 if (!hp) {
210 this->error = (int)WSAGetLastError ();
211 return WPTERR_WINSOCK_CONNECT;
212 }
213 memcpy (&srv.sin_addr, hp->h_addr, hp->h_length);
214 }
215 else {
216 addr = inet_addr (_host);
217 if (addr == INADDR_NONE) {
218 this->error = (int)WSAGetLastError ();
219 return WPTERR_WINSOCK_CONNECT;
220 }
221 memcpy (&srv.sin_addr, &addr, sizeof addr);
222 }
223
224 fd = socket (AF_INET, SOCK_STREAM, 0);
225 if (fd < 0) {
226 this->error = (int)WSAGetLastError ();
227 return WPTERR_WINSOCK_SOCKET;
228 }
229
230 if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR,
231 (const char*)&i, sizeof i)) {
232 closesocket (fd);
233 fd = 0;
234 this->error = (int)WSAGetLastError ();
235 return WPTERR_WINSOCK_SOCKET;
236 }
237
238 if (::connect (fd, (struct sockaddr *)&srv, sizeof srv)) {
239 closesocket (fd);
240 fd = 0;
241 this->error = (int)WSAGetLastError ();
242 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 if (n == -1) {
288 this->error = (int)WSAGetLastError ();
289 break;
290 }
291 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 {
321 char tmpbuf[2*MAX_PATH+1];
322 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 strncpy (tmpbuf, _url, DIM (tmpbuf)-1);
330
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 *_host = strdup (p);
339 p = strchr (_url, '/');
340 if (!p) /* document were given so we assume the root '/'. */
341 *new_url = strdup ("/");
342 else
343 *new_url = strdup (p);
344 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 p = (char*) malloc (strlen (name)+strlen (val)+strlen (fmt)+4);
356 sprintf (p, fmt, name, val);
357 addHeader (&req_headers, p);
358 safe_free (p);
359
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 _url++;
403 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 n += strlen (id[method]) + strlen (h_head)
415 + strlen (_url) + n + 2 + 1 + 4;
416 p = (char*)calloc (1, n+1);
417 if (!p)
418 BUG (0);
419 sprintf (p, h_head, id[method], _url, ver);
420 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 safe_free (p);
427 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 rc = readLine (buf, DIM (buf)-1, 1, &nn, NULL);
444 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 if (n == -1) {
480 this->error = (int)WSAGetLastError ();
481 return SOCKET_ERROR;
482 }
483 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 rc = readLine (buf, DIM (buf)-1, 1, &n, &eof);
493 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 safe_free (url);
568 safe_free (host);
569 if (fd != 0) {
570 closesocket (fd);
571 fd = 0;
572 }
573 }
574
575
576 /* Read @buflen bytes from the http stream into the buffer @buf. */
577 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 /* Write the buffer @buf to the http stream. */
604 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
614
615 /* Return the Winsock specific error code. */
616 int wHTTP::getErrorCode (void)
617 {
618 return error;
619 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26