9 #include "wvhttppool.h"
11 #include "wvsslstream.h"
15 #ifdef HAVE_EXECINFO_H
20 #define ETIMEDOUT WSAETIMEDOUT
23 WvHttpStream::WvHttpStream(
const WvIPPortAddr &_remaddr, WvStringParm _username,
24 bool _ssl, WvIPPortAddrTable &_pipeline_incompatible)
26 pipeline_incompatible(_pipeline_incompatible),
29 log(
"Opening server connection.\n");
33 in_chunk_trailer =
false;
34 pipeline_test_count = 0;
35 last_was_pipeline_test =
false;
37 enable_pipelining = global_enable_pipelining
38 && !pipeline_incompatible[target.remaddr];
44 sent_url_request =
false;
50 WvHttpStream::~WvHttpStream()
52 log(WvLog::Debug2,
"Deleting.\n");
55 #ifdef HAVE_EXECINFO_H
57 int count = backtrace(trace,
sizeof(trace)/
sizeof(trace[0]));
58 char** tracedump = backtrace_symbols(trace, count);
59 log(WvLog::Debug,
"TRACE");
60 for (
int i = 0; i < count; ++i)
61 log(WvLog::Debug,
":%s", tracedump[i]);
62 log(WvLog::Debug,
"\n");
68 log(
"Error was: %s\n", errstr());
75 log(
"close called\n");
78 #ifdef HAVE_EXECINFO_H
80 int count = backtrace(trace,
sizeof(trace)/
sizeof(trace[0]));
81 char** tracedump = backtrace_symbols(trace, count);
82 log(WvLog::Debug,
"TRACE");
83 for (
int i = 0; i < count; ++i)
84 log(WvLog::Debug,
":%s", tracedump[i]);
85 log(WvLog::Debug,
"\n");
92 if (enable_pipelining && max_requests > 1
93 && (pipeline_test_count < 1
94 || (pipeline_test_count == 1 && last_was_pipeline_test)))
95 pipelining_is_broken(2);
106 if (!msgurl && !urls.isempty())
107 msgurl = urls.first();
108 if (!msgurl && !waiting_urls.isempty())
109 msgurl = waiting_urls.first();
113 log(
"URL '%s' is FAILED (%s (%s))\n", msgurl->url,
geterr(),
122 log(
"curl is %s\n", curl->url);
129 void WvHttpStream::doneurl()
140 assert(curl != NULL);
141 WvString last_response(http_response);
142 log(
"Done URL: %s\n", curl->url);
146 in_chunk_trailer =
false;
149 last_was_pipeline_test = curl->pipeline_test;
151 if (last_was_pipeline_test)
153 pipeline_test_count++;
154 if (pipeline_test_count == 1)
155 start_pipeline_test(&curl->url);
156 else if (pipeline_test_response != last_response)
161 pipelining_is_broken(4);
164 pipeline_test_response = last_response;
167 assert(curl == urls.first());
170 sent_url_request =
false;
181 static WvString encode64(WvStringParm user, WvStringParm password)
190 static WvString fixnl(WvStringParm nonl)
195 for (cptr = nonl; cptr && *cptr; cptr++)
199 else if (*cptr ==
'\n')
212 if(!!url->url.getuser() && !!url->url.getpassword())
213 auth =
WvString(
"Authorization: Basic %s\n",
214 encode64(url->url.getuser(), url->url.getpassword()));
227 url->url.gethost(), url->url.getport(),
228 keepalive ?
"keep-alive" :
"close",
231 "Content-Length: %s\n", putstream_data.
used()) :
""),
233 !!url->headers ?
"\n" :
""));
241 log(
"Request #%s: %s\n", request_count, url->url);
242 write(request_str(url, url->pipeline_test
243 || request_count < max_requests));
244 write(putstream_data);
245 sent_url_request =
true;
250 void WvHttpStream::start_pipeline_test(
WvUrl *url)
253 "%s://%s:%s/wvhttp-pipeline-check-should-not-exist/",
254 url->getproto(), url->gethost(), url->getport()));
257 testurl->instream =
this;
258 send_request(testurl);
259 urls.append(testurl,
true,
"sent_running_url");
263 void WvHttpStream::request_next()
266 putstream_data.
zap();
270 if (request_count >= max_requests || waiting_urls.isempty())
274 if (!enable_pipelining && !urls.isempty())
280 waiting_urls.unlink_first();
283 if (enable_pipelining && !request_count && max_requests > 1)
284 start_pipeline_test(&url->url);
287 urls.append(url,
false,
"sent_running_url");
291 void WvHttpStream::pipelining_is_broken(
int why)
293 if (!pipeline_incompatible[target.remaddr])
295 pipeline_incompatible.add(
new WvIPPortAddr(target.remaddr),
true);
296 log(
"Pipelining is broken on this server (%s)! Disabling.\n", why);
311 if(url && url->putstream)
330 if(url && url->putstream && url->putstream->
post_select(si))
341 char buf[1024], *line;
349 log(WvLog::Debug4,
"urls count: %s\n", urls.count());
361 url->outstream->
seterr(ETIMEDOUT);
372 if (curl && !curl->outstream)
374 if (!(encoding == PostHeadInfinity
375 || encoding == PostHeadChunked
376 || encoding == PostHeadStream))
379 pipeline_test_count++;
380 last_was_pipeline_test =
false;
391 if(!sent_url_request && !urls.isempty())
399 if(url->putstream->
isok())
400 len = url->putstream->
read(putstream_data, 1024);
402 if(!url->putstream->
isok() || len == 0)
404 url->putstream = NULL;
418 log(WvLog::Debug4,
"Header: '%s'\n", line);
421 http_response = line;
427 if (last_was_pipeline_test
428 && http_response == pipeline_test_response)
430 pipelining_is_broken(1);
440 if (last_was_pipeline_test && !!http_response)
442 const char *cptr = strchr(http_response,
' ');
443 if (cptr && atoi(cptr+1) == 400)
445 pipelining_is_broken(3);
454 log(
"got unsolicited data.\n");
455 seterr(
"unsolicited data from server!");
459 if (!strncasecmp(line,
"Content-length: ", 16))
461 bytes_remaining = atoi(line+16);
462 encoding = ContentLength;
464 else if (!strncasecmp(line,
"Transfer-Encoding: ", 19)
465 && strstr(line+19,
"chunked"))
475 if ((p = strchr(line,
':')) != NULL)
482 outstream->headers.add(h,
true);
485 else if (strncasecmp(line,
"HTTP/", 5) == 0)
487 char *p = strchr(line,
' ');
493 outstream->version = line+5;
494 outstream->status = atoi(p+1);
503 in_chunk_trailer =
false;
505 "Starting data: %s (enc=%s)\n", bytes_remaining, encoding);
507 if (encoding == Unknown)
510 if (curl->method ==
"HEAD")
512 log(
"Got all headers.\n");
513 if (!enable_pipelining)
516 if (encoding == Infinity)
517 encoding = PostHeadInfinity;
518 else if (encoding == Chunked)
519 encoding = PostHeadChunked;
521 encoding = PostHeadStream;
526 else if (encoding == PostHeadInfinity
527 || encoding == PostHeadChunked
528 || encoding == PostHeadStream)
531 len =
read(chkbuf, 5);
536 if (len && strncmp(
reinterpret_cast<const char *
>(chkbuf.
peek(0, 5)),
539 if (encoding == PostHeadInfinity)
540 encoding = ChuckInfinity;
541 else if (encoding == PostHeadChunked)
542 encoding = ChuckChunked;
543 else if (encoding == PostHeadStream)
544 encoding = ChuckStream;
546 log(WvLog::Warning,
"WvHttpStream: inconsistent state.\n");
553 else if (encoding == ChuckInfinity)
555 len =
read(buf,
sizeof(buf));
557 log(WvLog::Debug5,
"Chucking %s bytes.\n", len);
561 else if (encoding == ChuckChunked && !bytes_remaining)
565 else if (encoding == ChuckChunked || encoding == ChuckStream)
567 if (bytes_remaining >
sizeof(buf))
568 len =
read(buf,
sizeof(buf));
570 len =
read(buf, bytes_remaining);
571 bytes_remaining -= len;
574 "Chucked %s bytes (%s bytes left).\n", len, bytes_remaining);
575 if (!bytes_remaining && encoding == ContentLength)
577 if (bytes_remaining && !
isok())
578 seterr(
"connection interrupted");
580 else if (encoding == Chunked && !bytes_remaining)
587 if (in_chunk_trailer)
590 log(WvLog::Debug4,
"Trailer: '%s'\n", line);
601 bytes_remaining = (size_t)strtoul(line, NULL, 16);
602 if (!bytes_remaining)
603 in_chunk_trailer =
true;
604 log(WvLog::Debug4,
"Chunk length is %s ('%s').\n",
605 bytes_remaining, line);
610 else if (encoding == Infinity)
615 len =
read(buf,
sizeof(buf));
620 log(WvLog::Debug5,
"Infinity: read %s bytes.\n", len);
621 if (curl && curl->outstream)
622 curl->outstream->
write(buf, len);
632 if (bytes_remaining >
sizeof(buf))
633 len =
read(buf,
sizeof(buf));
635 len =
read(buf, bytes_remaining);
639 bytes_remaining -= len;
642 "Read %s bytes (%s bytes left).\n", len, bytes_remaining);
643 if (curl && curl->outstream)
644 curl->outstream->
write(buf, len);
646 if (!bytes_remaining && encoding == ContentLength && curl)
649 if (bytes_remaining && !
isok())
650 seterr(
"connection interrupted");