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

Contents of /trunk/Src/wptHTTP.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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


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 releaseMem ();
66
67 if (extractHostInfo (_url, &this->host, &this->url))
68 return -1;
69
70 if (!sendRequest (this->host, this->port, this->url))
71 return parseResponse (&statcode);
72
73 return 0;
74 }
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 error = 0;
89 }
90
91
92 /* Perform a HTTP 'HEAD' request. */
93 int
94 wHTTP::head (const char *_url)
95 {
96 int err;
97
98 safe_free (host);
99 safe_free (url);
100
101 err = extractHostInfo (_url, &this->host, &this->url);
102 if (err)
103 return err;
104 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 int err;
118
119 safe_free (host);
120 safe_free (url);
121
122 err = extractHostInfo (_url, &this->host, &this->url);
123 if (err)
124 return err;
125
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 if (!hp) {
220 this->error = (int)WSAGetLastError ();
221 return WPTERR_WINSOCK_CONNECT;
222 }
223 memcpy (&srv.sin_addr, hp->h_addr, hp->h_length);
224 }
225 else {
226 addr = inet_addr (_host);
227 if (addr == INADDR_NONE) {
228 this->error = (int)WSAGetLastError ();
229 return WPTERR_WINSOCK_CONNECT;
230 }
231 memcpy (&srv.sin_addr, &addr, sizeof addr);
232 }
233
234 fd = socket (AF_INET, SOCK_STREAM, 0);
235 if (fd < 0) {
236 this->error = (int)WSAGetLastError ();
237 return WPTERR_WINSOCK_SOCKET;
238 }
239
240 if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR,
241 (const char*)&i, sizeof i)) {
242 closesocket (fd);
243 fd = 0;
244 this->error = (int)WSAGetLastError ();
245 return WPTERR_WINSOCK_SOCKET;
246 }
247
248 if (::connect (fd, (struct sockaddr *)&srv, sizeof srv)) {
249 closesocket (fd);
250 fd = 0;
251 this->error = (int)WSAGetLastError ();
252 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 if (n == -1) {
298 this->error = (int)WSAGetLastError ();
299 break;
300 }
301 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 {
331 char tmpbuf[2*MAX_PATH+1];
332 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 strncpy (tmpbuf, _url, DIM (tmpbuf)-1);
340
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 *_host = strdup (p);
349 p = strchr (_url, '/');
350 if (!p) /* document were given so we assume the root '/'. */
351 *new_url = strdup ("/");
352 else
353 *new_url = strdup (p);
354 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 p = (char*) malloc (strlen (name)+strlen (val)+strlen (fmt)+4);
366 sprintf (p, fmt, name, val);
367 addHeader (&req_headers, p);
368 safe_free (p);
369
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 _url++;
413 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 n += strlen (id[method]) + strlen (h_head)
425 + strlen (_url) + n + 2 + 1 + 4;
426 p = (char*)calloc (1, n+1);
427 if (!p)
428 BUG (0);
429 sprintf (p, h_head, id[method], _url, ver);
430 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 safe_free (p);
437 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 rc = readLine (buf, DIM (buf)-1, 1, &nn, NULL);
454 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 if (n == -1) {
490 this->error = (int)WSAGetLastError ();
491 return SOCKET_ERROR;
492 }
493 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 rc = readLine (buf, DIM (buf)-1, 1, &n, &eof);
503 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 /* Release all used structures. */
563 void
564 wHTTP::releaseMem (void)
565 {
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 resp_headers = NULL;
574 while (req_headers) {
575 h = req_headers->next;
576 free (req_headers);
577 req_headers = h;
578 }
579 req_headers = NULL;
580 safe_free (url);
581 safe_free (host);
582 }
583
584
585 /* Destroy HTTP object. */
586 wHTTP::~wHTTP (void)
587 {
588 releaseMem ();
589 if (fd != 0) {
590 closesocket (fd);
591 fd = 0;
592 }
593 }
594
595
596 /* Read @buflen bytes from the http stream into the buffer @buf. */
597 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 /* Write the buffer @buf to the http stream. */
624 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
634
635 /* Return the Winsock specific error code. */
636 int wHTTP::getErrorCode (void)
637 {
638 return error;
639 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26