97 addParametersToRequestBody (addParametersToBody),
98 hasBodyDataToSend (addParametersToRequestBody || url.hasBodyDataToSend()),
99 httpRequestCmd (hasBodyDataToSend ?
"POST" :
"GET")
110 void withExtraHeaders (
const String& extraHeaders)
115 headers << extraHeaders;
121 void withCustomRequestCommand (
const String& customRequestCommand) { httpRequestCmd = customRequestCommand; }
122 void withConnectionTimeout (
int timeoutInMs) { timeOutMs = timeoutInMs; }
123 void withNumRedirectsToFollow (
int maxRedirectsToFollow) { numRedirectsToFollow = maxRedirectsToFollow; }
124 int getStatusCode()
const {
return statusCode; }
125 StringPairArray getRequestHeaders()
const {
return WebInputStream::parseHttpHeaders (headers); }
133 for (
int i = 0; i < headerLines.
size(); ++i)
135 auto& headersEntry = headerLines[i];
136 auto key = headersEntry.upToFirstOccurrenceOf (
": ",
false,
false);
137 auto value = headersEntry.fromFirstOccurrenceOf (
": ",
false,
false);
138 auto previousValue = responseHeaders[key];
139 responseHeaders.
set (key, previousValue.isEmpty() ? value : (previousValue +
"," + value));
143 return responseHeaders;
151 if (hasBeenCancelled)
155 address = url.
toString (! addParametersToRequestBody);
156 statusCode = createConnection (listener, numRedirectsToFollow);
158 return statusCode != 0;
165 hasBeenCancelled =
true;
173 bool isError()
const {
return socketHandle < 0; }
174 bool isExhausted() {
return finished; }
175 int64 getPosition() {
return position; }
176 int64 getTotalLength() {
return contentLength; }
178 int read (
void* buffer,
int bytesToRead)
180 if (finished || isError())
183 if (isChunked && ! readingChunk)
185 if (position >= chunkEnd)
193 if (read (&c, 1) != 1 || c !=
'\r'
194 || read (&c, 1) != 1 || c !=
'\n')
201 while (chunkLengthBuffer.
getDataSize() < 512 && ! (finished || isError()))
203 if (read (&c, 1) != 1)
226 chunkEnd += chunkSize;
229 if (bytesToRead > chunkEnd - position)
230 bytesToRead =
static_cast<int> (chunkEnd - position);
233 pollfd pfd { socketHandle, POLLIN, 0 };
235 if (
poll (&pfd, 1, timeOutMs) <= 0)
238 auto bytesRead =
jmax (0, (
int)
recv (socketHandle, buffer, (
size_t) bytesToRead, MSG_WAITALL));
244 position += bytesRead;
249 bool setPosition (
int64 wantedPos)
254 if (wantedPos != position)
258 if (wantedPos < position)
261 auto numBytesToSkip = wantedPos - position;
262 auto skipBufferSize = (
int)
jmin (numBytesToSkip, (
int64) 16384);
265 while (numBytesToSkip > 0 && ! isExhausted())
266 numBytesToSkip -= read (temp, (
int)
jmin (numBytesToSkip, (
int64) skipBufferSize));
278 int socketHandle = -1, levelsOfRedirection = 0;
282 int64 contentLength = -1, position = 0;
283 bool finished =
false;
284 const bool addParametersToRequestBody, hasBodyDataToSend;
286 int numRedirectsToFollow = 5;
289 bool isChunked =
false, readingChunk =
false;
291 bool hasBeenCancelled =
false;
293 void closeSocket (
bool resetLevelsOfRedirection =
true)
297 if (socketHandle >= 0)
299 ::shutdown (socketHandle, SHUT_RDWR);
300 ::close (socketHandle);
305 if (resetLevelsOfRedirection)
306 levelsOfRedirection = 0;
313 if (hasBodyDataToSend)
314 WebInputStream::createHeadersAndPostData (url,
317 addParametersToRequestBody);
325 timeOutTime = 0xffffffff;
327 timeOutTime += (
uint32) timeOutMs;
329 String hostName, hostPath;
332 if (! decomposeURL (address, hostName, hostPath, hostPort))
335 String serverName, proxyName, proxyPath;
341 if (proxyURL.startsWithIgnoreCase (
"http://"))
343 if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort))
346 serverName = proxyName;
351 serverName = hostName;
355 struct addrinfo hints;
358 hints.ai_family = AF_UNSPEC;
359 hints.ai_socktype = SOCK_STREAM;
360 hints.ai_flags = AI_NUMERICSERV;
362 struct addrinfo* result =
nullptr;
370 socketHandle = hasBeenCancelled ? -1
371 :
socket (result->ai_family, result->ai_socktype, 0);
374 if (socketHandle == -1)
380 int receiveBufferSize = 16384;
381 setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (
char*) &receiveBufferSize,
sizeof (receiveBufferSize));
382 setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE,
nullptr, 0);
385 setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0);
388 if (::connect (socketHandle, result->ai_addr, result->ai_addrlen) == -1)
398 const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, proxyName, proxyPort, hostPath, address,
399 headers, postData, httpRequestCmd));
401 if (! sendHeader (socketHandle, requestHeader, timeOutTime, owner, listener))
408 auto responseHeader = readResponse (timeOutTime);
411 if (responseHeader.isNotEmpty())
415 auto status = responseHeader.fromFirstOccurrenceOf (
" ",
false,
false)
416 .substring (0, 3).getIntValue();
418 auto location = findHeaderItem (headerLines,
"Location:");
420 if (++levelsOfRedirection <= numRedirects
421 && status >= 300 && status < 400
422 && location.isNotEmpty() && location != address)
424 if (! (location.startsWithIgnoreCase (
"http://")
425 || location.startsWithIgnoreCase (
"https://")
426 || location.startsWithIgnoreCase (
"ftp://")))
429 if (location.startsWithChar (
'/'))
432 location = address +
"/" + location;
436 return createConnection (listener, numRedirects);
439 auto contentLengthString = findHeaderItem (headerLines,
"Content-Length:");
441 if (contentLengthString.isNotEmpty())
442 contentLength = contentLengthString.getLargeIntValue();
444 isChunked = (findHeaderItem (headerLines,
"Transfer-Encoding:") ==
"chunked");
456 int numConsecutiveLFs = 0;
459 while (numConsecutiveLFs < 2
462 && ! (finished || isError()))
466 if (read (&c, 1) != 1)
474 numConsecutiveLFs = 0;
479 if (header.startsWithIgnoreCase (
"HTTP/"))
488 dest <<
"\r\n" << key <<
' ' << value;
494 dest << httpRequestCmd <<
' ' << path <<
" HTTP/1.1\r\nHost: " << host;
502 const String& proxyName,
int proxyPort,
505 const String& httpRequestCmd)
510 writeHost (header, httpRequestCmd, hostPath, hostName, hostPort);
512 writeHost (header, httpRequestCmd, originalURL, proxyName, proxyPort);
517 writeValueIfNotPresent (header, userHeaders,
"Connection:",
"close");
519 const auto postDataSize = postData.
getSize();
520 const auto hasPostData = postDataSize > 0;
523 writeValueIfNotPresent (header, userHeaders,
"Content-Length:",
String ((
int) postDataSize));
526 header <<
"\r\n" << userHeaders;
528 header <<
"\r\n\r\n";
536 static bool sendHeader (
int socketHandle,
const MemoryBlock& requestHeader,
uint32 timeOutTime,
539 size_t totalHeaderSent = 0;
541 while (totalHeaderSent < requestHeader.
getSize())
546 auto numToSend =
jmin (1024, (
int) (requestHeader.
getSize() - totalHeaderSent));
548 if (
send (socketHandle,
static_cast<const char*
> (requestHeader.
getData()) + totalHeaderSent, (
size_t) numToSend, 0) != numToSend)
551 totalHeaderSent += (
size_t) numToSend;
562 if (! url.startsWithIgnoreCase (
"http://"))
565 auto nextSlash = url.indexOfChar (7,
'/');
566 auto nextColon = url.indexOfChar (7,
':');
568 if (nextColon > nextSlash && nextSlash > 0)
573 host = url.substring (7, nextColon);
576 port = url.substring (nextColon + 1, nextSlash).getIntValue();
578 port = url.substring (nextColon + 1).getIntValue();
585 host = url.substring (7, nextSlash);
587 host = url.substring (7);
591 path = url.substring (nextSlash);
600 for (
int i = 0; i < lines.
size(); ++i)
601 if (lines[i].startsWithIgnoreCase (itemName))
602 return lines[i].substring (itemName.
length()).
trim();