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

Contents of /trunk/Src/wptHTTP.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 314 - (show annotations)
Sun May 13 09:44:03 2007 UTC (17 years, 9 months ago) by twoaday
File size: 12654 byte(s)


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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26