📄 sbinethttpstream.cpp
字号:
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 = ~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; } // Skip comma, or if at end of string, stop parsing.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -