9 #include "wvhttppool.h" 11 #include "wvsslstream.h" 15 #ifdef HAVE_EXECINFO_H 20 #define ETIMEDOUT WSAETIMEDOUT 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];
42 cloned =
new WvSSLStream(static_cast<WvFDStream*>(cloned));
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;
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",
230 (putstream_data.used() > 0 ?
WvString(
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");
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
virtual bool isok() const
return true if the stream is actually usable right now
virtual void pre_select(SelectInfo &si)
pre_select() sets up for eventually calling ::select().
SSL Stream, handles SSLv2, SSLv3, and TLS Methods - If you want it to be a server, then you must feed the constructor a WvX509Mgr object.
virtual int geterr() const
If isok() is false, return the system error number corresponding to the error, -1 for a special error...
virtual void close()
Close the stream if it is open; isok() becomes false from now on.
char * getline(time_t wait_msec=0, char separator= '\n', int readahead=1024)
Read up to one line of data from the stream and return a pointer to the internal buffer containing th...
bool alarm_was_ticking
This will be true during callback execution if the callback was triggered by the alarm going off...
virtual void execute()
The callback() function calls execute(), and then calls the user- specified callback if one is define...
An IP+Port address also includes a port number, with the resulting form www.xxx.yyy.zzz:pppp.
virtual bool post_select(SelectInfo &si)
post_select() is called after ::select(), and returns true if this object is now ready.
virtual size_t write(const void *buf, size_t count)
Write data to the stream.
virtual void pre_select(SelectInfo &si)
pre_select() sets up for eventually calling ::select().
virtual void pre_select(SelectInfo &si)
pre_select() sets up for eventually calling ::select().
char * edit()
make the string editable, and return a non-const (char*)
virtual bool post_select(SelectInfo &si)
post_select() is called after ::select(), and returns true if this object is now ready.
the data structure used by pre_select()/post_select() and internally by select(). ...
A SelectRequest is a convenient way to remember what we want to do to a particular stream: read from ...
virtual void execute()
The callback() function calls execute(), and then calls the user- specified callback if one is define...
virtual bool isok() const
return true if the stream is actually usable right now
virtual void close()
Close this stream.
virtual void seterr(int _errnum)
Override seterr() from WvError so that it auto-closes the stream.
const T * peek(int offset, size_t count)
Returns a const pointer into the buffer at the specified offset to the specified number of elements w...
char * trim_string(char *string)
Trims whitespace from the beginning and end of the character string, including carriage return / line...
WvString is an implementation of a simple and efficient printable-string class.
void alarm(time_t msec_timeout)
set an alarm, ie.
virtual size_t read(void *buf, size_t count)
read a data block on the stream.
bool flushstrstr(WvStringParm instr, WvString &outstr, bool finish=false)
Flushes data through the encoder from a string to a string.
virtual void unread(WvBuf &outbuf, size_t count)
Puts data back into the stream's internal buffer.
virtual bool post_select(SelectInfo &si)
post_select() is called after ::select(), and returns true if this object is now ready.
WvString getstr()
Returns the entire buffer as a null-terminated WvString.
virtual void close()
Close this stream.