📄 sbinethttpstream.cpp
字号:
/****************License************************************************
*
* Copyright 2000-2003. ScanSoft, Inc.
*
* Use of this software is subject to notices and obligations set forth
* in the SpeechWorks Public License - Software Version 1.2 which is
* included with this software.
*
* ScanSoft is a registered trademark of ScanSoft, Inc., and OpenSpeech,
* SpeechWorks and the SpeechWorks logo are registered trademarks or
* trademarks of SpeechWorks International, Inc. in the United States
* and other countries.
*
***********************************************************************/
#include <vxibuildopts.h>
#if P_VXI
#ifndef _SB_USE_STD_NAMESPACE
#define _SB_USE_STD_NAMESPACE
#endif
#ifdef WIN32
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#endif
#include <sys/timeb.h> // for _ftime( )/ftime( )
#include "vxi/VXIvalue.h"
#include "vxi/VXIinet.h"
#include "vxi/VXItrd.h"
#include "SBinetLog.h"
#include "SBinetURL.h"
#include "SBinetHttpStream.hpp"
#include "SBinetChannel.h"
#include "SBinetHttpConnection.hpp"
#include "SBinetValidator.h"
#include "SBinetCookie.h"
#include "SBinetUtils.hpp"
#include "SWITimeWatch.hpp"
#include "util_date.h"
#include "ap_ctype.h"
#include "HttpUtils.hpp"
#include <vxi/SWIsocket.hpp>
#include <vxi/SWIdataOutputStream.hpp>
#include <vxi/SWIbufferedInputStream.hpp>
#include <vxi/SWIbufferedOutputStream.hpp>
#if defined(_MSC_VER)
#pragma warning(disable:4061)
#endif
#define HTTP_UNDEFINED -1234 /* No HTTP error code yet */
#define READ_CHUNK_SIZE 128 * 1024 // Read from socket in 128 Kb chunks
SBinetHttpStream::SBinetHttpStream(SBinetURL* url,
SubmitMethod method,
SBinetChannel* ch,
VXIlogInterface *log,
VXIunsigned diagLogBase):
SBinetStoppableStream(url, log, diagLogBase),
_HttpStatus(HTTP_UNDEFINED), _leftToRead((unsigned)~0), _chunked(false),
_method(method), _channel(ch), _connection(NULL),
_inputStream(NULL), _outputStream(NULL),
_closeConnection(TRUE), _validator(NULL), _connectionAborted(FALSE)
{}
SBinetHttpStream::~SBinetHttpStream()
{
delete _validator;
Close();
}
VXIinetResult SBinetHttpStream::initSocket(SubmitMethod method,
const VXIMap *properties,
VXIMap *streamInfo)
{
_connection = _channel->getHttpConnection(_url, properties);
if (_connection == NULL)
{
Error(262, L"%s%s", L"URL", _url->getAbsolute());
return VXIinet_RESULT_FETCH_ERROR;
}
SWITimeWatch timeWatch;
timeWatch.start();
switch (_connection->connect(getDelay()))
{
case SWIstream::TIMED_OUT:
Error(241, NULL);
return VXIinet_RESULT_FETCH_TIMEOUT;
break;
case SWIstream::SUCCESS:
break;
default:
Error(244, L"%s%s", L"URL", _url->getAbsolute());
return VXIinet_RESULT_FETCH_ERROR;
}
_inputStream = _connection->getInputStream();
_outputStream = _connection->getOutputStream();
Diag(MODULE_SBINET_TIMING_TAGID, L"SBinetHttpStream::initSocket",
L"%i msecs, Connect to socket", timeWatch.getElapsed());
writeDebugTimeStamp();
if (method == SBinetHttpStream::GET_METHOD)
_channel->writeString(_outputStream, "GET ");
else
_channel->writeString(_outputStream, "POST ");
if (_connection->usesProxy())
_channel->writeString(_outputStream, _url->getNAbsolute());
else
_channel->writeString(_outputStream, _url->getNPath());
_channel->writeString(_outputStream, " HTTP/1.1" CRLF);
_channel->writeString(_outputStream, "Accept: */*" CRLF);
// Write HOST header.
_channel->writeString(_outputStream, "Host: ");
_channel->writeString(_outputStream, _url->getNHost());
int defaultPort = _url->getProtocol() == SBinetURL::HTTP_PROTOCOL ? 80 : 443;
int port = _url->getPort();
if (port != defaultPort)
{
_channel->writeString(_outputStream, ":");
_channel->writeInt(_outputStream, port);
}
_channel->writeString(_outputStream, CRLF);
// Write USER-AGENT header.
_channel->writeString(_outputStream, "User-Agent: ");
_channel->writeString(_outputStream, _channel->getUserAgent());
_channel->writeString(_outputStream, CRLF);
// Write REFERER header.
SBinetNString baseUrl = _url->getNBase();
if (baseUrl.length() > 0)
{
// Cut query arguments, if present
VXIunsigned queryArgsSeparator = baseUrl.find('?');
if (queryArgsSeparator < baseUrl.length())
baseUrl.resize(queryArgsSeparator);
if (baseUrl.length() > 0)
{
_channel->writeString(_outputStream, "Referer: ");
_channel->writeString(_outputStream, baseUrl.c_str());
_channel->writeString(_outputStream, CRLF);
}
}
SBinetUtils::getInteger(properties, INET_CLOSE_CONNECTION, _closeConnection);
if (_closeConnection)
{
_channel->writeString(_outputStream, "Connection: close" CRLF);
}
VXIint32 maxAge = -1;
VXIint32 maxStale = -1;
getCacheInfo(properties, maxAge, maxStale);
if (maxAge != -1)
{
_channel->writeString(_outputStream, "Cache-Control: max-age=");
_channel->writeInt(_outputStream, maxAge);
_channel->writeString(_outputStream, CRLF);
}
if (maxStale != -1)
{
_channel->writeString(_outputStream, "Cache-Control: max-stale=");
_channel->writeInt(_outputStream, maxStale);
_channel->writeString(_outputStream, CRLF);
}
time_t lastModified = (time_t) -1;
SBinetNString etag;
getModInfo(properties, lastModified, etag);
if (method == SBinetHttpStream::GET_METHOD && lastModified != (time_t) -1)
{
char lastModStr[32];
ap_gm_timestr_822(lastModStr, lastModified);
_channel->writeString(_outputStream, "If-Modified-Since: ");
_channel->writeString(_outputStream, lastModStr);
_channel->writeString(_outputStream, CRLF);
}
if (etag[0])
{
_channel->writeString(_outputStream, "If-None-Match: ");
_channel->writeString(_outputStream, etag.c_str());
_channel->writeString(_outputStream, CRLF);
}
if (_channel->cookiesEnabled())
{
_channel->collectCookies(_outputStream, _url->getNHost(), _url->getNPath());
}
return VXIinet_RESULT_SUCCESS;
}
VXIinetResult SBinetHttpStream::doGet(VXIint32 flags,
const VXIMap *properties,
VXIMap *streamInfo)
{
VXIinetResult rc = initSocket(GET_METHOD, properties, streamInfo);
if (rc != VXIinet_RESULT_SUCCESS)
return rc;
_channel->writeString(_outputStream, CRLF);
if (_outputStream->flush() != SWIstream::SUCCESS)
rc = VXIinet_RESULT_IO_ERROR;
return rc;
}
/*
* POST simple form data. Don't know how to combine Form data and audio in multipart POST
*/
VXIinetResult SBinetHttpStream::doPost(VXIint32 flags,
const VXIMap *properties,
VXIMap *streamInfo)
{
const VXIMap *queryArgs =
(const VXIMap *)VXIMapGetProperty(properties, INET_URL_QUERY_ARGS);
SBinetNString queryArgsStr;
VXIint argsLen;
EncodingType encodingType = getEncodingType(properties);
const char *encodingStr;
if (encodingType == TYPE_MULTIPART)
{
encodingStr = SB_MULTIPART;
queryArgsStr = _url->queryArgsToMultipart(queryArgs);
argsLen = queryArgsStr.length();
Diag(MODULE_SBINET_STREAM_TAGID, L"SBinetHttpStream::doPost",
L"Performing multipart POST, data size %i", argsLen);
}
else
{
encodingStr = SB_URLENCODED;
queryArgsStr = _url->queryArgsToNString(queryArgs);
argsLen = queryArgsStr.length();
Diag(MODULE_SBINET_STREAM_TAGID, L"SBinetHttpStream::doPost",
L"Performing URL-encoded POST, data size %i", argsLen);
}
VXIinetResult rc = initSocket(POST_METHOD, properties, streamInfo);
if (rc != VXIinet_RESULT_SUCCESS)
return rc;
_channel->writeString(_outputStream, "Expect: 100-continue" CRLF);
_channel->writeString(_outputStream, "Content-Length: ");
_channel->writeInt(_outputStream, argsLen);
_channel->writeString(_outputStream, CRLF);
_channel->writeString(_outputStream, "Content-Type: ");
_channel->writeString(_outputStream, encodingStr);
_channel->writeString(_outputStream, CRLF CRLF);
if (_outputStream->flush() != SWIstream::SUCCESS)
{
return VXIinet_RESULT_IO_ERROR;
}
// Wait for input stream to be ready. If time-out, just send data anyway.
// If we don't time out, we should be able to get an HTTP_CONTINUE.
VXIint32 continueTimeout = SBinetChannel::getPostContinueTimeout();
int maxDelay = getDelay();
if (continueTimeout < 0 ||
(maxDelay >=0 && continueTimeout > maxDelay))
continueTimeout = maxDelay;
switch (_inputStream->waitReady(continueTimeout))
{
case SWIstream::SUCCESS:
// Read the HTTP_CONTINUE header
if ((rc = getHeaderInfo(streamInfo)) != VXIinet_RESULT_SUCCESS)
return rc;
if (_HttpStatus != SBinetHttpUtils::HTTP_CONTINUE)
return VXIinet_RESULT_FETCH_ERROR;
break;
case SWIstream::TIMED_OUT:
// We still have to send the body of the message. This is done
// after the switch anyway so we do nothing.
break;
default:
// I/O error on the stream. Just return an error.
return VXIinet_RESULT_FETCH_ERROR;
}
if (encodingType == TYPE_MULTIPART)
{
_channel->writeData(_outputStream, queryArgsStr.data(), argsLen);
}
else
{
_channel->writeString(_outputStream, queryArgsStr.c_str());
}
if (_outputStream->flush() != SWIstream::SUCCESS)
rc = VXIinet_RESULT_IO_ERROR;
return rc;
}
bool SBinetHttpStream::handleRedirect(VXIMap *streamInfo)
{
const VXIchar *location;
switch (_HttpStatus)
{
case SBinetHttpUtils::HTTP_SEE_OTHER:
// This return code implies that we change a POST into a GET.
_method = GET_METHOD;
// no break: intentional
case SBinetHttpUtils::HTTP_MULTIPLE_CHOICES:
case SBinetHttpUtils::HTTP_PERM_REDIRECT:
case SBinetHttpUtils::HTTP_FOUND:
case SBinetHttpUtils::HTTP_TEMP_REDIRECT:
location = SBinetUtils::getString(streamInfo, L"Location");
if (location == NULL || !*location)
{
Error(247, L"%s%s", L"URL", _url->getAbsolute());
return false;
}
if (_url->parse(location, _url->getAbsolute()) != VXIinet_RESULT_SUCCESS)
{
Error(248, L"%s%s%s%s", L"URL", _url->getAbsolute(),
L"Location", location);
return false;
}
return true;
break;
default:
return false;
}
}
// stupid function required because the people who designed VXIMap, in their
// great wisdom, did not include a VXIMapClear function or the ability for the
// iterator to delete the current entry.
static void clearMap(VXIMap *streamInfo)
{
VXIunsigned n = VXIMapNumProperties(streamInfo);
const VXIchar **keys = new const VXIchar *[n];
const VXIchar *key;
const VXIValue *value;
int i = 0;
VXIMapIterator *mapIterator = VXIMapGetFirstProperty(streamInfo,
&key,
&value );
do
{
if (key != NULL)
{
keys[i++] = key;
}
}
while (VXIMapGetNextProperty(mapIterator, &key, &value) == VXIvalue_RESULT_SUCCESS);
VXIMapIteratorDestroy(&mapIterator);
i--;
while (--i >= 0)
{
VXIMapDeleteProperty(streamInfo, keys[i]);
}
delete [] keys;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -