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

Contents of /trunk/Src/wptHTTP.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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


1 /* wptHTTP.cpp - Generic HTTP support
2 * Copyright (C) 2004-2007, 2009 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) && strstr (n->d, name)) {
202 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 NetHTTP::connect (const char *_host, int _port)
213 {
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 if (!hp) {
226 this->error = (int)WSAGetLastError ();
227 return WPTERR_WINSOCK_CONNECT;
228 }
229 memcpy (&srv.sin_addr, hp->h_addr, hp->h_length);
230 }
231 else {
232 addr = inet_addr (_host);
233 if (addr == INADDR_NONE) {
234 this->error = (int)WSAGetLastError ();
235 return WPTERR_WINSOCK_CONNECT;
236 }
237 memcpy (&srv.sin_addr, &addr, sizeof addr);
238 }
239
240 fd = socket (AF_INET, SOCK_STREAM, 0);
241 if (fd < 0) {
242 this->error = (int)WSAGetLastError ();
243 return WPTERR_WINSOCK_SOCKET;
244 }
245
246 if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR,
247 (const char*)&i, sizeof i)) {
248 closesocket (fd);
249 fd = 0;
250 this->error = (int)WSAGetLastError ();
251 return WPTERR_WINSOCK_SOCKET;
252 }
253
254 if (::connect (fd, (struct sockaddr *)&srv, sizeof (srv))) {
255 closesocket (fd);
256 fd = 0;
257 this->error = (int)WSAGetLastError ();
258 return WPTERR_WINSOCK_CONNECT;
259 }
260
261 return 0;
262 }
263
264
265 /* Return 0 if the next recv() call would be block. */
266 int
267 NetHTTP::isDataAvailable (int _fd)
268 {
269 struct timeval tv;
270 fd_set inp;
271
272 FD_ZERO (&inp);
273 FD_SET (_fd, &inp);
274 tv.tv_sec = 1;
275 tv.tv_usec = 0;
276
277 int n = select (_fd+1, &inp, NULL, NULL, &tv);
278 if (n && FD_ISSET (_fd, &inp))
279 return n;
280 return 0;
281 }
282
283
284 /* 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 int
288 NetHTTP::readLine (char *buf, unsigned int nbuf,
289 int nonblock, int *nn, int *eof)
290 {
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 if (n == -1) {
307 this->error = (int)WSAGetLastError ();
308 break;
309 }
310 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 NetHTTP::extractHostInfo (const char *_url, char **_host, char **new_url)
339 {
340 char tmpbuf[2*MAX_PATH+1];
341 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 strncpy (tmpbuf, _url, DIM (tmpbuf)-1);
349
350 p = (char*)"http://";
351 if (strlen (_url) < 10 || strncmp (_url, p, strlen (p)))
352 return WPTERR_GENERAL;
353 _url += strlen (p);
354 p = strtok (tmpbuf+7, "/");
355 if (!p)
356 return WPTERR_GENERAL;
357 *_host = strdup (p);
358 p = strchr (_url, '/');
359 /* if no document were given so we assume the root '/'. */
360 *new_url = !p? strdup ("/") : strdup (p);
361
362 return 0;
363 }
364
365
366 /* Add a request header. */
367 int
368 NetHTTP::addRequestHeader (const char *name, const char *val)
369 {
370 StringBuffer p;
371
372 p = p + name + ": " + val;
373 addHeader (&req_headers, p.getBuffer ());
374 return 0;
375 }
376
377
378 void
379 NetHTTP::setVersion (int _ver)
380 {
381 if (_ver > 9)
382 _ver = 0;
383 this->ver = _ver;
384 }
385
386
387 int
388 NetHTTP::addRequestHeader (const char *name, unsigned int val)
389 {
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 NetHTTP::sendRequest (const char *_host, int _port, const char *_url)
401 {
402 const char *id[] = {"GET", "HEAD", "PUT", "POST"};
403 http_head_t h;
404 StringBuffer out;
405 int n;
406
407 if (this->fd == 0) {
408 int rc = connect (_host, _port);
409 if (rc)
410 return rc;
411 }
412
413 if (*_url == '/')
414 _url++;
415 if (_url == NULL)
416 _url = "";
417
418 addRequestHeader ("Host", _host);
419 if (ver < 1)
420 addRequestHeader ("Connection", "close");
421
422 out = out + id[method] + " " + "/" + _url + " " + "HTTP/1." + (int)ver + "\r\n";
423 for (h = req_headers; h; h = h->next)
424 out = out + h->d + "\r\n";
425 out = out + "\r\n";
426
427 n = write (out.getBuffer (), strlen (out.getBuffer ()));
428 //log_box ("debug", 0, "%d", n);
429 return n > 0? 0 : WPTERR_GENERAL;
430 }
431
432
433 /* Parse all response resp_headers. */
434 int
435 NetHTTP::parseHeaders (http_head_t *r_head)
436 {
437 char buf[300];
438 int nn;
439 int rc;
440
441 if (!r_head)
442 return WPTERR_GENERAL;
443
444 do {
445 rc = readLine (buf, DIM (buf)-1, 1, &nn, NULL);
446 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 /* Default buffer size */
457 #define BUFSIZE 1024
458
459
460 /* Read data from the response and write it to @out. */
461 int
462 NetHTTP::readData (FILE *out)
463 {
464 const char *val;
465 char buf[BUFSIZE+1];
466 int nlen, nn = 0, n, eof=0;
467 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 n = recv (fd, buf, nlen > BUFSIZE? BUFSIZE : nlen, 0);
485 if (n == -1) {
486 this->error = (int)WSAGetLastError ();
487 return SOCKET_ERROR;
488 }
489 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 rc = readLine (buf, DIM (buf)-1, 1, &n, &eof);
499 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 NetHTTP::parseResponse (int *_statcode)
517 {
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 rc = readLine (buf, DIM (buf)-1, 1, &nn, NULL);
526 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 rc = parseHeaders (&resp_headers);
552 if (rc)
553 return rc;
554 return 0;
555 }
556
557
558 /* Destroy HTTP object. */
559 NetHTTP::~NetHTTP (void)
560 {
561 http_head_t h;
562
563 while (resp_headers) {
564 h = resp_headers->next;
565 free (resp_headers);
566 resp_headers = h;
567 }
568 while (req_headers) {
569 h = req_headers->next;
570 free (req_headers);
571 req_headers = h;
572 }
573 safe_free (url);
574 safe_free (host);
575 if (fd != 0) {
576 closesocket (fd);
577 fd = 0;
578 }
579 }
580
581
582 /* Read @buflen bytes from the http stream into the buffer @buf. */
583 int
584 NetHTTP::read (void *buf, unsigned int buflen)
585 {
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 if (nleft < 0)
604 nleft = 0;
605 }
606 return n;
607 }
608
609
610 /* 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 int
614 NetHTTP::write (const void *buf, unsigned int buflen)
615 {
616 const char *buffer = (const char*)buf;
617 unsigned int bytesleft = buflen;
618 int nwritten;
619
620 if (this->fd == 0)
621 return -1;
622
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 }
632
633
634 /* Return the Winsock specific error code. */
635 int NetHTTP::getErrorCode (void)
636 {
637 return error;
638 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26