📄 sbinethttpstream.cpp
字号:
VXIinetResult
SBinetHttpStream::Open(VXIint32 flags,
const VXIMap *properties,
VXIMap *streamInfo)
{
VXIinetResult rc;
// Make a copy in case of redirect.
SBinetString initialUrl = _url->getAbsolute();
const int maxRedirect = 5;
int nbRedirect = 0;
time_t requestTime = (time_t) -1;
for (;;)
{
// Set up request
rc = VXIinet_RESULT_NON_FATAL_ERROR;
_HttpStatus = HTTP_UNDEFINED;
requestTime = time(NULL);
_chunked = false;
_leftToRead = (unsigned)~0;
_closeConnection = !SBinetChannel::getUsePersistentConnections();
_connectionAborted = FALSE;
if (_method == GET_METHOD)
rc = doGet(flags, properties, streamInfo);
else if (_method == POST_METHOD)
rc = doPost(flags, properties, streamInfo);
if (rc != VXIinet_RESULT_SUCCESS)
{
_closeConnection = TRUE;
break;
}
else if ((rc = getHeaderInfo(streamInfo)) != VXIinet_RESULT_SUCCESS)
{
if (_connectionAborted)
{
_closeConnection = TRUE;
Close();
// Clear the streamInfo map so that previous fetch attributes do not
// remain in the map.
if (streamInfo != NULL)
clearMap(streamInfo);
continue;
}
else
{
_closeConnection = TRUE;
break;
}
}
// Avoid race condition on 100-Continue time-out. If we got a CONTINUE
// from previous call to getHeaderInfo, we need to get it again.
if (_HttpStatus == SBinetHttpUtils::HTTP_CONTINUE &&
(rc = getHeaderInfo(streamInfo)) != VXIinet_RESULT_SUCCESS)
{
_closeConnection = TRUE;
break;
}
if (_HttpStatus >= 300 && _HttpStatus <= 399 &&
handleRedirect(streamInfo))
{
// perform a reset on the stream.
Close();
if (++nbRedirect > maxRedirect)
{
Error(229, L"%s%s", L"URL", initialUrl.c_str());
return VXIinet_RESULT_FETCH_ERROR;
}
// Clear the streamInfo map to avoid previous fetch attribute to remain
// in the map.
if (streamInfo != NULL)
clearMap(streamInfo);
}
else
break;
}
if (streamInfo != NULL && _HttpStatus >= 100 && _HttpStatus <= 999)
{
VXIMapSetProperty(streamInfo, INET_INFO_HTTP_STATUS,
(VXIValue*) VXIIntegerCreate(_HttpStatus));
}
switch (_HttpStatus)
{
case SBinetHttpUtils::HTTP_OK:
if (streamInfo != NULL)
{
processHeaderInfo(streamInfo);
setValidatorInfo(streamInfo, requestTime);
}
rc = VXIinet_RESULT_SUCCESS;
break;
case SBinetHttpUtils::HTTP_NOT_MODIFIED:
if (streamInfo != NULL) setValidatorInfo(streamInfo, requestTime);
Close();
rc = VXIinet_RESULT_NOT_MODIFIED;
break;
default:
Close();
// Map the HTTP error code
if (_HttpStatus != HTTP_UNDEFINED)
{
const VXIchar *errorDesc;
rc = MapError(_HttpStatus, &errorDesc);
Error(219, L"%s%s%s%s%s%d%s%s", L"URL", initialUrl.c_str(),
L"Method", (_method == GET_METHOD) ? L"GET" : L"POST",
L"HTTPStatus", _HttpStatus, L"HTTPStatusDescription", errorDesc);
}
else
{
Error(217, L"%s%s", L"URL", initialUrl.c_str());
rc = VXIinet_RESULT_NON_FATAL_ERROR;
}
break;
}
return rc;
}
VXIinetResult SBinetHttpStream::Close()
{
if (_connection != NULL)
{
if (_closeConnection)
delete _connection;
else
{
// First make sure there is nothing left in the body.
VXIinetResult rc = skipBody();
if (rc == VXIinet_RESULT_SUCCESS)
_channel->putHttpConnection(_connection);
else
delete _connection;
}
_connection = NULL;
_inputStream = NULL;
_outputStream = NULL;
}
return(VXIinet_RESULT_SUCCESS);
}
VXIinetResult SBinetHttpStream::skipBody()
{
// These HTTP status code do not allow for a body.
if (_HttpStatus == SBinetHttpUtils::HTTP_NOT_MODIFIED ||
_HttpStatus == SBinetHttpUtils::HTTP_NO_DATA ||
(_HttpStatus >= 100 && _HttpStatus <= 199))
return VXIinet_RESULT_SUCCESS;
VXIinetResult rc;
VXIbyte buffer[1024];
while ((rc = Read(buffer, sizeof(buffer), NULL)) == VXIinet_RESULT_SUCCESS);
if (rc == VXIinet_RESULT_END_OF_STREAM)
rc = VXIinet_RESULT_SUCCESS;
return rc;
}
SBinetHttpStream::EncodingType SBinetHttpStream::getEncodingType(const VXIMap *properties)
{
SBinetHttpStream::EncodingType encodingType = TYPE_URL_ENCODED;
// Get the submit MIME type from the properties, if defined
const VXIchar *strType =
SBinetUtils::getString(properties, INET_SUBMIT_MIME_TYPE);
if (strType && *strType)
{
// If DEFAULT ever changes, this code will need to be updated
if (::wcscmp(strType, INET_SUBMIT_MIME_TYPE_DEFAULT) == 0)
encodingType = TYPE_URL_ENCODED;
else if (::wcscmp(strType, L"multipart/form-data") == 0)
encodingType = TYPE_MULTIPART;
else
Error(304, L"%s%s%s%s%s%s", L"URL", _url->getAbsolute(),
L"EncodingType", strType,
L"DefaultType", INET_SUBMIT_MIME_TYPE_DEFAULT);
}
return encodingType;
}
VXIinetResult SBinetHttpStream::getStatus()
{
_HttpStatus = HTTP_UNDEFINED;
VXIinetResult rc = waitStreamReady();
if (rc != VXIinet_RESULT_SUCCESS)
return rc;
writeDebugTimeStamp();
// The HTTP status line should have the following syntax:
// HTTP/x.x yyy msg CRLF
//
// This is buggy if we ever see an HTTP/1.xx in the future
SWIdataOutputStream line;
const char *c_line = NULL;
SWIstream::Result src = _channel->readLine(_inputStream, &line);
if (src >= 0)
{
if (ap_checkmask(c_line = line.getString(), "HTTP/#.# ###*"))
{
_HttpStatus = atoi(&c_line[9]);
int version = (c_line[5] - '0') * 10 + (c_line[7] - '0');
if (version < 11) _closeConnection = TRUE;
}
else
{
Error(249, L"%s%s", L"URL", _url->getAbsolute());
}
}
// A return value of END_OF_FILE means the connection was gracefully closed.
// A return value of CONNECTION_ABORTED means the connection was aborted
// because of a time out or some other problem.
else if ((src == SWIstream::CONNECTION_ABORTED) || (src == SWIstream::END_OF_FILE))
{
// Return immediately and do not check the _HttpStatus since
// we don't want to print errors for nothing.
_connectionAborted = true;
return VXIinet_RESULT_NON_FATAL_ERROR;
}
else
{
Error(249, L"%s%s", L"URL", _url->getAbsolute());
}
if (_HttpStatus < 100 || _HttpStatus > 999)
{
if (_HttpStatus != HTTP_UNDEFINED)
{
Error(219, L"%s%s%s%s%s%d",
L"URL", _url->getAbsolute(),
L"Method", _method == GET_METHOD ? L"GET" : L"POST",
L"HTTPStatus", _HttpStatus);
}
else
{
Error(221, L"%s%s", L"URL", _url->getAbsolute());
}
rc = VXIinet_RESULT_NON_FATAL_ERROR;
}
return rc;
}
enum HandlerValueType
{
HANDLER_INT = 0x01,
HANDLER_DATE = 0x02,
HANDLER_STRING = 0x04,
HANDLER_CONTENT = 0x08,
HANDLER_LIST = 0x10
};
// Currently doing a linear search because of case-insenstivity of
// comparisons. If the number of entries in this array increases, we should
// consider using a hash implementation instead.
SBinetHttpStream::HeaderInfo SBinetHttpStream::headerInfoMap[] =
{
{ "Age", NULL, HANDLER_INT, NULL },
{ "Cache-Control", NULL, HANDLER_STRING | HANDLER_LIST, NULL },
{ "Content-Length", INET_INFO_SIZE_BYTES, HANDLER_INT, contentLengthHandler },
{ "Content-Type", INET_INFO_MIME_TYPE, HANDLER_STRING, NULL },
{ "Connection", NULL, HANDLER_STRING, connectionHandler },
{ "Date", NULL, HANDLER_DATE, NULL },
{ "ETag", NULL, HANDLER_STRING, NULL },
{ "Expires", NULL, HANDLER_DATE, NULL },
{ "Last-Modified", NULL, HANDLER_DATE, NULL },
{ "Location", NULL, HANDLER_STRING, NULL },
{ "Pragma", NULL, HANDLER_STRING, NULL },
{ "Set-Cookie", NULL, HANDLER_STRING, setCookieHandler },
{ "Set-Cookie2", NULL, HANDLER_STRING, setCookie2Handler },
{ "Transfer-Encoding", NULL, HANDLER_STRING, transferEncodingHandler},
{ NULL, NULL, 0, NULL }
};
void SBinetHttpStream::setCookieHandler(HeaderInfo *headerInfo,
const char *value,
SBinetHttpStream *httpStream,
VXIMap *streamInfo)
{
if (!httpStream->_channel->cookiesEnabled())
return;
const char *p = value;
while (p != NULL && *p)
{
SBinetCookie *cookie = SBinetCookie::parse(httpStream->_url, p, httpStream);
if (cookie != NULL)
{
httpStream->_channel->updateCookie(cookie);
}
}
httpStream->Diag(MODULE_SBINET_STREAM_TAGID,
L"SBinetHttpStream::setCookieHandler",
L"Set-Cookie: %S", value);
}
void SBinetHttpStream::setCookie2Handler(HeaderInfo *headerInfo,
const char *value,
SBinetHttpStream *httpStream,
VXIMap *streamInfo)
{
if (!httpStream->_channel->cookiesEnabled())
return;
httpStream->Diag(MODULE_SBINET_STREAM_TAGID,
L"SBinetHttpStream::setCookie2Handler",
L"Not Supported: \"Set-Cookie2: %S\"", value);
}
void SBinetHttpStream::contentLengthHandler(HeaderInfo *headerInfo,
const char *value,
SBinetHttpStream *httpStream,
VXIMap *streamInfo)
{
// Ignore content-length is stream is chunked.
if (!httpStream->_chunked)
{
httpStream->_leftToRead = atoi(value);
}
}
void SBinetHttpStream::connectionHandler(HeaderInfo *headerInfo,
const char *value,
SBinetHttpStream *httpStream,
VXIMap *streamInfo)
{
SBinetNString attrib;
for (;;)
{
if ((value = SBinetHttpUtils::getValue(value, attrib)) == NULL)
{
httpStream->Diag(MODULE_SBINET_STREAM_TAGID,
L"SBinetHttpStream::connectionHandler",
L"Could not get attribute.");
break;
}
if ((value = SBinetHttpUtils::expectChar(value, ",")) == NULL)
{
httpStream->Diag(MODULE_SBINET_STREAM_TAGID,
L"SBinetHttpStream::connectionHandler",
L"Expecting ','.");
break;
}
if (::strcasecmp(attrib.c_str(), "close") == 0)
{
httpStream->_closeConnection = TRUE;
}
// Skip comma, or if at end of string, stop parsing.
if (*value)
value++;
else
break;
}
}
void SBinetHttpStream::transferEncodingHandler(HeaderInfo *headerInfo,
const char *value,
SBinetHttpStream *httpStream,
VXIMap *streamInfo)
{
SBinetNString encoding;
for (;;)
{
if ((value = SBinetHttpUtils::getValue(value, encoding)) == NULL)
{
httpStream->Diag(MODULE_SBINET_STREAM_TAGID,
L"SBinetHttpStream::transferEncodingHandler",
L"Could not get encoding.");
break;
}
if ((value = SBinetHttpUtils::expectChar(value, ",")) == NULL)
{
httpStream->Diag(MODULE_SBINET_STREAM_TAGID,
L"SBinetHttpStream::transferEncodingHandler",
L"Expecting ','.");
break;
}
if (::strcasecmp(encoding.c_str(), "chunked") == 0)
{
httpStream->_chunked = true;
httpStream->_leftToRead = 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -