mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
1311 lines
41 KiB
C
1311 lines
41 KiB
C
|
/*H********************************************************************************/
|
||
|
/*!
|
||
|
\File http.c
|
||
|
|
||
|
\Description
|
||
|
Implements basic http get and post client.
|
||
|
|
||
|
\Copyright
|
||
|
Copyright (c) 2005 Electronic Arts Inc.
|
||
|
|
||
|
\Version 10/28/2005 (jbrookes) First Version
|
||
|
*/
|
||
|
/********************************************************************************H*/
|
||
|
|
||
|
/*** Include files ****************************************************************/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <ctype.h>
|
||
|
#include <time.h>
|
||
|
|
||
|
#include "DirtySDK/platform.h"
|
||
|
#include "DirtySDK/dirtysock.h"
|
||
|
#include "DirtySDK/proto/protohttp.h"
|
||
|
#include "DirtySDK/proto/protossl.h"
|
||
|
|
||
|
#include "libsample/zlib.h"
|
||
|
#include "libsample/zmem.h"
|
||
|
#include "libsample/zfile.h"
|
||
|
|
||
|
#include "testermodules.h"
|
||
|
|
||
|
/*** Defines **********************************************************************/
|
||
|
|
||
|
#define HTTP_BUFSIZE (4096)
|
||
|
#define HTTP_RATE (1)
|
||
|
|
||
|
#define HTTP_XTRAHDR0 ""
|
||
|
#define HTTP_XTRAHDR1 "X-Agent: DirtySock HTTP Tester\r\n" // test "normal" replacement (replaces Accept: header)
|
||
|
#define HTTP_XTRAHDR2 "User-Agent: DirtySock HTTP Tester\r\n" // test "extended" replacement (replaces User-Agent: and Accept: headers)
|
||
|
|
||
|
//$$ tmp -- special test header used for hard-coded multipart/form-data testing -- this should be removed at some point when we have real multipart/form-data support
|
||
|
#define HTTP_XTRAHDR3 "Content-Type: multipart/form-data; boundary=TeStInG\r\n" \
|
||
|
"User-Agent: DirtySock HTTP Tester\r\n" \
|
||
|
"Accept: */*\r\n"
|
||
|
|
||
|
#define HTTP_APNDHDR HTTP_XTRAHDR0
|
||
|
|
||
|
/*** Function Prototypes **********************************************************/
|
||
|
|
||
|
static int32_t _CmdHttpIdleCB(ZContext *argz, int32_t argc, char *argv[]);
|
||
|
|
||
|
/*** Type Definitions *************************************************************/
|
||
|
|
||
|
typedef struct HttpRefT // module state storage
|
||
|
{
|
||
|
ProtoHttpRefT *http;
|
||
|
int64_t show;
|
||
|
int64_t count;
|
||
|
int32_t sttime;
|
||
|
int64_t iDataSize;
|
||
|
int64_t iSentSize;
|
||
|
int32_t iSendBufData;
|
||
|
int32_t iSendBufSent;
|
||
|
ZFileT iInpFile;
|
||
|
ZFileT iOutFile;
|
||
|
int32_t iOutSize;
|
||
|
char *pOutData;
|
||
|
char *pClientCert;
|
||
|
int32_t iCertSize;
|
||
|
char *pClientKey;
|
||
|
int32_t iKeySize;
|
||
|
int32_t iDebugLevel;
|
||
|
uint8_t bStreaming;
|
||
|
uint8_t bUseWriteCb;
|
||
|
uint8_t bRecvAll;
|
||
|
char strModuleName[32];
|
||
|
char strHost[128];
|
||
|
char strCookie[2048];
|
||
|
char strApndHdr[2048];
|
||
|
char strFileBuffer[64*1024]; // must be at a minimum 4k or protohttp buffer size, whichever is larger
|
||
|
|
||
|
// module state
|
||
|
enum
|
||
|
{
|
||
|
IDLE, DNLOAD, UPLOAD
|
||
|
} state;
|
||
|
} HttpRefT;
|
||
|
|
||
|
/*** Variables ********************************************************************/
|
||
|
|
||
|
static HttpRefT _Http_Ref;
|
||
|
static uint8_t _Http_bInitialized = FALSE;
|
||
|
|
||
|
/*** Private Functions ************************************************************/
|
||
|
|
||
|
/*F********************************************************************************/
|
||
|
/*!
|
||
|
\Function _GetIntArg
|
||
|
|
||
|
\Description
|
||
|
Get fourcc/integer from command-line argument
|
||
|
|
||
|
\Input *pArg - pointer to argument
|
||
|
|
||
|
\Version 10/20/2011 (jbrookes)
|
||
|
*/
|
||
|
/********************************************************************************F*/
|
||
|
static int32_t _GetIntArg(const char *pArg)
|
||
|
{
|
||
|
int32_t iValue;
|
||
|
|
||
|
// check for possible fourcc value
|
||
|
if ((strlen(pArg) == 4) && (isalpha(pArg[0]) || isalpha(pArg[1]) || isalpha(pArg[2]) || isalpha(pArg[3])))
|
||
|
{
|
||
|
iValue = pArg[0] << 24;
|
||
|
iValue |= pArg[1] << 16;
|
||
|
iValue |= pArg[2] << 8;
|
||
|
iValue |= pArg[3];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iValue = (signed)strtol(pArg, NULL, 10);
|
||
|
}
|
||
|
return(iValue);
|
||
|
}
|
||
|
|
||
|
/*F********************************************************************************/
|
||
|
/*!
|
||
|
\Function _CmdHttpUsage
|
||
|
|
||
|
\Description
|
||
|
Display usage information.
|
||
|
|
||
|
\Input argc - argument count
|
||
|
\Input *argv[] - argument list
|
||
|
|
||
|
\Version 02/18/2008 (jbrookes)
|
||
|
*/
|
||
|
/********************************************************************************F*/
|
||
|
static void _CmdHttpUsage(int argc, char *argv[])
|
||
|
{
|
||
|
if (argc == 2)
|
||
|
{
|
||
|
ZPrintf(" execute http get or put operations\n");
|
||
|
ZPrintf(" usage: %s [cclr|cert|cer2|cver|create|ctrl|debug|destroy|get|head|put|post|puts|delete|options|parse|stat|urlparse]", argv[0]);
|
||
|
}
|
||
|
else if (argc == 3)
|
||
|
{
|
||
|
if (!strcmp(argv[2], "cclr") || !strcmp(argv[2], "cert") || !strcmp(argv[2], "cer2") || !strcmp(argv[2], "cver"))
|
||
|
{
|
||
|
ZPrintf(" usage: %s cert|cer2 <certfile> - load certificate file\n", argv[0]);
|
||
|
ZPrintf(" %s cclr - clear dynamic CA certs\n");
|
||
|
ZPrintf(" %s cver - verify dynamic CA certs\n");
|
||
|
}
|
||
|
else if (!strcmp(argv[2], "create"))
|
||
|
{
|
||
|
ZPrintf(" usage: %s create <bufsize>\n", argv[0]);
|
||
|
}
|
||
|
else if (!strcmp(argv[2], "destroy"))
|
||
|
{
|
||
|
ZPrintf(" usage: %s destroy\n", argv[0]);
|
||
|
}
|
||
|
else if (!strcmp(argv[2], "get") || !strcmp(argv[2], "head") || !strcmp(argv[2], "options") || !strcmp(argv[2], "delete"))
|
||
|
{
|
||
|
ZPrintf(" usage: %s %s <options> [url] <outfile>\n", argv[0], argv[2]);
|
||
|
}
|
||
|
else if (!strcmp(argv[2], "put") || !strcmp(argv[2], "puts") || !strcmp(argv[2], "post"))
|
||
|
{
|
||
|
ZPrintf(" usage: %s %s <options> [url] [infile] <outfile>\n", argv[0], argv[2]);
|
||
|
}
|
||
|
else if (!strcmp(argv[2], "debug"))
|
||
|
{
|
||
|
ZPrintf(" usage: %s debug [debuglevel]\n", argv[0]);
|
||
|
}
|
||
|
else if (!strcmp(argv[2], "parse"))
|
||
|
{
|
||
|
ZPrintf(" usage: %s parse [url]\n", argv[0]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int32_t _HttpUrlParseTest(const char *pModuleName, const char *pUrl)
|
||
|
{
|
||
|
char strKind[16], strHost[256];
|
||
|
int32_t iPort, iSecure;
|
||
|
uint8_t bPortSpecified;
|
||
|
const char *pUri;
|
||
|
|
||
|
ZPrintf("%s: parsing url %s\n", pModuleName, pUrl);
|
||
|
pUri = ProtoHttpUrlParse2(pUrl, strKind, sizeof(strKind), strHost, sizeof(strHost), &iPort, &iSecure, &bPortSpecified);
|
||
|
|
||
|
ZPrintf("%s: uri=%s\n", pModuleName, pUri);
|
||
|
ZPrintf("%s: kind=%s\n", pModuleName, strKind);
|
||
|
ZPrintf("%s: host=%s\n", pModuleName, strHost);
|
||
|
ZPrintf("%s: iPort=%d\n", pModuleName, iPort);
|
||
|
ZPrintf("%s: iSecure=%d\n", pModuleName, iSecure);
|
||
|
ZPrintf("%s: bPortSpecified=%s\n", pModuleName, bPortSpecified ? "TRUE" : "FALSE");
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
static int32_t _HttpUrlEncodeTest(const char *pModuleName, char **pArgs, int32_t iNumArgs)
|
||
|
{
|
||
|
char strUrl[128];
|
||
|
int32_t iArg, iOff;
|
||
|
|
||
|
ds_snzprintf(strUrl, sizeof(strUrl), "https://urltest.ea.com/");
|
||
|
for (iArg = 0, iOff = (int32_t)strlen(strUrl); iArg < iNumArgs; iArg += 2)
|
||
|
{
|
||
|
iOff += ProtoHttpUrlEncodeStrParm(strUrl+iOff, sizeof(strUrl)-iOff, pArgs[iArg], pArgs[iArg+1]);
|
||
|
}
|
||
|
ProtoHttpUrlEncodeIntParm(strUrl, sizeof(strUrl), "&test=", 2918123);
|
||
|
ZPrintf("%s: url=%s\n", pModuleName, strUrl);
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
/*F********************************************************************************/
|
||
|
/*!
|
||
|
\Function _HttpLoadCertificate
|
||
|
|
||
|
\Description
|
||
|
Display usage information.
|
||
|
|
||
|
\Input *pFilename - certificate filename
|
||
|
\Input *pCertSize - [out] storage for cert size
|
||
|
|
||
|
\Output
|
||
|
char * - pointer to certificate, or NULL on error
|
||
|
|
||
|
\Version 10/20/2013 (jbrookes)
|
||
|
*/
|
||
|
/********************************************************************************F*/
|
||
|
static char *_HttpLoadCertificate(const char *pFilename, int32_t *pCertSize)
|
||
|
{
|
||
|
char *pCertBuf;
|
||
|
int32_t iFileSize;
|
||
|
|
||
|
// load certificate file
|
||
|
if ((pCertBuf = (char *)ZFileLoad(pFilename, &iFileSize, ZFILE_OPENFLAG_RDONLY)) == NULL)
|
||
|
{
|
||
|
return(NULL);
|
||
|
}
|
||
|
|
||
|
// calculate length
|
||
|
*pCertSize = iFileSize;
|
||
|
// return to caller
|
||
|
return(pCertBuf);
|
||
|
}
|
||
|
|
||
|
/*F********************************************************************************/
|
||
|
/*!
|
||
|
\Function _HttpReset
|
||
|
|
||
|
\Description
|
||
|
Reset transaction state
|
||
|
|
||
|
\Input *pRef - module state
|
||
|
|
||
|
\Version 03/21/2014 (jbrookes)
|
||
|
*/
|
||
|
/********************************************************************************F*/
|
||
|
static void _HttpReset(HttpRefT *pRef)
|
||
|
{
|
||
|
// clear any previous stats
|
||
|
pRef->count = 0;
|
||
|
pRef->show = 0;
|
||
|
pRef->iDataSize = 0;
|
||
|
pRef->iSentSize = 0;
|
||
|
pRef->iSendBufSent = 0;
|
||
|
|
||
|
// clear previous options
|
||
|
pRef->bStreaming = FALSE;
|
||
|
pRef->bUseWriteCb = FALSE;
|
||
|
pRef->bRecvAll = FALSE;
|
||
|
}
|
||
|
|
||
|
/*F********************************************************************************/
|
||
|
/*!
|
||
|
\Function _HttpCustomHeaderCallback
|
||
|
|
||
|
\Description
|
||
|
ProtoHttp custom header callback.
|
||
|
|
||
|
\Input *pProtoHttp - protohttp module state
|
||
|
\Input *pHeader - header to be sent
|
||
|
\Input uHeaderSize - header size
|
||
|
\Input *pUserData - user ref (HttpRefT)
|
||
|
|
||
|
\Output
|
||
|
int32_t - zero
|
||
|
|
||
|
\Notes
|
||
|
The header returned should be terminated by a *single* CRLF; ProtoHttp will
|
||
|
append the final CRLF to complete the header. The callback may return the
|
||
|
size of the header, or zero, in which case ProtoHttp will calculate the
|
||
|
headersize using strlen().
|
||
|
|
||
|
\Version 02/24/2009 (jbrookes)
|
||
|
*/
|
||
|
/********************************************************************************F*/
|
||
|
static int32_t _HttpCustomHeaderCallback(ProtoHttpRefT *pProtoHttp, char *pHeader, uint32_t uHeaderSize, const char *pData, int64_t iDataLen, void *pUserRef)
|
||
|
{
|
||
|
int32_t iOffset;
|
||
|
|
||
|
NetPrintf(("http: custom header callback, size=%d, data=%d, pData=%p\n", uHeaderSize, iDataLen, pData));
|
||
|
|
||
|
// find end of header
|
||
|
iOffset = (int32_t)strlen(pHeader);
|
||
|
|
||
|
// append a header
|
||
|
iOffset += ds_snzprintf(pHeader+iOffset, uHeaderSize-iOffset, "X-CustomHeaderCallback: Testing custom header callback\r\n");
|
||
|
|
||
|
// we can either return the header length or zero; in the latter case protohttp will re-calculate for us
|
||
|
return(iOffset);
|
||
|
}
|
||
|
|
||
|
/*F********************************************************************************/
|
||
|
/*!
|
||
|
\Function _HttpRecvHeaderCallback
|
||
|
|
||
|
\Description
|
||
|
ProtoHttp recv header callback.
|
||
|
|
||
|
\Input *pProtoHttp - protohttp module state
|
||
|
\Input *pHeader - received header
|
||
|
\Input uHeaderSize - header size
|
||
|
\Input *pUserData - user ref (HttpRefT)
|
||
|
|
||
|
\Output
|
||
|
int32_t - zero
|
||
|
|
||
|
\Version 02/24/2009 (jbrookes)
|
||
|
*/
|
||
|
/********************************************************************************F*/
|
||
|
static int32_t _HttpRecvHeaderCallback(ProtoHttpRefT *pProtoHttp, const char *pHeader, uint32_t uHeaderSize, void *pUserRef)
|
||
|
{
|
||
|
HttpRefT *pHttp = (HttpRefT *)pUserRef;
|
||
|
char strBuffer[2048], strName[128];
|
||
|
const char *pHdrTmp;
|
||
|
int32_t iLocnSize;
|
||
|
|
||
|
// check for location header
|
||
|
if ((iLocnSize = ProtoHttpGetHeaderValue(pProtoHttp, pHeader, "location", NULL, 0, NULL)) > 0)
|
||
|
{
|
||
|
ZPrintf("http: location header size=%d\n", iLocnSize);
|
||
|
if (ProtoHttpGetHeaderValue(pProtoHttp, pHeader, "location", strBuffer, sizeof(strBuffer), NULL) == 0)
|
||
|
{
|
||
|
ZPrintf("http: location url='%s'\n", strBuffer);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ZPrintf("http: error querying location url\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// test ProtoHttpGetNextHeader()
|
||
|
for (pHdrTmp = pHeader; ProtoHttpGetNextHeader(pProtoHttp, pHdrTmp, strName, sizeof(strName), strBuffer, sizeof(strBuffer), &pHdrTmp) == 0; )
|
||
|
{
|
||
|
#if 0
|
||
|
ZPrintf("http: ===%s: %s\n", strName, strBuffer);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
// parse any set-cookie requests
|
||
|
for (pHdrTmp = pHeader; ProtoHttpGetHeaderValue(pProtoHttp, pHdrTmp, "set-cookie", strBuffer, sizeof(strBuffer), &pHdrTmp) == 0; )
|
||
|
{
|
||
|
// print the cookie
|
||
|
ZPrintf("http: parsed cookie '%s'\n", strBuffer);
|
||
|
|
||
|
// add field seperator
|
||
|
if (pHttp->strCookie[0] != '\0')
|
||
|
{
|
||
|
ds_strnzcat(pHttp->strCookie, ", ", sizeof(pHttp->strCookie));
|
||
|
}
|
||
|
// append to cookie list
|
||
|
ds_strnzcat(pHttp->strCookie, strBuffer, sizeof(pHttp->strCookie));
|
||
|
}
|
||
|
|
||
|
// if this is a redirection update append header with any new cookies
|
||
|
if ((PROTOHTTP_GetResponseClass(ProtoHttpStatus(pProtoHttp, 'code', NULL, 0)) == PROTOHTTP_RESPONSE_REDIRECTION) && (pHttp->strCookie[0] != '\0'))
|
||
|
{
|
||
|
// format append header
|
||
|
ds_snzprintf(strBuffer, sizeof(strBuffer), "Cookie: %s\r\n%s", pHttp->strCookie, HTTP_APNDHDR);
|
||
|
ds_strnzcat(strBuffer, pHttp->strApndHdr, sizeof(strBuffer));
|
||
|
ProtoHttpControl(pProtoHttp, 'apnd', 0, 0, strBuffer);
|
||
|
}
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
/*F********************************************************************************/
|
||
|
/*!
|
||
|
\Function _HttpReallocBuff
|
||
|
|
||
|
\Description
|
||
|
Realloc buffer used for RecvAll operation
|
||
|
|
||
|
\Input *pRef - pointer to http ref
|
||
|
|
||
|
\Version 07/29/2012 (jbrookes)
|
||
|
*/
|
||
|
/********************************************************************************F*/
|
||
|
static void _HttpReallocBuff(HttpRefT *pRef)
|
||
|
{
|
||
|
char *pNewData;
|
||
|
int32_t iNewSize;
|
||
|
|
||
|
// calc new buffer size
|
||
|
if ((iNewSize = pRef->iOutSize) == 0)
|
||
|
{
|
||
|
// try getting body size
|
||
|
if ((iNewSize = ProtoHttpStatus(pRef->http, 'body', NULL, 0)) > 0)
|
||
|
{
|
||
|
// bump up buffer size for recvall null terminator
|
||
|
//$$ TODO V9 -- why 2 required, not 1??
|
||
|
iNewSize += 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// assign a fixed size, since we didn't get a body size
|
||
|
iNewSize = 4096;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iNewSize *= 2;
|
||
|
}
|
||
|
|
||
|
// allocate new buffer
|
||
|
if ((pNewData = ZMemAlloc(iNewSize)) == NULL)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
// if realloc, copy old data and free old pointer
|
||
|
if (pRef->pOutData != NULL)
|
||
|
{
|
||
|
ds_memcpy(pNewData, pRef->pOutData, pRef->iOutSize);
|
||
|
ZMemFree(pRef->pOutData);
|
||
|
}
|
||
|
// save new pointer
|
||
|
pRef->pOutData = pNewData;
|
||
|
pRef->iOutSize = iNewSize;
|
||
|
}
|
||
|
|
||
|
/*F********************************************************************************/
|
||
|
/*!
|
||
|
\Function _HttpDownloadProcessData
|
||
|
|
||
|
\Description
|
||
|
Process data received in a download operation
|
||
|
|
||
|
\Input *pRef - module state
|
||
|
\Input iLen - data length or PROTOHTTP_RECV*
|
||
|
|
||
|
\Version 05/03/2012 (jbrookes)
|
||
|
*/
|
||
|
/********************************************************************************F*/
|
||
|
static void _HttpDownloadProcessData(HttpRefT *pRef, int32_t iLen)
|
||
|
{
|
||
|
int32_t iResult;
|
||
|
|
||
|
// see if we should show progress
|
||
|
if ((pRef->count/1024) != (pRef->show/1024))
|
||
|
{
|
||
|
pRef->show = pRef->count;
|
||
|
ZPrintf("%s: downloaded %qd bytes\n", pRef->strModuleName, pRef->count);
|
||
|
}
|
||
|
|
||
|
// see if we are done
|
||
|
if ((iLen < 0) && (iLen != PROTOHTTP_RECVWAIT))
|
||
|
{
|
||
|
// completed successfully?
|
||
|
if ((iLen == PROTOHTTP_RECVDONE) || (iLen == PROTOHTTP_RECVHEAD))
|
||
|
{
|
||
|
int32_t iDlTime = NetTickDiff(NetTick(), pRef->sttime);
|
||
|
int64_t iBodySize;
|
||
|
char strRespHdr[1024];
|
||
|
|
||
|
ZPrintf("%s: download complete (%qd bytes)\n", pRef->strModuleName, pRef->count);
|
||
|
ZPrintf("%s: download time: %qd bytes in %.2f seconds (%.3f k/sec)\n", pRef->strModuleName, pRef->count,
|
||
|
(float)iDlTime/1000.0f, ((float)pRef->count * 1000.0f) / ((float)iDlTime * 1024.0f));
|
||
|
|
||
|
// make sure we got it all
|
||
|
if ((iBodySize = ProtoHttpStatus(pRef->http, 'body', NULL, 0)) != pRef->count)
|
||
|
{
|
||
|
ZPrintf("%s: WARNING -- mismatch between expected size (%d) and actual size (%d)\n", pRef->strModuleName, iBodySize, pRef->count);
|
||
|
}
|
||
|
|
||
|
// display response header
|
||
|
if (ProtoHttpStatus(pRef->http, 'htxt', strRespHdr, sizeof(strRespHdr)) >= 0)
|
||
|
{
|
||
|
ZPrintf("%s response header:\n%s\n", pRef->strModuleName, strRespHdr);
|
||
|
}
|
||
|
|
||
|
// display a couple of parsed fields
|
||
|
if (ProtoHttpStatus(pRef->http, 'head', NULL, 0) > 0)
|
||
|
{
|
||
|
time_t tLastMod = ProtoHttpStatus(pRef->http, 'date', NULL, 0);
|
||
|
const char *pTime;
|
||
|
|
||
|
if (tLastMod != 0)
|
||
|
{
|
||
|
if ((pTime = ctime(&tLastMod)) != NULL)
|
||
|
{
|
||
|
ZPrintf("%s: Last-Modified: %s", pRef->strModuleName, pTime);
|
||
|
}
|
||
|
}
|
||
|
// get content-length; by passing in a 64bit int we support 64bit transfer sizes
|
||
|
ProtoHttpStatus(pRef->http, 'body', &iBodySize, sizeof(iBodySize));
|
||
|
ZPrintf("%s: Content-Length: %qd\n", pRef->strModuleName, iBodySize);
|
||
|
}
|
||
|
}
|
||
|
else // failure
|
||
|
{
|
||
|
ProtoSSLAlertDescT AlertDesc;
|
||
|
int32_t iSockErr = ProtoHttpStatus(pRef->http, 'serr', NULL, 0);
|
||
|
int32_t iSslFail = ProtoHttpStatus(pRef->http, 'essl', NULL, 0);
|
||
|
int32_t iAlert = ProtoHttpStatus(pRef->http, 'alrt', &AlertDesc, sizeof(AlertDesc));
|
||
|
ZPrintf("%s: download failed (err=%d, sockerr=%d sslerr=%d)\n", pRef->strModuleName, iLen, iSockErr, iSslFail);
|
||
|
if ((iSslFail == PROTOSSL_ERROR_CERT_INVALID) || (iSslFail == PROTOSSL_ERROR_CERT_HOST) ||
|
||
|
(iSslFail == PROTOSSL_ERROR_CERT_NOTRUST) || (iSslFail == PROTOSSL_ERROR_CERT_REQUEST))
|
||
|
{
|
||
|
ProtoSSLCertInfoT CertInfo;
|
||
|
if (ProtoHttpStatus(pRef->http, 'cert', &CertInfo, sizeof(CertInfo)) == 0)
|
||
|
{
|
||
|
ZPrintf("%s: cert failure (%d): (C=%s, ST=%s, L=%s, O=%s, OU=%s, CN=%s)\n", pRef->strModuleName, iSslFail,
|
||
|
CertInfo.Ident.strCountry, CertInfo.Ident.strState, CertInfo.Ident.strCity,
|
||
|
CertInfo.Ident.strOrg, CertInfo.Ident.strUnit, CertInfo.Ident.strCommon);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ZPrintf("%s: could not get cert info\n", pRef->strModuleName);
|
||
|
}
|
||
|
}
|
||
|
if (iAlert > 0)
|
||
|
{
|
||
|
ZPrintf("%s: %s ssl alert %s (%d)\n", pRef->strModuleName, (iAlert == 1) ? "recv" : "sent", AlertDesc.pAlertDesc, AlertDesc.iAlertType);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ZPrintf("%s: hResult=0x%08x\n", pRef->strModuleName, ProtoHttpStatus(pRef->http, 'hres', NULL, 0));
|
||
|
|
||
|
// if file exists, close it
|
||
|
if (pRef->iOutFile != ZFILE_INVALID)
|
||
|
{
|
||
|
if ((iResult = ZFileClose(pRef->iOutFile)) != 0)
|
||
|
{
|
||
|
ZPrintf("%s: error %d closing output file\n", pRef->strModuleName, iResult);
|
||
|
}
|
||
|
pRef->iOutFile = ZFILE_INVALID;
|
||
|
}
|
||
|
|
||
|
// if output buffer exists, free it
|
||
|
if (pRef->pOutData != NULL)
|
||
|
{
|
||
|
pRef->pOutData = NULL;
|
||
|
}
|
||
|
pRef->iOutSize = 0;
|
||
|
|
||
|
// set to idle state
|
||
|
pRef->state = IDLE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*F********************************************************************************/
|
||
|
/*!
|
||
|
\Function _HttpWriteCb
|
||
|
|
||
|
\Description
|
||
|
Implementation of ProtoHttp write callback
|
||
|
|
||
|
\Input *pState - http module state
|
||
|
\Input *pWriteInfo - callback info
|
||
|
\Input *pData - transaction data pointer
|
||
|
\Input iDataSize - size of data
|
||
|
\Input *pUserData - user callback data
|
||
|
|
||
|
\Output
|
||
|
int32_t - zero
|
||
|
|
||
|
\Version 05/03/2012 (jbrookes)
|
||
|
*/
|
||
|
/********************************************************************************F*/
|
||
|
static int32_t _HttpWriteCb(ProtoHttpRefT *pState, const ProtoHttpWriteCbInfoT *pWriteInfo, const char *pData, int32_t iDataSize, void *pUserData)
|
||
|
{
|
||
|
HttpRefT *pRef = (HttpRefT *)pUserData;
|
||
|
//NetPrintf(("http: writecb (%d,%d) %d bytes\n", pWriteInfo->eRequestType, pWriteInfo->eRequestResponse, iDataSize));
|
||
|
|
||
|
// if we got data, update count and write to output file if available
|
||
|
if (iDataSize > 0)
|
||
|
{
|
||
|
pRef->count += iDataSize;
|
||
|
if (pRef->iOutFile != ZFILE_INVALID)
|
||
|
{
|
||
|
ZFileWrite(pRef->iOutFile, (void *)pData, iDataSize);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// process
|
||
|
_HttpDownloadProcessData(pRef, iDataSize);
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
/*F********************************************************************************/
|
||
|
/*!
|
||
|
\Function _HttpRecvData
|
||
|
|
||
|
\Description
|
||
|
Receive data from ProtoHttp using either ProtoHttpRecv() or
|
||
|
ProtoHttpRecvAll().
|
||
|
|
||
|
\Input *pRef - module state
|
||
|
\Input *pState - transaction state
|
||
|
|
||
|
\Version 07/02/2013 (jbrookes) brought over from httpmgr
|
||
|
*/
|
||
|
/********************************************************************************F*/
|
||
|
static int32_t _HttpRecvData(HttpRefT *pRef)
|
||
|
{
|
||
|
char strBuf[16*1024];
|
||
|
int32_t iLen;
|
||
|
|
||
|
// check for data
|
||
|
if (!pRef->bRecvAll)
|
||
|
{
|
||
|
while ((iLen = ProtoHttpRecv(pRef->http, strBuf, 1, sizeof(strBuf))) > 0)
|
||
|
{
|
||
|
pRef->count += iLen;
|
||
|
if (pRef->iOutFile != ZFILE_INVALID)
|
||
|
{
|
||
|
ZFileWrite(pRef->iOutFile, strBuf, iLen);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// receive all the data
|
||
|
if ((iLen = ProtoHttpRecvAll(pRef->http, pRef->pOutData, pRef->iOutSize)) > 0)
|
||
|
{
|
||
|
pRef->count = iLen;
|
||
|
if (pRef->iOutFile != ZFILE_INVALID)
|
||
|
{
|
||
|
ZFileWrite(pRef->iOutFile, pRef->pOutData, iLen);
|
||
|
}
|
||
|
iLen = PROTOHTTP_RECVDONE;
|
||
|
}
|
||
|
else if (iLen == PROTOHTTP_RECVBUFF)
|
||
|
{
|
||
|
// grow the buffer
|
||
|
_HttpReallocBuff(pRef);
|
||
|
// swallow error code
|
||
|
iLen = 0;
|
||
|
}
|
||
|
}
|
||
|
return(iLen);
|
||
|
}
|
||
|
|
||
|
/*F********************************************************************************/
|
||
|
/*!
|
||
|
\Function _HttpDownloadProcess
|
||
|
|
||
|
\Description
|
||
|
Process a download transaction, using polling method
|
||
|
|
||
|
\Input *pRef - module state
|
||
|
|
||
|
\Version 10/28/2005 (jbrookes)
|
||
|
*/
|
||
|
/********************************************************************************F*/
|
||
|
static void _HttpDownloadProcess(HttpRefT *pRef)
|
||
|
{
|
||
|
int32_t iLen;
|
||
|
|
||
|
// if we're not doing the write callback thing, poll for data
|
||
|
for (iLen = 1; (iLen != PROTOHTTP_RECVWAIT) && (iLen != 0) && (pRef->state != IDLE); )
|
||
|
{
|
||
|
// receive data
|
||
|
iLen = _HttpRecvData(pRef);
|
||
|
// process data
|
||
|
_HttpDownloadProcessData(pRef, iLen);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*F********************************************************************************/
|
||
|
/*!
|
||
|
\Function _HttpUploadProcess
|
||
|
|
||
|
\Description
|
||
|
Process an upload transaction.
|
||
|
|
||
|
\Input *pRef - module state
|
||
|
|
||
|
\Version 10/28/2005 (jbrookes)
|
||
|
*/
|
||
|
/********************************************************************************F*/
|
||
|
static void _HttpUploadProcess(HttpRefT *pRef)
|
||
|
{
|
||
|
int32_t iResult;
|
||
|
|
||
|
// if no input file, nothing to send
|
||
|
if (pRef->iInpFile == ZFILE_INVALID)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// send all the data
|
||
|
while ((pRef->state == UPLOAD) && (pRef->iSentSize < pRef->iDataSize))
|
||
|
{
|
||
|
// do we need more data?
|
||
|
if (pRef->iSendBufSent == pRef->iSendBufData)
|
||
|
{
|
||
|
if ((pRef->iSendBufData = ZFileRead(pRef->iInpFile, pRef->strFileBuffer, sizeof(pRef->strFileBuffer))) > 0)
|
||
|
{
|
||
|
ZPrintf("%s: read %d bytes from file\n", pRef->strModuleName, pRef->iSendBufData);
|
||
|
pRef->iSendBufSent = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ZPrintf("%s: error %d reading from file\n", pRef->strModuleName, pRef->iSendBufData);
|
||
|
pRef->state = IDLE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// do we have buffered data to send?
|
||
|
if (pRef->iSendBufSent < pRef->iSendBufData)
|
||
|
{
|
||
|
iResult = ProtoHttpSend(pRef->http, pRef->strFileBuffer + pRef->iSendBufSent, pRef->iSendBufData - pRef->iSendBufSent);
|
||
|
if (iResult > 0)
|
||
|
{
|
||
|
pRef->iSentSize += iResult;
|
||
|
pRef->iSendBufSent += iResult;
|
||
|
ZPrintf("%s: sent %d bytes (%qd total)\n", pRef->strModuleName, iResult, pRef->iSentSize);
|
||
|
}
|
||
|
else if (iResult < 0)
|
||
|
{
|
||
|
ZPrintf("%s: ProtoHttpSend() failed; error %d\n", pRef->strModuleName, iResult);
|
||
|
pRef->state = IDLE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// check for upload completion
|
||
|
if (pRef->iSentSize == pRef->iDataSize)
|
||
|
{
|
||
|
int32_t ultime = NetTick() - pRef->sttime;
|
||
|
|
||
|
// if streaming upload, signal we are done
|
||
|
if (pRef->bStreaming == TRUE)
|
||
|
{
|
||
|
ProtoHttpSend(pRef->http, NULL, PROTOHTTP_STREAM_END);
|
||
|
pRef->bStreaming = FALSE;
|
||
|
}
|
||
|
|
||
|
// done uploading
|
||
|
ZPrintf("%s: upload complete (%qd bytes)\n", pRef->strModuleName, pRef->iSentSize);
|
||
|
ZPrintf("%s: upload time: %d bytes in %.2f seconds (%.3f k/sec)\n", pRef->strModuleName, pRef->iSentSize,
|
||
|
(float)ultime/1000.0f, ((float)pRef->iSentSize * 1000.0f) / ((float)ultime * 1024.0f));
|
||
|
|
||
|
// close the file
|
||
|
ZFileClose(pRef->iInpFile);
|
||
|
pRef->iInpFile = ZFILE_INVALID;
|
||
|
|
||
|
// transition to download state to receive server response
|
||
|
pRef->state = DNLOAD;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*F********************************************************************************/
|
||
|
/*!
|
||
|
\Function _CmdHttpIdleCB
|
||
|
|
||
|
\Description
|
||
|
Callback to process while idle
|
||
|
|
||
|
\Input *argz -
|
||
|
\Input argc -
|
||
|
\Input *argv[] -
|
||
|
|
||
|
\Output int32_t -
|
||
|
|
||
|
\Version 09/26/2007 (jbrookes)
|
||
|
*/
|
||
|
/********************************************************************************F*/
|
||
|
static int32_t _CmdHttpIdleCB(ZContext *argz, int32_t argc, char *argv[])
|
||
|
{
|
||
|
HttpRefT *pRef = &_Http_Ref;
|
||
|
|
||
|
// shut down?
|
||
|
if ((argc == 0) || (pRef->http == NULL))
|
||
|
{
|
||
|
if (pRef->http != NULL)
|
||
|
{
|
||
|
ProtoHttpDestroy(pRef->http);
|
||
|
pRef->http = NULL;
|
||
|
}
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
// give ref processing time
|
||
|
ProtoHttpUpdate(pRef->http);
|
||
|
|
||
|
// download processing (if not using write callback)
|
||
|
if ((pRef->state == DNLOAD) && (!pRef->bUseWriteCb))
|
||
|
{
|
||
|
_HttpDownloadProcess(pRef);
|
||
|
}
|
||
|
// upload processing
|
||
|
if (pRef->state == UPLOAD)
|
||
|
{
|
||
|
_HttpUploadProcess(pRef);
|
||
|
}
|
||
|
|
||
|
// keep on idling
|
||
|
return(ZCallback(&_CmdHttpIdleCB, HTTP_RATE));
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** Public Functions *************************************************************/
|
||
|
|
||
|
|
||
|
/*F********************************************************************************/
|
||
|
/*!
|
||
|
\Function CmdHttp
|
||
|
|
||
|
\Description
|
||
|
Initiate an HTTP transaction.
|
||
|
|
||
|
\Input *argz -
|
||
|
\Input argc -
|
||
|
\Input *argv[] -
|
||
|
|
||
|
\Output int32_t -
|
||
|
|
||
|
\Version 10/28/2005 (jbrookes)
|
||
|
*/
|
||
|
/********************************************************************************F*/
|
||
|
int32_t CmdHttp(ZContext *argz, int32_t argc, char *argv[])
|
||
|
{
|
||
|
const char *pFileName = NULL, *pUrl;
|
||
|
HttpRefT *pRef = &_Http_Ref;
|
||
|
int32_t iResult = 0, iBufSize = HTTP_BUFSIZE;
|
||
|
char strBuffer[4096];
|
||
|
int32_t iStartArg = 2; // first arg past get/put/delete/whatever
|
||
|
|
||
|
if (argc < 2)
|
||
|
{
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
// check for help
|
||
|
if ((argc >= 2) && !strcmp(argv[1], "help"))
|
||
|
{
|
||
|
_CmdHttpUsage(argc, argv);
|
||
|
return(iResult);
|
||
|
}
|
||
|
|
||
|
// check for 'parse' command
|
||
|
if ((argc == 3) && !strcmp(argv[1], "parse"))
|
||
|
{
|
||
|
char strKind[8], strHost[128];
|
||
|
const char *pUri;
|
||
|
int32_t iPort, iSecure;
|
||
|
ds_memclr(strKind, sizeof(strKind));
|
||
|
ds_memclr(strHost, sizeof(strHost));
|
||
|
pUri = ProtoHttpUrlParse(argv[2], strKind, sizeof(strKind), strHost, sizeof(strHost), &iPort, &iSecure);
|
||
|
ZPrintf("parsed url: kind=%s, host=%s, port=%d, secure=%d, uri=%s\n", strKind, strHost, iPort, iSecure, pUri);
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
// if not initialized yet, do so now
|
||
|
if (_Http_bInitialized == FALSE)
|
||
|
{
|
||
|
ds_memclr(pRef, sizeof(*pRef));
|
||
|
ds_strnzcpy(pRef->strModuleName, argv[0], sizeof(pRef->strModuleName));
|
||
|
pRef->iInpFile = ZFILE_INVALID;
|
||
|
pRef->iOutFile = ZFILE_INVALID;
|
||
|
_Http_bInitialized = TRUE;
|
||
|
}
|
||
|
|
||
|
// check for explicit destroy
|
||
|
if ((argc == 2) && !ds_stricmp(argv[1], "destroy"))
|
||
|
{
|
||
|
if (pRef->http != NULL)
|
||
|
{
|
||
|
ProtoHttpDestroy(pRef->http);
|
||
|
pRef->http = NULL;
|
||
|
}
|
||
|
if (pRef->pClientCert != NULL)
|
||
|
{
|
||
|
ZMemFree(pRef->pClientCert);
|
||
|
}
|
||
|
if (pRef->pClientKey != NULL)
|
||
|
{
|
||
|
ZMemFree(pRef->pClientKey);
|
||
|
}
|
||
|
_Http_bInitialized = FALSE;
|
||
|
return(iResult);
|
||
|
}
|
||
|
|
||
|
// check for create request
|
||
|
if ((argc == 3) && !strcmp(argv[1], "create"))
|
||
|
{
|
||
|
iBufSize = (int32_t)strtol(argv[2], NULL, 10);
|
||
|
}
|
||
|
|
||
|
// check for request to set a CA certificate
|
||
|
if ((argc == 3) && ((!strcmp(argv[1], "cert")) || (!strcmp(argv[1], "cer2"))))
|
||
|
{
|
||
|
const uint8_t *pFileData;
|
||
|
int32_t iFileSize;
|
||
|
|
||
|
// try and open file
|
||
|
if ((pFileData = (const uint8_t *)ZFileLoad(argv[2], &iFileSize, ZFILE_OPENFLAG_RDONLY|ZFILE_OPENFLAG_BINARY)) != NULL)
|
||
|
{
|
||
|
if (!strcmp(argv[1], "cert"))
|
||
|
{
|
||
|
iResult = ProtoHttpSetCACert(pFileData, iFileSize);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iResult = ProtoHttpSetCACert2(pFileData, iFileSize);
|
||
|
}
|
||
|
ZMemFree((void *)pFileData);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ZPrintf("%s: unable to load certificate file '%s'\n", argv[0], argv[2]);
|
||
|
}
|
||
|
return((iResult >= 0) ? 0 : -1);
|
||
|
}
|
||
|
else if (!strcmp(argv[1], "cclr"))
|
||
|
{
|
||
|
ZPrintf("%s: clearing dynamic CA certs\n", argv[0]);
|
||
|
ProtoHttpClrCACerts();
|
||
|
return(0);
|
||
|
}
|
||
|
else if (!strcmp(argv[1], "cver"))
|
||
|
{
|
||
|
int32_t iInvalid;
|
||
|
ZPrintf("%s: verifying dynamic CA certs\n", argv[0]);
|
||
|
if ((iInvalid = ProtoHttpValidateAllCA()) > 0)
|
||
|
{
|
||
|
ZPrintf("%s: could not verify %d CA certs\n", argv[0], iInvalid);
|
||
|
iResult = -1;
|
||
|
}
|
||
|
return(iResult);
|
||
|
}
|
||
|
else if ((argc == 3) && !strcmp(argv[1], "scrt"))
|
||
|
{
|
||
|
if ((pRef->pClientCert = (char *)_HttpLoadCertificate(argv[2], &pRef->iCertSize)) == NULL)
|
||
|
{
|
||
|
ZPrintf("%s: could not load client certificate '%s'\n", argv[0], argv[2]);
|
||
|
iResult = -1;
|
||
|
}
|
||
|
return(iResult);
|
||
|
}
|
||
|
else if ((argc == 3) && !strcmp(argv[1], "skey"))
|
||
|
{
|
||
|
if ((pRef->pClientKey = (char *)_HttpLoadCertificate(argv[2], &pRef->iKeySize)) == NULL)
|
||
|
{
|
||
|
ZPrintf("%s: could not load client private key '%s'\n", argv[0], argv[2]);
|
||
|
iResult = -1;
|
||
|
}
|
||
|
return(iResult);
|
||
|
}
|
||
|
|
||
|
// test url parsing
|
||
|
if ((argc == 3) && !strcmp(argv[1], "urlparse"))
|
||
|
{
|
||
|
return(_HttpUrlParseTest(argv[0], argv[2]));
|
||
|
}
|
||
|
|
||
|
// test url encoding
|
||
|
if ((argc >= 3) && !strcmp(argv[1], "urlencode"))
|
||
|
{
|
||
|
return(_HttpUrlEncodeTest(argv[0], argv+2, argc-2));
|
||
|
}
|
||
|
|
||
|
// create protohttp module if necessary
|
||
|
if (pRef->http == NULL)
|
||
|
{
|
||
|
// disable the secure flags to help with our testing setup
|
||
|
#if DIRTYCODE_GDK
|
||
|
ProtoHttpControl(pRef->http, 'secu', 0, 0, NULL);
|
||
|
#endif
|
||
|
|
||
|
ZPrintf("%s: creating module with a %dkbyte buffer\n", argv[0], iBufSize);
|
||
|
pRef->http = ProtoHttpCreate(iBufSize);
|
||
|
if (pRef->http != NULL)
|
||
|
{
|
||
|
ProtoHttpCallback(pRef->http, _HttpCustomHeaderCallback, _HttpRecvHeaderCallback, pRef);
|
||
|
pRef->iDebugLevel = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check for create request -- if so, we're done
|
||
|
if ((argc <= 3) && !strcmp(argv[1], "create"))
|
||
|
{
|
||
|
return(iResult);
|
||
|
}
|
||
|
else if ((argc > 2) && (argc < 7) && !strcmp(argv[1], "ctrl"))
|
||
|
{
|
||
|
int32_t iCmd, iValue = 0, iValue2 = 0;
|
||
|
const char *pValue = NULL;
|
||
|
|
||
|
iCmd = argv[2][0] << 24;
|
||
|
iCmd |= argv[2][1] << 16;
|
||
|
iCmd |= argv[2][2] << 8;
|
||
|
iCmd |= argv[2][3];
|
||
|
|
||
|
if (argc > 3)
|
||
|
{
|
||
|
iValue = _GetIntArg(argv[3]);
|
||
|
}
|
||
|
|
||
|
if (argc > 4)
|
||
|
{
|
||
|
iValue2 = _GetIntArg(argv[4]);
|
||
|
}
|
||
|
|
||
|
if (argc > 5)
|
||
|
{
|
||
|
pValue = argv[5];
|
||
|
}
|
||
|
|
||
|
// snoop 'spam'
|
||
|
if (iCmd == 'spam')
|
||
|
{
|
||
|
pRef->iDebugLevel = iValue;
|
||
|
}
|
||
|
|
||
|
return(ProtoHttpControl(pRef->http, iCmd, iValue, iValue2, (void *)pValue));
|
||
|
}
|
||
|
else if ((argc == 3) && !strcmp(argv[1], "stat"))
|
||
|
{
|
||
|
int32_t iCmd;
|
||
|
|
||
|
iCmd = argv[2][0] << 24;
|
||
|
iCmd |= argv[2][1] << 16;
|
||
|
iCmd |= argv[2][2] << 8;
|
||
|
iCmd |= argv[2][3];
|
||
|
|
||
|
iResult = ProtoHttpStatus(pRef->http, iCmd, strBuffer, sizeof(strBuffer));
|
||
|
ZPrintf("http: ProtoHttpStatus('%s') returned %d\n", argv[2], iResult);
|
||
|
if (strBuffer[0] != '\0')
|
||
|
{
|
||
|
ZPrintf("%s\n", strBuffer);
|
||
|
}
|
||
|
return(0);
|
||
|
}
|
||
|
// check for setting of base url
|
||
|
else if ((argc == 3) && !strcmp(argv[1], "base"))
|
||
|
{
|
||
|
ProtoHttpSetBaseUrl(pRef->http, argv[2]);
|
||
|
return(iResult);
|
||
|
}
|
||
|
// check for debug setting
|
||
|
else if (!strcmp(argv[1], "debug") && (argc == 3))
|
||
|
{
|
||
|
int32_t iDebugLevel = (int32_t)strtol(argv[2], NULL, 10);
|
||
|
pRef->iDebugLevel = iDebugLevel;
|
||
|
return(iResult);
|
||
|
}
|
||
|
else if (!ds_stricmp(argv[1], "abrt"))
|
||
|
{
|
||
|
ProtoHttpAbort(pRef->http);
|
||
|
pRef->state = IDLE;
|
||
|
return(iResult);
|
||
|
}
|
||
|
|
||
|
// check for valid get/put request
|
||
|
else if ((!ds_stricmp(argv[1], "get") || !ds_stricmp(argv[1], "head") || !ds_stricmp(argv[1], "put") || !ds_stricmp(argv[1], "post") ||
|
||
|
!ds_stricmp(argv[1], "puts") || !ds_stricmp(argv[1], "delete") || !ds_stricmp(argv[1], "options")) &&
|
||
|
((argc > 2) || (argc < 6)))
|
||
|
{
|
||
|
int32_t iArg;
|
||
|
|
||
|
// reset to clear any previous stats
|
||
|
_HttpReset(pRef);
|
||
|
|
||
|
// init start timer
|
||
|
pRef->sttime = NetTick();
|
||
|
|
||
|
// set up append header
|
||
|
ds_strnzcpy(pRef->strApndHdr, HTTP_APNDHDR, sizeof(pRef->strApndHdr));
|
||
|
|
||
|
// check for args
|
||
|
for (iArg = 2; (iArg < argc) && (argv[iArg][0] == '-'); iArg += 1)
|
||
|
{
|
||
|
if (!ds_strnicmp(argv[iArg], "-header=", 8))
|
||
|
{
|
||
|
ds_strnzcat(pRef->strApndHdr, argv[iArg]+8, sizeof(pRef->strApndHdr));
|
||
|
ds_strnzcat(pRef->strApndHdr, "\r\n", sizeof(pRef->strApndHdr));
|
||
|
}
|
||
|
if (!ds_strnicmp(argv[iArg], "-writecb", 8))
|
||
|
{
|
||
|
pRef->bUseWriteCb = TRUE;
|
||
|
}
|
||
|
if (!ds_strnicmp(argv[iArg], "-recvall", 8))
|
||
|
{
|
||
|
pRef->bRecvAll = TRUE;
|
||
|
}
|
||
|
// skip any option arguments to find url and (optionally) filename
|
||
|
iStartArg += 1;
|
||
|
}
|
||
|
|
||
|
// set client cert if specified
|
||
|
if (pRef->pClientCert != NULL)
|
||
|
{
|
||
|
ProtoHttpControl(pRef->http, 'scrt', pRef->iCertSize, 0, pRef->pClientCert);
|
||
|
}
|
||
|
// set client key if specified
|
||
|
if (pRef->pClientKey != NULL)
|
||
|
{
|
||
|
ProtoHttpControl(pRef->http, 'skey', pRef->iKeySize, 0, pRef->pClientKey);
|
||
|
}
|
||
|
|
||
|
// fall-through to code below
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ZPrintf(" unrecognized or badly formatted command '%s'\n", argv[1]);
|
||
|
_CmdHttpUsage(argc, argv);
|
||
|
return(iResult);
|
||
|
}
|
||
|
|
||
|
// locate url and filename
|
||
|
pUrl = argv[iStartArg];
|
||
|
if (argc > iStartArg)
|
||
|
{
|
||
|
pFileName = argv[iStartArg+1];
|
||
|
}
|
||
|
|
||
|
// do we have a url?
|
||
|
if (pUrl != NULL)
|
||
|
{
|
||
|
const char* pTemp = pUrl; //< Used for parsing the query param
|
||
|
char strKind[5], strHost[128], strName[32], strValue[32];
|
||
|
int32_t iPort, iSecure;
|
||
|
|
||
|
// get url info
|
||
|
ProtoHttpUrlParse(pUrl, strKind, sizeof(strKind), strHost, sizeof(strHost), &iPort, &iSecure);
|
||
|
|
||
|
// if the host has changed, reset cookie buffer
|
||
|
if (ds_stricmp(pRef->strHost, strHost) && (pRef->strCookie[0] != '\0'))
|
||
|
{
|
||
|
ZPrintf("http: host change to %s; resetting cookies from %s\n", strHost, pRef->strHost);
|
||
|
pRef->strCookie[0] = '\0';
|
||
|
}
|
||
|
// save host
|
||
|
ds_strnzcpy(pRef->strHost, strHost, sizeof(strHost));
|
||
|
|
||
|
while (ProtoHttpGetNextParam(pRef->http, pTemp, strName, sizeof(strName), strValue, sizeof(strValue), &pTemp) == 0)
|
||
|
{
|
||
|
ProtoHttpUrlDecodeStrParm(strValue, strValue, sizeof(strValue));
|
||
|
|
||
|
#if 0
|
||
|
ZPrintf("http query param: key=%s, value=%s\n", strName, strValue);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
// set append header
|
||
|
strBuffer[0] = '\0';
|
||
|
if (pRef->strCookie[0] != '\0')
|
||
|
{
|
||
|
ds_snzprintf(strBuffer, sizeof(strBuffer), "Cookie: %s\r\n", pRef->strCookie);
|
||
|
}
|
||
|
ds_strnzcat(strBuffer, pRef->strApndHdr, sizeof(strBuffer));
|
||
|
ProtoHttpControl(pRef->http, 'apnd', 0, 0, strBuffer);
|
||
|
// set debug level
|
||
|
ProtoHttpControl(pRef->http, 'spam', pRef->iDebugLevel, 0, NULL);
|
||
|
|
||
|
// if we're uploading, open required input file
|
||
|
if (!ds_stricmp(argv[1], "put") || !ds_stricmp(argv[1], "post") || !ds_stricmp(argv[1], "puts"))
|
||
|
{
|
||
|
ZPrintf("%s: uploading %s to %s\n", argv[0], pFileName, pUrl);
|
||
|
|
||
|
// assume failure
|
||
|
iResult = -1;
|
||
|
|
||
|
// try and open file
|
||
|
if ((pRef->iInpFile = ZFileOpen(pFileName, ZFILE_OPENFLAG_RDONLY|ZFILE_OPENFLAG_BINARY)) != ZFILE_INVALID)
|
||
|
{
|
||
|
// get the file size
|
||
|
if ((pRef->iDataSize = ZFileSize(pRef->iInpFile)) > 0)
|
||
|
{
|
||
|
// load data from file
|
||
|
if ((pRef->iSendBufData = ZFileRead(pRef->iInpFile, pRef->strFileBuffer, sizeof(pRef->strFileBuffer))) > 0)
|
||
|
{
|
||
|
if (ds_stricmp(argv[1], "puts"))
|
||
|
{
|
||
|
// initiate put/post transaction
|
||
|
ZPrintf("%s: uploading %d bytes\n", argv[0], pRef->iDataSize);
|
||
|
if ((pRef->iSendBufSent = ProtoHttpPost(pRef->http, pUrl, pRef->strFileBuffer, pRef->iDataSize, !ds_stricmp(argv[1], "put") ? PROTOHTTP_PUT : PROTOHTTP_POST)) < 0)
|
||
|
{
|
||
|
ZPrintf("%s: error %d initiating send\n", argv[0], pRef->iSendBufSent);
|
||
|
iResult = -1;
|
||
|
}
|
||
|
else if (pRef->iSendBufSent >= 0)
|
||
|
{
|
||
|
ZPrintf("%s: sent %d bytes\n", argv[0], pRef->iSendBufSent);
|
||
|
pRef->iSentSize = pRef->iSendBufSent;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// initiate streaming put
|
||
|
if ((iResult = ProtoHttpPost(pRef->http, pUrl, NULL, PROTOHTTP_STREAM_BEGIN, PROTOHTTP_POST)) >= 0)
|
||
|
{
|
||
|
pRef->bStreaming = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// wait for reply
|
||
|
pRef->state = UPLOAD;
|
||
|
|
||
|
// locate output file
|
||
|
pFileName = (argc > (iStartArg+2)) ? argv[iStartArg+2] : NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ZPrintf("%s: error %d reading data from file\n", argv[0], pRef->iSendBufData, pFileName);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ZPrintf("%s: error %d getting size of file %s\n", argv[0], pRef->iDataSize, pFileName);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ZPrintf("%s: unable to load file '%s'\n", argv[0], pFileName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// open output file?
|
||
|
if (pFileName != NULL)
|
||
|
{
|
||
|
ZPrintf("%s: saving %s data to %s\n", argv[0], pUrl, pFileName);
|
||
|
if ((pRef->iOutFile = ZFileOpen(pFileName, ZFILE_OPENFLAG_WRONLY|ZFILE_OPENFLAG_BINARY|ZFILE_OPENFLAG_CREATE)) == ZFILE_INVALID)
|
||
|
{
|
||
|
ZPrintf("%s: error opening file '%s' for writing\n", argv[0], pFileName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// issue request
|
||
|
if (!ds_stricmp(argv[1], "get") || !ds_stricmp(argv[1], "head") || !ds_stricmp(argv[1], "delete") || !ds_stricmp(argv[1], "options"))
|
||
|
{
|
||
|
ProtoHttpRequestTypeE eRequestType;
|
||
|
|
||
|
ZPrintf("%s: downloading %s\n", argv[0], pUrl);
|
||
|
|
||
|
// map to protohttp request type
|
||
|
if (!ds_stricmp(argv[1], "head"))
|
||
|
{
|
||
|
eRequestType = PROTOHTTP_REQUESTTYPE_HEAD;
|
||
|
}
|
||
|
else if (!ds_stricmp(argv[1], "get"))
|
||
|
{
|
||
|
eRequestType = PROTOHTTP_REQUESTTYPE_GET;
|
||
|
}
|
||
|
else if (!ds_stricmp(argv[1], "delete"))
|
||
|
{
|
||
|
eRequestType = PROTOHTTP_REQUESTTYPE_DELETE;
|
||
|
}
|
||
|
else if (!ds_stricmp(argv[1], "options"))
|
||
|
{
|
||
|
eRequestType = PROTOHTTP_REQUESTTYPE_OPTIONS;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ZPrintf("%s: unrecognized request %s\n", argv[0], argv[1]);
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
if (pRef->bUseWriteCb)
|
||
|
{
|
||
|
iResult = ProtoHttpRequestCb(pRef->http, pUrl, NULL, 0, eRequestType, _HttpWriteCb, pRef);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iResult = ProtoHttpRequest(pRef->http, pUrl, NULL, 0, eRequestType);
|
||
|
}
|
||
|
|
||
|
if (iResult == 0)
|
||
|
{
|
||
|
pRef->state = DNLOAD;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// set up recurring callback to process transaction
|
||
|
if (pRef->state != IDLE)
|
||
|
{
|
||
|
iResult = ZCallback(_CmdHttpIdleCB, HTTP_RATE);
|
||
|
}
|
||
|
|
||
|
return(iResult);
|
||
|
}
|
||
|
|