22#include "wsresponse.h"
23#include "securesocket.h"
24#include "compressor.h"
31#define HTTP_TOKEN_MAXLEN 79
32#define HTTP_HEADER_MAXLEN 0x4000
33#define RESPONSE_BUFFER_SIZE 0x400
34#define RESPONSE_MAX_SIZE 0x20000
35#define CHUNK_MAX_SIZE 0x20000
36#define REQUEST_BUFFER_SIZE 0x1000
38using namespace NSROOT;
40void WSResponse::init(
const WSRequest &request,
int maxRedirs,
bool trustedLocation,
bool followAny)
42 p =
new _response(request);
43 while (0 < maxRedirs--)
45 int status = p->GetStatusCode();
46 if (status == 301 || status == 302)
49 URIParser uri(p->Redirection());
50 bool trusted = (uri.Scheme() && strncmp(
"https", uri.Scheme(), 5) == 0);
51 unsigned port = uri.Port();
53 port = (trusted ? 443 : 80);
54 bool samehost = (!uri.Host() || request.GetServer() == uri.Host());
55 bool sameorigin = (!uri.Host() || (samehost && request.GetPort() == port));
58 (samehost && (!trustedLocation || trusted)) ||
59 (followAny && (!trustedLocation || trusted))
62 DBG(DBG_DEBUG,
"%s: (%d) LOCATION = %s\n", __FUNCTION__, p->GetStatusCode(), p->Redirection().c_str());
63 WSRequest redir(request, uri);
67 redir.ClearHeader(ws_header_to_upperstr(WS_HEADER_Authorization));
68 redir.ClearHeader(
"COOKIE");
71 p =
new _response(redir);
79WSResponse::~WSResponse()
86bool WSResponse::ReadHeaderLine(NetSocket *socket,
const char *eol, std::string& line,
size_t *len)
88 char buf[RESPONSE_BUFFER_SIZE];
90 int p = 0, p_eol = 0, l_eol;
97 l_eol = strlen(s_eol);
102 if (socket->ReceiveData(&buf[p], 1) > 0)
104 if (buf[p++] == s_eol[p_eol])
106 if (++p_eol >= l_eol)
108 buf[p - l_eol] =
'\0';
117 if (p > (RESPONSE_BUFFER_SIZE - 2 - l_eol))
133 while (l < HTTP_HEADER_MAXLEN);
139WSResponse::_response::_response(
const WSRequest &request)
146, m_contentEncoding(WS_CENCODING_None)
148, m_contentEmpty(false)
149, m_contentChunked(false)
153, m_chunkBuffer(nullptr)
159 if (request.IsSecureURI())
160 m_socket = SSLSessionFactory::Instance().NewClientSocket();
164 DBG(DBG_ERROR,
"%s: create socket failed\n", __FUNCTION__);
165 else if (m_socket->Connect(request.GetServer().c_str(), request.GetPort(), SOCKET_RCVBUF_MINSIZE))
167 m_socket->SetReadAttempt(6);
168 if (!request.WriteMessage(*this))
169 DBG(DBG_WARN,
"%s: broken request\n", __FUNCTION__);
172 if (m_statusCode < 200)
173 DBG(DBG_WARN,
"%s: status %d\n", __FUNCTION__, m_statusCode);
174 else if (m_statusCode < 300)
176 else if (m_statusCode < 400)
177 m_successful = false;
178 else if (m_statusCode < 500)
179 DBG(DBG_ERROR,
"%s: bad request (%d)\n", __FUNCTION__, m_statusCode);
181 DBG(DBG_ERROR,
"%s: server error (%d)\n", __FUNCTION__, m_statusCode);
184 DBG(DBG_ERROR,
"%s: invalid response\n", __FUNCTION__);
188WSResponse::_response::~_response()
201bool WSResponse::_response::WriteRequestStream(
const char * data,
unsigned len)
205 if (!(m_chunkBuffer =
new char[REQUEST_BUFFER_SIZE]))
207 m_chunkPtr = m_chunkEOR = m_chunkBuffer;
208 m_chunkEnd = m_chunkBuffer + REQUEST_BUFFER_SIZE;
213 size_t s = m_chunkEnd - m_chunkPtr;
216 memcpy(m_chunkPtr, data, len);
222 memcpy(m_chunkPtr, data, s);
224 if (!m_socket->SendData(m_chunkBuffer, REQUEST_BUFFER_SIZE))
226 DBG(DBG_ERROR,
"%s: failed (%d)\n", __FUNCTION__, m_socket->GetErrNo());
227 delete [] m_chunkBuffer;
228 m_chunkBuffer = m_chunkPtr = m_chunkEOR = m_chunkEnd =
nullptr;
231 m_chunkPtr = m_chunkBuffer;
237bool WSResponse::_response::FlushRequestStream()
242 size_t s = m_chunkPtr - m_chunkBuffer;
243 if (s > 0 && !m_socket->SendData(m_chunkBuffer, s))
245 DBG(DBG_ERROR,
"%s: failed (%d)\n", __FUNCTION__, m_socket->GetErrNo());
248 delete [] m_chunkBuffer;
249 m_chunkBuffer = m_chunkPtr = m_chunkEOR = m_chunkEnd =
nullptr;
253bool WSResponse::_response::ReadResponse()
258 char token[HTTP_TOKEN_MAXLEN + 1];
259 int n = 0, token_len = 0;
263 while (WSResponse::ReadHeaderLine(m_socket, WS_CRLF, strread, &len))
267 if (sum > RESPONSE_MAX_SIZE)
270 const char *line = strread.c_str(), *val =
nullptr;
273 DBG(DBG_PROTO,
"%s: %s\n", __FUNCTION__, line);
282 if (len > 5 && 0 == memcmp(line,
"HTTP", 4) && 1 == sscanf(line,
"%*s %d", &status))
285 m_statusCode = status;
305 if ((line[0] ==
' ' || line[0] ==
'\t') && token_len)
315 else if ((val = strchr(line,
':')))
318 if ((token_len = val - line) > HTTP_TOKEN_MAXLEN)
319 token_len = HTTP_TOKEN_MAXLEN;
320 for (p = 0; p < token_len; ++p)
321 token[p] = toupper(line[p]);
322 token[token_len] = 0;
323 value_len = len - (val - line + 1);
324 while (value_len > 0 && (*(++val) ==
' ' || *val ==
'\t')) --value_len;
325 WSHeader& hv = m_headers[token];
326 hv.SetName(line, token_len);
333 token[token_len] = 0;
336 if (token_len && val)
338 std::string& newval = m_headers[token].Back().append(val);
339 switch (ws_header_from_upperstr(token))
342 m_etag.assign(newval);
344 case WS_HEADER_Server:
345 m_serverInfo.assign(newval);
347 case WS_HEADER_Location:
348 m_location.assign(newval);
350 case WS_HEADER_Content_Type:
353 case WS_HEADER_Content_Length:
355 int64_t num = atol(newval.c_str());
358 m_contentLength = (size_t)num;
359 m_contentEmpty = (num == 0);
363 case WS_HEADER_Content_Encoding:
364 m_contentEncoding = ws_cencoding_from_str(newval.c_str());
365 if (m_contentEncoding == WS_CENCODING_UNKNOWN)
366 DBG(DBG_ERROR,
"%s: unsupported content encoding (%s)\n", __FUNCTION__, newval.c_str());
368 case WS_HEADER_Transfer_Encoding:
369 if (newval.find(
"chunked") != std::string::npos)
371 m_contentChunked =
true;
384int WSResponse::_response::ReadChunk(
void *buf,
size_t buflen)
387 if (m_contentChunked)
390 if (m_chunkPtr >= m_chunkEnd)
394 delete [] m_chunkBuffer;
395 m_chunkBuffer = m_chunkPtr = m_chunkEOR = m_chunkEnd =
nullptr;
398 while (WSResponse::ReadHeaderLine(m_socket, WS_CRLF, strread, &len) && len == 0);
399 DBG(DBG_PROTO,
"%s: chunked data (%s)\n", __FUNCTION__, strread.c_str());
400 std::string chunkStr(
"0x0");
402 if (strread.empty() || sscanf(chunkStr.append(strread.substr(0, strread.find(
';'))).c_str(),
"%x", &chunkSize) != 1)
407 if (chunkSize > CHUNK_MAX_SIZE)
409 DBG(DBG_ERROR,
"%s: chunk-size overflow (req=%u) (max=%u)\n", __FUNCTION__, chunkSize, (
unsigned)CHUNK_MAX_SIZE);
412 if (!(m_chunkBuffer =
new char[chunkSize]))
414 m_chunkPtr = m_chunkEOR = m_chunkBuffer;
415 m_chunkEnd = m_chunkBuffer + chunkSize;
420 while (WSResponse::ReadHeaderLine(m_socket, WS_CRLF, strread, &len) && len != 0);
425 if (m_chunkPtr >= m_chunkEOR)
429 m_chunkEOR += m_socket->ReceiveData(m_chunkEOR, m_chunkEnd - m_chunkEOR);
431 if ((s = m_chunkEOR - m_chunkPtr) < 0)
433 if (buflen < (
size_t)s)
435 memcpy(buf, m_chunkPtr, s);
442int WSResponse::_response::SocketStreamReader(
void *hdl,
void *buf,
int sz)
444 _response *resp =
static_cast<_response*
>(hdl);
449 if (!resp->m_contentLength)
450 s = (int)resp->m_socket->ReceiveData(buf, sz);
451 else if (resp->m_contentLength > resp->m_consumed)
453 size_t len = resp->m_contentLength - resp->m_consumed;
454 s = (int)resp->m_socket->ReceiveData(buf, len > (
size_t)sz ? (
size_t)sz : len);
457 resp->m_consumed = resp->m_contentLength;
459 resp->m_consumed += s;
463int WSResponse::_response::ChunkStreamReader(
void *hdl,
void *buf,
int sz)
465 _response *resp =
static_cast<_response*
>(hdl);
466 if (resp && resp->m_chunkNext)
468 int s = resp->ReadChunk(buf, sz);
470 resp->m_chunkNext =
false;
476int WSResponse::_response::ReadContent(
char* buf,
size_t buflen)
478 if (!m_contentChunked)
480 if (m_contentEncoding == WS_CENCODING_None)
482 if (m_contentLength > m_consumed)
484 size_t len = m_contentLength - m_consumed;
485 int s = (int)m_socket->ReceiveData(buf, len > buflen ? buflen : len);
487 m_consumed = m_contentLength;
492 else if (!m_contentEmpty && m_hasContent)
495 int s = (int)m_socket->ReceiveData(buf, buflen);
499 else if (m_contentEncoding == WS_CENCODING_Gzip || m_contentEncoding == WS_CENCODING_Deflate)
502 if (m_decoder ==
nullptr)
503 m_decoder =
new Decompressor(&SocketStreamReader,
this, (m_contentEncoding == WS_CENCODING_Gzip));
504 if (m_decoder->HasOutputData())
505 s = (int)m_decoder->ReadOutput(buf, buflen);
506 if (s == 0 && !m_decoder->IsCompleted())
508 if (m_decoder->HasStreamError())
509 DBG(DBG_ERROR,
"%s: decoding failed: stream error\n", __FUNCTION__);
510 else if (m_decoder->HasBufferError())
511 DBG(DBG_ERROR,
"%s: decoding failed: buffer error\n", __FUNCTION__);
513 DBG(DBG_ERROR,
"%s: decoding failed\n", __FUNCTION__);
521 if (m_contentEncoding == WS_CENCODING_None)
525 int s = ReadChunk(buf, buflen);
531 else if (m_contentEncoding == WS_CENCODING_Gzip || m_contentEncoding == WS_CENCODING_Deflate)
534 if (m_decoder ==
nullptr)
535 m_decoder =
new Decompressor(&ChunkStreamReader,
this, (m_contentEncoding == WS_CENCODING_Gzip));
536 if (m_decoder->HasOutputData())
537 s = (int)m_decoder->ReadOutput(buf, buflen);
538 if (s == 0 && !m_decoder->IsCompleted())
540 if (m_decoder->HasStreamError())
541 DBG(DBG_ERROR,
"%s: decoding failed: stream error\n", __FUNCTION__);
542 else if (m_decoder->HasBufferError())
543 DBG(DBG_ERROR,
"%s: decoding failed: buffer error\n", __FUNCTION__);
545 DBG(DBG_ERROR,
"%s: decoding failed\n", __FUNCTION__);
554bool WSResponse::_response::GetHeaderValue(
const std::string& header, std::string& value)
557 VARS::const_iterator it = m_headers.find(header);
558 if (it == m_headers.end())
560 value.assign(it->second.Last());
564const std::string& WSResponse::_response::GetHeaderValue(
const std::string& header)
const
566 static std::string emptyStr =
"";
567 VARS::const_iterator it = m_headers.find(header);
568 if (it != m_headers.end())
569 return it->second.Last();
char * m_chunkEOR
The end of received data in the chunk.
char * m_chunkPtr
The next position to read data from the chunk.
char * m_chunkEnd
The end of the chunk buffer.
char * m_chunkBuffer
The chunk data buffer.