mirror of
https://github.com/oopuuu/zTC1.git
synced 2025-12-15 06:28:14 +08:00
1268 lines
48 KiB
C
1268 lines
48 KiB
C
/**
|
|
******************************************************************************
|
|
* @file HTTPUtils.c
|
|
* @author William Xu
|
|
* @version V1.0.0
|
|
* @date 05-May-2014
|
|
* @brief These functions assist with interacting with HTTP clients and servers.
|
|
******************************************************************************
|
|
*
|
|
* The MIT License
|
|
* Copyright (c) 2014 MXCHIP Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is furnished
|
|
* to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include "debug.h"
|
|
#include "StringUtils.h"
|
|
#include "HTTPUtils.h"
|
|
#include "platform.h"
|
|
#include "mico.h"
|
|
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
|
|
#define kCRLFNewLine "\r\n"
|
|
#define kCRLFLineEnding "\r\n\r\n"
|
|
|
|
#define http_utils_log(M, ...) custom_log("HTTPUtils", M, ##__VA_ARGS__)
|
|
|
|
#define READ_LENGTH 1500
|
|
|
|
|
|
int SocketReadHTTPHeader( int inSock, HTTPHeader_t *inHeader )
|
|
{
|
|
int err =0;
|
|
char * buf;
|
|
char * dst;
|
|
char * lim;
|
|
char * end;
|
|
size_t len;
|
|
ssize_t n;
|
|
|
|
buf = inHeader->buf;
|
|
dst = buf + inHeader->len;
|
|
lim = buf + inHeader->bufLen;
|
|
for( ;; )
|
|
{
|
|
if(findHeader( inHeader, &end ))
|
|
break ;
|
|
n = read( inSock, dst, (size_t)( lim - dst ) );
|
|
if( n > 0 ) len = (size_t) n;
|
|
else { err = kConnectionErr; goto exit; }
|
|
dst += len;
|
|
inHeader->len += len;
|
|
}
|
|
|
|
inHeader->len = (size_t)( end - buf );
|
|
err = HTTPHeaderParse( inHeader );
|
|
require_noerr( err, exit );
|
|
inHeader->extraDataLen = (size_t)( dst - end );
|
|
if(inHeader->extraDataPtr) {
|
|
free((uint8_t *)inHeader->extraDataPtr);
|
|
inHeader->extraDataPtr = 0;
|
|
}
|
|
|
|
/* For chunked extra data without content length */
|
|
if(inHeader->chunkedData == true){
|
|
inHeader->chunkedDataBufferLen = (inHeader->extraDataLen > READ_LENGTH)? inHeader->extraDataLen:READ_LENGTH;
|
|
inHeader->chunkedDataBufferPtr = calloc(inHeader->chunkedDataBufferLen, sizeof(uint8_t)); //Make extra data buffer larger than chunk length
|
|
require_action(inHeader->chunkedDataBufferPtr, exit, err = kNoMemoryErr);
|
|
memcpy((uint8_t *)inHeader->chunkedDataBufferPtr, end, inHeader->extraDataLen);
|
|
inHeader->extraDataPtr = inHeader->chunkedDataBufferPtr;
|
|
return kNoErr;
|
|
}
|
|
|
|
/* Extra data with content length */
|
|
if (inHeader->contentLength != 0){ //Content length >0, create a memory buffer (Content length) and store extra data
|
|
size_t copyDataLen = (inHeader->contentLength >= inHeader->extraDataLen)? inHeader->extraDataLen : inHeader->contentLength;
|
|
if(inHeader->onReceivedDataCallback && (inHeader->onReceivedDataCallback)(inHeader, 0, (uint8_t *)end, copyDataLen, inHeader->userContext)==kNoErr){
|
|
inHeader->isCallbackSupported = true;
|
|
inHeader->extraDataPtr = calloc(READ_LENGTH, sizeof(uint8_t));
|
|
require_action(inHeader->extraDataPtr, exit, err = kNoMemoryErr);
|
|
inHeader->extraDataLen = copyDataLen;
|
|
}else{
|
|
inHeader->isCallbackSupported = false;
|
|
inHeader->extraDataPtr = calloc(inHeader->contentLength , sizeof(uint8_t));
|
|
require_action(inHeader->extraDataPtr, exit, err = kNoMemoryErr);
|
|
memcpy((uint8_t *)inHeader->extraDataPtr, end, copyDataLen);
|
|
}
|
|
err = kNoErr;
|
|
} /* Extra data without content length, data is ended by conntection close */
|
|
// else if(inHeader->extraDataLen != 0){ //Content length =0, but extra data length >0, create a memory buffer (1500)and store extra data
|
|
// inHeader->dataEndedbyClose = true;
|
|
// inHeader->extraDataPtr = calloc(1500, sizeof(uint8_t));
|
|
// require_action(inHeader->extraDataPtr, exit, err = kNoMemoryErr);
|
|
// memcpy((uint8_t *)inHeader->extraDataPtr, end, inHeader->extraDataLen);
|
|
// (inHeader->onReceivedDataCallback)(inHeader, 0, (uint8_t *)inHeader->extraDataPtr, inHeader->extraDataLen, inHeader->userContext);
|
|
|
|
// err = kNoErr;
|
|
// }
|
|
else
|
|
return kNoErr;
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
|
|
OSStatus SocketReadHTTPBody( int inSock, HTTPHeader_t *inHeader )
|
|
{
|
|
OSStatus err = kParamErr;
|
|
ssize_t readResult;
|
|
int selectResult;
|
|
fd_set readSet;
|
|
size_t lastChunkLen, chunckheaderLen;
|
|
char *nextPackagePtr;
|
|
struct timeval t;
|
|
size_t readLength;
|
|
uint32_t pos = 0;
|
|
t.tv_sec = 5;
|
|
t.tv_usec = 0;
|
|
|
|
require( inHeader, exit );
|
|
err = kNotReadableErr;
|
|
|
|
FD_ZERO( &readSet );
|
|
FD_SET( inSock, &readSet );
|
|
|
|
/* Chunked data without content length */
|
|
if( inHeader->chunkedData == true ){
|
|
do{
|
|
/* Find Chunk data length */
|
|
while ( findChunkedDataLength( inHeader->chunkedDataBufferPtr, inHeader->extraDataLen, &inHeader->extraDataPtr ,"%llu", &inHeader->contentLength ) == false){
|
|
require_action(inHeader->extraDataLen < inHeader->chunkedDataBufferLen, exit, err=kMalformedErr );
|
|
|
|
selectResult = select( inSock + 1, &readSet, NULL, NULL, NULL );
|
|
require_action( selectResult >= 1, exit, err = kNotReadableErr );
|
|
|
|
readResult = read( inSock, inHeader->chunkedDataBufferPtr + inHeader->extraDataLen, (size_t)( inHeader->chunkedDataBufferLen - inHeader->extraDataLen ) );
|
|
|
|
if( readResult > 0 ) inHeader->extraDataLen += readResult;
|
|
else { err = kConnectionErr; goto exit; }
|
|
}
|
|
|
|
chunckheaderLen = inHeader->extraDataPtr - inHeader->chunkedDataBufferPtr;
|
|
|
|
/* Check the last chunk */
|
|
if(inHeader->contentLength == 0){
|
|
while( findCRLF( inHeader->extraDataPtr, inHeader->extraDataLen - chunckheaderLen, &nextPackagePtr ) == false){ //find CRLF
|
|
selectResult = select( inSock + 1, &readSet, NULL, NULL, NULL );
|
|
require_action( selectResult >= 1, exit, err = kNotReadableErr );
|
|
|
|
readResult = read( inSock,
|
|
(uint8_t *)( inHeader->extraDataPtr + inHeader->extraDataLen - chunckheaderLen ),
|
|
256 - inHeader->extraDataLen ); //Assume chunk trailer length is less than 256 (256 is the min chunk buffer, maybe dangerous
|
|
|
|
if( readResult > 0 ) inHeader->extraDataLen += readResult;
|
|
else { err = kConnectionErr; goto exit; }
|
|
(inHeader->onReceivedDataCallback)(inHeader, inHeader->extraDataLen - readResult, (uint8_t *)inHeader->extraDataPtr, readResult, inHeader->userContext);
|
|
}
|
|
|
|
err = kNoErr;
|
|
goto exit;
|
|
}
|
|
else{ // Read chunk data
|
|
/* Chunk package already exist, callback, move to buffer's head */
|
|
if( inHeader->extraDataLen >= inHeader->contentLength + chunckheaderLen + 2 ){
|
|
require_action( *(inHeader->extraDataPtr + inHeader->contentLength ) == '\r' &&
|
|
*(inHeader->extraDataPtr + inHeader->contentLength + 1 ) == '\n',
|
|
exit, err = kMalformedErr);
|
|
|
|
(inHeader->onReceivedDataCallback)(inHeader, pos,
|
|
(uint8_t *)inHeader->extraDataPtr,
|
|
inHeader->contentLength,
|
|
inHeader->userContext);
|
|
pos+=inHeader->contentLength;
|
|
|
|
/* Move next chunk to chunked data buffer header point */
|
|
lastChunkLen = inHeader->extraDataPtr - inHeader->chunkedDataBufferPtr + inHeader->contentLength;
|
|
if(inHeader->contentLength) lastChunkLen += 2; //Last chunck data has a CRLF tail
|
|
memmove( inHeader->chunkedDataBufferPtr, inHeader->chunkedDataBufferPtr + lastChunkLen, inHeader->chunkedDataBufferLen - lastChunkLen );
|
|
inHeader->extraDataLen -= lastChunkLen;
|
|
}
|
|
/* Chunck exist without the last LF, generate callback abd recv LF */
|
|
/* Callback , read LF */
|
|
else if(inHeader->extraDataLen == inHeader->contentLength + chunckheaderLen +1){ //recv CR
|
|
|
|
require_action( *(inHeader->chunkedDataBufferPtr + inHeader->extraDataLen - 1) == '\r' ,
|
|
exit, err = kMalformedErr);
|
|
|
|
(inHeader->onReceivedDataCallback)(inHeader, pos,
|
|
(uint8_t *)inHeader->extraDataPtr,
|
|
inHeader->extraDataLen - chunckheaderLen-1,
|
|
inHeader->userContext);
|
|
pos+=inHeader->extraDataLen - chunckheaderLen-1;
|
|
selectResult = select( inSock + 1, &readSet, NULL, NULL, NULL );
|
|
require_action( selectResult >= 1, exit, err = kNotReadableErr );
|
|
|
|
readResult = read( inSock, (uint8_t *)inHeader->extraDataPtr, 1);
|
|
|
|
if( readResult > 0 ) {}
|
|
else { err = kConnectionErr; goto exit; }
|
|
|
|
require_action( *(inHeader->extraDataPtr) == '\n', exit, err = kMalformedErr);
|
|
inHeader->extraDataLen = 0;
|
|
}
|
|
else{
|
|
/* Callback , read , callback , read CRLF */
|
|
(inHeader->onReceivedDataCallback)(inHeader, pos,
|
|
(uint8_t *)inHeader->extraDataPtr,
|
|
inHeader->extraDataLen - chunckheaderLen,
|
|
inHeader->userContext);
|
|
pos += inHeader->extraDataLen - chunckheaderLen;
|
|
|
|
while ( inHeader->extraDataLen < inHeader->contentLength + chunckheaderLen ){
|
|
selectResult = select( inSock + 1, &readSet, NULL, NULL, NULL );
|
|
require_action( selectResult >= 1, exit, err = kNotReadableErr );
|
|
|
|
if( inHeader->contentLength - (inHeader->extraDataLen - chunckheaderLen) > inHeader->chunkedDataBufferLen - chunckheaderLen)
|
|
//Data needed is greater than valid buffer size
|
|
readLength = inHeader->chunkedDataBufferLen - chunckheaderLen;
|
|
else
|
|
//Data needed is less than valid buffer size
|
|
readLength = inHeader->contentLength - (inHeader->extraDataLen - chunckheaderLen) ;
|
|
|
|
readResult = read( inSock, (uint8_t *)inHeader->extraDataPtr, readLength);
|
|
|
|
if( readResult > 0 ) inHeader->extraDataLen += readResult;
|
|
else { err = kConnectionErr; goto exit; }
|
|
|
|
(inHeader->onReceivedDataCallback)(inHeader, pos,
|
|
(uint8_t *)inHeader->extraDataPtr,
|
|
readResult,
|
|
inHeader->userContext);
|
|
pos += readResult;
|
|
}
|
|
|
|
selectResult = select( inSock + 1, &readSet, NULL, NULL, NULL );
|
|
require_action( selectResult >= 1, exit, err = kNotReadableErr );
|
|
|
|
readResult = read( inSock, (uint8_t *)inHeader->extraDataPtr, 2);
|
|
|
|
if( readResult > 0 ) {}
|
|
else { err = kConnectionErr; goto exit; }
|
|
|
|
|
|
require_action( *(inHeader->extraDataPtr) == '\r' &&
|
|
*(inHeader->extraDataPtr+1 ) == '\n',
|
|
exit, err = kMalformedErr);
|
|
|
|
inHeader->extraDataLen = 0;
|
|
}
|
|
}
|
|
}while(inHeader->contentLength != 0);
|
|
}
|
|
|
|
/* We has extra data but total length is not clear, store them to 1500 bytes buffer
|
|
return when connection is disconnected by remote server */
|
|
// if( inHeader->dataEndedbyClose == true){
|
|
// if(inHeader->contentLength == 0) { //First read body, return using data received by SocketReadHTTPHeader
|
|
// inHeader->contentLength = inHeader->extraDataLen;
|
|
// }else{
|
|
// selectResult = select( inSock + 1, &readSet, NULL, NULL, NULL );
|
|
// require_action( selectResult >= 1, exit, err = kNotReadableErr );
|
|
|
|
// readResult = read( inSock,
|
|
// (uint8_t*)( inHeader->extraDataPtr ),
|
|
// 1500 );
|
|
// if( readResult > 0 ) inHeader->contentLength = readResult;
|
|
// else { err = kConnectionErr; goto exit; }
|
|
// }
|
|
// err = kNoErr;
|
|
// goto exit;
|
|
// }
|
|
|
|
while ( inHeader->extraDataLen < inHeader->contentLength )
|
|
{
|
|
selectResult = select( inSock + 1, &readSet, NULL, NULL, (struct timeval *)&t );
|
|
require_action( selectResult >= 1, exit, err = kNotReadableErr );
|
|
|
|
if(inHeader->isCallbackSupported == true){
|
|
/* We has extra data, and we give these data to application by onReceivedDataCallback function */
|
|
readLength = inHeader->contentLength - inHeader->extraDataLen > READ_LENGTH? READ_LENGTH:inHeader->contentLength - inHeader->extraDataLen;
|
|
readResult = read( inSock,
|
|
(uint8_t*)( inHeader->extraDataPtr),
|
|
readLength );
|
|
|
|
if( readResult > 0 ) inHeader->extraDataLen += readResult;
|
|
else { err = kConnectionErr; goto exit; }
|
|
err = (inHeader->onReceivedDataCallback)(inHeader, inHeader->extraDataLen - readResult, (uint8_t *)inHeader->extraDataPtr, readResult, inHeader->userContext);
|
|
if( err != kNoErr ) goto exit;
|
|
}else{
|
|
/* We has extra data and we has a predefined buffer to store the total extra data return when all data has received*/
|
|
readResult = read( inSock,
|
|
(uint8_t*)( inHeader->extraDataPtr + inHeader->extraDataLen ),
|
|
( inHeader->contentLength - inHeader->extraDataLen ) );
|
|
|
|
if( readResult > 0 ) inHeader->extraDataLen += readResult;
|
|
else { err = kConnectionErr; goto exit; }
|
|
}
|
|
}
|
|
err = kNoErr;
|
|
|
|
exit:
|
|
if(err != kNoErr) inHeader->len = 0;
|
|
return err;
|
|
}
|
|
|
|
OSStatus SocketReadHTTPSBody( mico_ssl_t ssl, HTTPHeader_t *inHeader )
|
|
{
|
|
OSStatus err = kParamErr;
|
|
ssize_t readResult;
|
|
int selectResult;
|
|
fd_set readSet;
|
|
size_t lastChunkLen, chunckheaderLen;
|
|
char *nextPackagePtr;
|
|
struct timeval t;
|
|
size_t readLength;
|
|
uint32_t pos = 0;
|
|
t.tv_sec = 5;
|
|
t.tv_usec = 0;
|
|
int inSock = ssl_socket( ssl );
|
|
|
|
require( inHeader, exit );
|
|
err = kNotReadableErr;
|
|
|
|
FD_ZERO( &readSet );
|
|
FD_SET( inSock, &readSet );
|
|
|
|
/* Chunked data without content length */
|
|
if( inHeader->chunkedData == true ){
|
|
do{
|
|
/* Find Chunk data length */
|
|
while ( findChunkedDataLength( inHeader->chunkedDataBufferPtr, inHeader->extraDataLen, &inHeader->extraDataPtr ,"%llu", &inHeader->contentLength ) == false){
|
|
require_action(inHeader->extraDataLen < inHeader->chunkedDataBufferLen, exit, err=kMalformedErr );
|
|
|
|
selectResult = select( inSock + 1, &readSet, NULL, NULL, NULL );
|
|
require_action( selectResult >= 1, exit, err = kNotReadableErr );
|
|
|
|
readResult = ssl_recv( ssl, inHeader->chunkedDataBufferPtr + inHeader->extraDataLen, (size_t)( inHeader->chunkedDataBufferLen - inHeader->extraDataLen ) );
|
|
|
|
if( readResult > 0 ) inHeader->extraDataLen += readResult;
|
|
else { err = kConnectionErr; goto exit; }
|
|
}
|
|
|
|
chunckheaderLen = inHeader->extraDataPtr - inHeader->chunkedDataBufferPtr;
|
|
|
|
/* Check the last chunk */
|
|
if(inHeader->contentLength == 0){
|
|
while( findCRLF( inHeader->extraDataPtr, inHeader->extraDataLen - chunckheaderLen, &nextPackagePtr ) == false){ //find CRLF
|
|
selectResult = select( inSock + 1, &readSet, NULL, NULL, NULL );
|
|
require_action( selectResult >= 1, exit, err = kNotReadableErr );
|
|
|
|
readResult = ssl_recv( ssl,
|
|
(uint8_t *)( inHeader->extraDataPtr + inHeader->extraDataLen - chunckheaderLen ),
|
|
256 - inHeader->extraDataLen ); //Assume chunk trailer length is less than 256 (256 is the min chunk buffer, maybe dangerous
|
|
|
|
if( readResult > 0 ) inHeader->extraDataLen += readResult;
|
|
else { err = kConnectionErr; goto exit; }
|
|
(inHeader->onReceivedDataCallback)(inHeader, inHeader->extraDataLen - readResult, (uint8_t *)inHeader->extraDataPtr, readResult, inHeader->userContext);
|
|
}
|
|
|
|
err = kNoErr;
|
|
goto exit;
|
|
}
|
|
else{ // Read chunk data
|
|
/* Chunk package already exist, callback, move to buffer's head */
|
|
if( inHeader->extraDataLen >= inHeader->contentLength + chunckheaderLen + 2 ){
|
|
require_action( *(inHeader->extraDataPtr + inHeader->contentLength ) == '\r' &&
|
|
*(inHeader->extraDataPtr + inHeader->contentLength + 1 ) == '\n',
|
|
exit, err = kMalformedErr);
|
|
|
|
(inHeader->onReceivedDataCallback)(inHeader, pos,
|
|
(uint8_t *)inHeader->extraDataPtr,
|
|
inHeader->contentLength,
|
|
inHeader->userContext);
|
|
pos+=inHeader->contentLength;
|
|
|
|
/* Move next chunk to chunked data buffer header point */
|
|
lastChunkLen = inHeader->extraDataPtr - inHeader->chunkedDataBufferPtr + inHeader->contentLength;
|
|
if(inHeader->contentLength) lastChunkLen += 2; //Last chunck data has a CRLF tail
|
|
memmove( inHeader->chunkedDataBufferPtr, inHeader->chunkedDataBufferPtr + lastChunkLen, inHeader->chunkedDataBufferLen - lastChunkLen );
|
|
inHeader->extraDataLen -= lastChunkLen;
|
|
}
|
|
/* Chunck exist without the last LF, generate callback abd recv LF */
|
|
/* Callback , read LF */
|
|
else if(inHeader->extraDataLen == inHeader->contentLength + chunckheaderLen +1){ //recv CR
|
|
|
|
require_action( *(inHeader->chunkedDataBufferPtr + inHeader->extraDataLen - 1) == '\r' ,
|
|
exit, err = kMalformedErr);
|
|
|
|
(inHeader->onReceivedDataCallback)(inHeader, pos,
|
|
(uint8_t *)inHeader->extraDataPtr,
|
|
inHeader->extraDataLen - chunckheaderLen-1,
|
|
inHeader->userContext);
|
|
pos+=inHeader->extraDataLen - chunckheaderLen-1;
|
|
selectResult = select( inSock + 1, &readSet, NULL, NULL, NULL );
|
|
require_action( selectResult >= 1, exit, err = kNotReadableErr );
|
|
|
|
readResult = ssl_recv( ssl, (uint8_t *)inHeader->extraDataPtr, 1);
|
|
|
|
if( readResult > 0 ) {}
|
|
else { err = kConnectionErr; goto exit; }
|
|
|
|
require_action( *(inHeader->extraDataPtr) == '\n', exit, err = kMalformedErr);
|
|
inHeader->extraDataLen = 0;
|
|
}
|
|
else{
|
|
/* Callback , read , callback , read CRLF */
|
|
(inHeader->onReceivedDataCallback)(inHeader, pos,
|
|
(uint8_t *)inHeader->extraDataPtr,
|
|
inHeader->extraDataLen - chunckheaderLen,
|
|
inHeader->userContext);
|
|
pos += inHeader->extraDataLen - chunckheaderLen;
|
|
|
|
while ( inHeader->extraDataLen < inHeader->contentLength + chunckheaderLen ){
|
|
selectResult = select( inSock + 1, &readSet, NULL, NULL, NULL );
|
|
require_action( selectResult >= 1, exit, err = kNotReadableErr );
|
|
|
|
if( inHeader->contentLength - (inHeader->extraDataLen - chunckheaderLen) > inHeader->chunkedDataBufferLen - chunckheaderLen)
|
|
//Data needed is greater than valid buffer size
|
|
readLength = inHeader->chunkedDataBufferLen - chunckheaderLen;
|
|
else
|
|
//Data needed is less than valid buffer size
|
|
readLength = inHeader->contentLength - (inHeader->extraDataLen - chunckheaderLen) ;
|
|
|
|
readResult = ssl_recv( ssl, (uint8_t *)inHeader->extraDataPtr, readLength);
|
|
|
|
if( readResult > 0 ) inHeader->extraDataLen += readResult;
|
|
else { err = kConnectionErr; goto exit; }
|
|
|
|
(inHeader->onReceivedDataCallback)(inHeader, pos,
|
|
(uint8_t *)inHeader->extraDataPtr,
|
|
readResult,
|
|
inHeader->userContext);
|
|
pos += readResult;
|
|
}
|
|
|
|
selectResult = select( inSock + 1, &readSet, NULL, NULL, NULL );
|
|
require_action( selectResult >= 1, exit, err = kNotReadableErr );
|
|
|
|
readResult = ssl_recv( ssl, (uint8_t *)inHeader->extraDataPtr, 2);
|
|
|
|
if( readResult > 0 ) {}
|
|
else { err = kConnectionErr; goto exit; }
|
|
|
|
|
|
require_action( *(inHeader->extraDataPtr) == '\r' &&
|
|
*(inHeader->extraDataPtr+1 ) == '\n',
|
|
exit, err = kMalformedErr);
|
|
|
|
inHeader->extraDataLen = 0;
|
|
}
|
|
}
|
|
}while(inHeader->contentLength != 0);
|
|
}
|
|
|
|
/* We has extra data but total length is not clear, store them to 1500 bytes buffer
|
|
return when connection is disconnected by remote server */
|
|
// if( inHeader->dataEndedbyClose == true){
|
|
// if(inHeader->contentLength == 0) { //First read body, return using data received by SocketReadHTTPHeader
|
|
// inHeader->contentLength = inHeader->extraDataLen;
|
|
// }else{
|
|
// selectResult = select( inSock + 1, &readSet, NULL, NULL, NULL );
|
|
// require_action( selectResult >= 1, exit, err = kNotReadableErr );
|
|
|
|
// readResult = read( inSock,
|
|
// (uint8_t*)( inHeader->extraDataPtr ),
|
|
// 1500 );
|
|
// if( readResult > 0 ) inHeader->contentLength = readResult;
|
|
// else { err = kConnectionErr; goto exit; }
|
|
// }
|
|
// err = kNoErr;
|
|
// goto exit;
|
|
// }
|
|
|
|
while ( inHeader->extraDataLen < inHeader->contentLength )
|
|
{
|
|
selectResult = select( inSock + 1, &readSet, NULL, NULL, (struct timeval *)&t );
|
|
require_action( selectResult >= 1, exit, err = kNotReadableErr );
|
|
|
|
if(inHeader->isCallbackSupported == true){
|
|
/* We has extra data, and we give these data to application by onReceivedDataCallback function */
|
|
readLength = inHeader->contentLength - inHeader->extraDataLen > READ_LENGTH? READ_LENGTH:inHeader->contentLength - inHeader->extraDataLen;
|
|
readResult = ssl_recv( ssl,
|
|
(uint8_t*)( inHeader->extraDataPtr),
|
|
readLength );
|
|
|
|
if( readResult > 0 ) inHeader->extraDataLen += readResult;
|
|
else { err = kConnectionErr; goto exit; }
|
|
err = (inHeader->onReceivedDataCallback)(inHeader, inHeader->extraDataLen - readResult, (uint8_t *)inHeader->extraDataPtr, readResult, inHeader->userContext);
|
|
if( err != kNoErr ) goto exit;
|
|
}else{
|
|
/* We has extra data and we has a predefined buffer to store the total extra data return when all data has received*/
|
|
readResult = ssl_recv( ssl,
|
|
(uint8_t*)( inHeader->extraDataPtr + inHeader->extraDataLen ),
|
|
( inHeader->contentLength - inHeader->extraDataLen ) );
|
|
|
|
if( readResult > 0 ) inHeader->extraDataLen += readResult;
|
|
else { err = kConnectionErr; goto exit; }
|
|
}
|
|
}
|
|
err = kNoErr;
|
|
|
|
exit:
|
|
if(err != kNoErr) inHeader->len = 0;
|
|
return err;
|
|
}
|
|
|
|
|
|
int SocketReadHTTPSHeader( mico_ssl_t ssl, HTTPHeader_t *inHeader )
|
|
{
|
|
int err =0;
|
|
char * buf;
|
|
char * dst;
|
|
char * lim;
|
|
char * end;
|
|
size_t len;
|
|
ssize_t n;
|
|
|
|
buf = inHeader->buf;
|
|
dst = buf + inHeader->len;
|
|
lim = buf + inHeader->bufLen;
|
|
for( ;; )
|
|
{
|
|
if(findHeader( inHeader, &end ))
|
|
break ;
|
|
n = ssl_recv( ssl, dst, (size_t)( lim - dst ) );
|
|
if( n > 0 ) len = (size_t) n;
|
|
else { err = kConnectionErr; goto exit; }
|
|
dst += len;
|
|
inHeader->len += len;
|
|
}
|
|
|
|
inHeader->len = (size_t)( end - buf );
|
|
err = HTTPHeaderParse( inHeader );
|
|
require_noerr( err, exit );
|
|
inHeader->extraDataLen = (size_t)( dst - end );
|
|
if(inHeader->extraDataPtr) {
|
|
free((uint8_t *)inHeader->extraDataPtr);
|
|
inHeader->extraDataPtr = 0;
|
|
}
|
|
|
|
/* For chunked extra data without content length */
|
|
if(inHeader->chunkedData == true){
|
|
inHeader->chunkedDataBufferLen = (inHeader->extraDataLen > READ_LENGTH)? inHeader->extraDataLen:READ_LENGTH;
|
|
inHeader->chunkedDataBufferPtr = calloc(inHeader->chunkedDataBufferLen, sizeof(uint8_t)); //Make extra data buffer larger than chunk length
|
|
require_action(inHeader->chunkedDataBufferPtr, exit, err = kNoMemoryErr);
|
|
memcpy((uint8_t *)inHeader->chunkedDataBufferPtr, end, inHeader->extraDataLen);
|
|
inHeader->extraDataPtr = inHeader->chunkedDataBufferPtr;
|
|
return kNoErr;
|
|
}
|
|
|
|
/* Extra data with content length */
|
|
if (inHeader->contentLength != 0){ //Content length >0, create a memory buffer (Content length) and store extra data
|
|
size_t copyDataLen = (inHeader->contentLength >= inHeader->extraDataLen)? inHeader->extraDataLen : inHeader->contentLength;
|
|
if(inHeader->onReceivedDataCallback && (inHeader->onReceivedDataCallback)(inHeader, 0, (uint8_t *)end, copyDataLen, inHeader->userContext)==kNoErr){
|
|
inHeader->isCallbackSupported = true;
|
|
inHeader->extraDataPtr = calloc(READ_LENGTH, sizeof(uint8_t));
|
|
require_action(inHeader->extraDataPtr, exit, err = kNoMemoryErr);
|
|
}else{
|
|
inHeader->isCallbackSupported = false;
|
|
inHeader->extraDataPtr = calloc(inHeader->contentLength , sizeof(uint8_t));
|
|
require_action(inHeader->extraDataPtr, exit, err = kNoMemoryErr);
|
|
memcpy((uint8_t *)inHeader->extraDataPtr, end, copyDataLen);
|
|
}
|
|
err = kNoErr;
|
|
}/* Extra data without content length, data is ended by conntection close */
|
|
// else if(inHeader->extraDataLen != 0){ //Content length =0, but extra data length >0, create a memory buffer (1500)and store extra data
|
|
// inHeader->dataEndedbyClose = true;
|
|
// inHeader->extraDataPtr = calloc(1500, sizeof(uint8_t));
|
|
// require_action(inHeader->extraDataPtr, exit, err = kNoMemoryErr);
|
|
// memcpy((uint8_t *)inHeader->extraDataPtr, end, inHeader->extraDataLen);
|
|
// (inHeader->onReceivedDataCallback)(inHeader, 0, (uint8_t *)inHeader->extraDataPtr, inHeader->extraDataLen, inHeader->userContext);
|
|
|
|
// err = kNoErr;
|
|
// }
|
|
else
|
|
return kNoErr;
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
|
|
bool findHeader ( HTTPHeader_t *inHeader, char ** outHeaderEnd)
|
|
{
|
|
char *dst = inHeader->buf + inHeader->len;
|
|
char *buf = (char *)inHeader->buf;
|
|
char *src = (char *)inHeader->buf;
|
|
size_t len;
|
|
|
|
// Check for interleaved binary data (4 byte header that begins with $). See RFC 2326 section 10.12.
|
|
if( ( ( dst - buf ) >= 4 ) && ( buf[ 0 ] == '$' ) )
|
|
{
|
|
*outHeaderEnd = buf + 4;
|
|
return true;
|
|
}
|
|
|
|
// Find an empty line (separates the header and body). The HTTP spec defines it as CRLFCRLF, but some
|
|
// use LFLF or weird combos like CRLFLF so this handles CRLFCRLF, LFLF, and CRLFLF (but not CRCR).
|
|
*outHeaderEnd = dst;
|
|
for( ;; )
|
|
{
|
|
while( ( src < *outHeaderEnd ) && ( *src != '\n' ) ) ++src;
|
|
if( src >= *outHeaderEnd ) break;
|
|
|
|
len = (size_t)( *outHeaderEnd - src );
|
|
if( ( len >= 3 ) && ( src[ 1 ] == '\r' ) && ( src[ 2 ] == '\n' ) ) // CRLFCRLF or LFCRLF.
|
|
{
|
|
*outHeaderEnd = src + 3;
|
|
return true;
|
|
}
|
|
else if( ( len >= 2 ) && ( src[ 1 ] == '\n' ) ) // LFLF or CRLFLF.
|
|
{
|
|
*outHeaderEnd = src + 2;
|
|
return true;
|
|
}
|
|
else if( len <= 1 )
|
|
{
|
|
break;
|
|
}
|
|
++src;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//===========================================================================================================================
|
|
// HTTPHeader_Parse
|
|
//
|
|
// Parses an HTTP header. This assumes the "buf" and "len" fields are set. The other fields are set by this function.
|
|
//===========================================================================================================================
|
|
|
|
OSStatus HTTPHeaderParse( HTTPHeader_t *ioHeader )
|
|
{
|
|
OSStatus err;
|
|
const char * src;
|
|
const char * end;
|
|
const char * ptr;
|
|
char c;
|
|
const char * value;
|
|
size_t valueSize;
|
|
int x;
|
|
|
|
require_action( ioHeader->len < ioHeader->bufLen, exit, err = kParamErr );
|
|
|
|
// Reset fields up-front to good defaults to simplify handling of unused fields later.
|
|
|
|
ioHeader->methodPtr = "";
|
|
ioHeader->methodLen = 0;
|
|
ioHeader->urlPtr = "";
|
|
ioHeader->urlLen = 0;
|
|
memset( &ioHeader->url, 0, sizeof( ioHeader->url ) );
|
|
ioHeader->protocolPtr = "";
|
|
ioHeader->protocolLen = 0;
|
|
ioHeader->statusCode = -1;
|
|
ioHeader->reasonPhrasePtr = "";
|
|
ioHeader->reasonPhraseLen = 0;
|
|
ioHeader->channelID = 0;
|
|
ioHeader->contentLength = 0;
|
|
ioHeader->persistent = false;
|
|
|
|
// Check for a 4-byte interleaved binary data header (see RFC 2326 section 10.12). It has the following format:
|
|
//
|
|
// '$' <1:channelID> <2:dataSize in network byte order> ... followed by dataSize bytes of binary data.
|
|
src = ioHeader->buf;
|
|
if( ( ioHeader->len == 4 ) && ( src[ 0 ] == '$' ) )
|
|
{
|
|
const uint8_t * usrc;
|
|
|
|
usrc = (const uint8_t *) src;
|
|
ioHeader->channelID = usrc[ 1 ];
|
|
ioHeader->contentLength = ( usrc[ 2 ] << 8 ) | usrc[ 3 ];
|
|
|
|
ioHeader->methodPtr = src;
|
|
ioHeader->methodLen = 1;
|
|
|
|
err = kNoErr;
|
|
goto exit;
|
|
}
|
|
|
|
// Parse the start line. This will also determine if it's a request or response.
|
|
// Requests are in the format <method> <url> <protocol>/<majorVersion>.<minorVersion>, for example:
|
|
//
|
|
// GET /abc/xyz.html HTTP/1.1
|
|
// GET http://www.host.com/abc/xyz.html HTTP/1.1
|
|
// GET http://user:password@www.host.com/abc/xyz.html HTTP/1.1
|
|
//
|
|
// Responses are in the format <protocol>/<majorVersion>.<minorVersion> <statusCode> <reasonPhrase>, for example:
|
|
//
|
|
// HTTP/1.1 404 Not Found
|
|
ptr = src;
|
|
end = src + ioHeader->len;
|
|
for( c = 0; ( ptr < end ) && ( ( c = *ptr ) != ' ' ) && ( c != '/' ); ++ptr ) {}
|
|
require_action( ptr < end, exit, err = kMalformedErr );
|
|
|
|
if( c == ' ' ) // Requests have a space after the method. Responses have '/' after the protocol.
|
|
{
|
|
ioHeader->methodPtr = src;
|
|
ioHeader->methodLen = (size_t)( ptr - src );
|
|
++ptr;
|
|
|
|
// Parse the URL.
|
|
ioHeader->urlPtr = ptr;
|
|
while( ( ptr < end ) && ( *ptr != ' ' ) ) ++ptr;
|
|
ioHeader->urlLen = (size_t)( ptr - ioHeader->urlPtr );
|
|
require_action( ptr < end, exit, err = kMalformedErr );
|
|
++ptr;
|
|
|
|
err = URLParseComponents( ioHeader->urlPtr, ioHeader->urlPtr + ioHeader->urlLen, &ioHeader->url, NULL );
|
|
require_noerr( err, exit );
|
|
|
|
// Parse the protocol and version.
|
|
ioHeader->protocolPtr = ptr;
|
|
while( ( ptr < end ) && ( ( c = *ptr ) != '\r' ) && ( c != '\n' ) ) ++ptr;
|
|
ioHeader->protocolLen = (size_t)( ptr - ioHeader->protocolPtr );
|
|
require_action( ptr < end, exit, err = kMalformedErr );
|
|
++ptr;
|
|
}
|
|
else // Response
|
|
{
|
|
// Parse the protocol version.
|
|
ioHeader->protocolPtr = src;
|
|
for( ++ptr; ( ptr < end ) && ( *ptr != ' ' ); ++ptr ) {}
|
|
ioHeader->protocolLen = (size_t)( ptr - ioHeader->protocolPtr );
|
|
require_action( ptr < end, exit, err = kMalformedErr );
|
|
++ptr;
|
|
|
|
// Parse the status code.
|
|
x = 0;
|
|
for( c = 0; ( ptr < end ) && ( ( c = *ptr ) >= '0' ) && ( c <= '9' ); ++ptr ) x = ( x * 10 ) + ( c - '0' );
|
|
ioHeader->statusCode = x;
|
|
if( c == ' ' ) ++ptr;
|
|
|
|
// Parse the reason phrase.
|
|
ioHeader->reasonPhrasePtr = ptr;
|
|
while( ( ptr < end ) && ( ( c = *ptr ) != '\r' ) && ( c != '\n' ) ) ++ptr;
|
|
ioHeader->reasonPhraseLen = (size_t)( ptr - ioHeader->reasonPhrasePtr );
|
|
require_action( ptr < end, exit, err = kMalformedErr );
|
|
++ptr;
|
|
}
|
|
|
|
// There should at least be a blank line after the start line so make sure there's more data.
|
|
require_action( ptr < end, exit, err = kMalformedErr );
|
|
|
|
// Determine persistence. Note: HTTP 1.0 defaults to non-persistent if a Connection header field is not present.
|
|
err = HTTPGetHeaderField( ioHeader->buf, ioHeader->len, "Connection", NULL, NULL, &value, &valueSize, NULL );
|
|
if( err ) ioHeader->persistent = (Boolean)( strnicmpx( ioHeader->protocolPtr, ioHeader->protocolLen, "HTTP/1.0" ) != 0 );
|
|
else ioHeader->persistent = (Boolean)( strnicmpx( value, valueSize, "close" ) != 0 );
|
|
|
|
err = HTTPGetHeaderField( ioHeader->buf, ioHeader->len, "Transfer-Encoding", NULL, NULL, &value, &valueSize, NULL );
|
|
if( err ) ioHeader->chunkedData = false;
|
|
else ioHeader->chunkedData = (Boolean)( strnicmpx( value, valueSize, kTransferrEncodingType_CHUNKED ) == 0 );
|
|
|
|
// Content-Length is such a common field that we get it here during general parsing.
|
|
HTTPScanFHeaderValue( ioHeader->buf, ioHeader->len, "Content-Length", "%llu", &ioHeader->contentLength );
|
|
|
|
err = kNoErr;
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
int findCRLF( const char *inDataPtr , size_t inDataLen, char ** nextDataPtr ) //find CRLF
|
|
{
|
|
char *dst = (char *)inDataPtr + inDataLen;
|
|
char *src = (char *)inDataPtr;
|
|
size_t len;
|
|
|
|
// Find an empty line (separates the length and data).
|
|
for( ;; )
|
|
{
|
|
while( ( src < dst ) && ( *src != '\r' ) ) ++src;
|
|
if( src >= dst ) break;
|
|
|
|
len = (size_t)( dst - src );
|
|
|
|
if( ( len >= 2 ) && ( src[ 1 ] == '\n' ) ) // CRLF
|
|
{
|
|
*nextDataPtr = src + 2;
|
|
return true;
|
|
}
|
|
else if( len <= 1 )
|
|
{
|
|
break;
|
|
}
|
|
++src;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int findChunkedDataLength( const char *inChunkPtr , size_t inChunkLen, char ** chunkedDataPtr, const char *inFormat, ... )
|
|
{
|
|
char *dst = (char *)inChunkPtr + inChunkLen;
|
|
char *src = (char *)inChunkPtr;
|
|
size_t len;
|
|
va_list args;
|
|
|
|
// Find an empty line (separates the length and data).
|
|
*chunkedDataPtr = dst;
|
|
for( ;; )
|
|
{
|
|
while( ( src < *chunkedDataPtr ) && ( *src != '\r' ) ) ++src;
|
|
if( src >= *chunkedDataPtr ) break;
|
|
|
|
len = (size_t)( *chunkedDataPtr - src );
|
|
|
|
if( ( len >= 2 ) && ( src[ 1 ] == '\n' ) ) // CRLF
|
|
{
|
|
if(*inChunkPtr == 0x30){ //last chunk
|
|
*chunkedDataPtr = src + 2;
|
|
va_start( args, inFormat );
|
|
VSNScanF( "0", 1, "%llu", args);
|
|
va_end( args );
|
|
return true;
|
|
}
|
|
|
|
*chunkedDataPtr = src + 2;
|
|
va_start( args, inFormat );
|
|
VSNScanF( inChunkPtr, src - inChunkPtr, "%x", args);
|
|
va_end( args );
|
|
return true;
|
|
}
|
|
else if( len <= 1 )
|
|
{
|
|
break;
|
|
}
|
|
++src;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
OSStatus HTTPGetHeaderField( const char *inHeaderPtr,
|
|
size_t inHeaderLen,
|
|
const char *inName,
|
|
const char **outNamePtr,
|
|
size_t *outNameLen,
|
|
const char **outValuePtr,
|
|
size_t *outValueLen,
|
|
const char **outNext )
|
|
{
|
|
const char * src;
|
|
const char * end;
|
|
size_t matchLen;
|
|
char c;
|
|
|
|
if( inHeaderLen == kSizeCString ) inHeaderLen = strlen( inHeaderPtr );
|
|
src = inHeaderPtr;
|
|
end = src + inHeaderLen;
|
|
matchLen = inName ? strlen( inName ) : 0;
|
|
for( ;; )
|
|
{
|
|
const char * linePtr;
|
|
const char * lineEnd;
|
|
size_t lineLen;
|
|
const char * valuePtr;
|
|
const char * valueEnd;
|
|
|
|
// Parse a line and check if it begins with the header field we're looking for.
|
|
linePtr = src;
|
|
while( ( src < end ) && ( ( c = *src ) != '\r' ) && ( c != '\n' ) ) ++src;
|
|
if( src >= end ) break;
|
|
lineEnd = src;
|
|
lineLen = (size_t)( src - linePtr );
|
|
if( ( src < end ) && ( *src == '\r' ) ) ++src;
|
|
if( ( src < end ) && ( *src == '\n' ) ) ++src;
|
|
|
|
if( !inName ) // Null name means to find the next header for iteration.
|
|
{
|
|
const char * nameEnd;
|
|
|
|
nameEnd = linePtr;
|
|
while( ( nameEnd < lineEnd ) && ( *nameEnd != ':' ) ) ++nameEnd;
|
|
if( nameEnd >= lineEnd ) continue;
|
|
matchLen = (size_t)( nameEnd - linePtr );
|
|
}
|
|
else if( ( lineLen <= matchLen ) || ( linePtr[ matchLen ] != ':' ) ||
|
|
( strnicmp( linePtr, inName, matchLen ) != 0 ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Found the header field. Separate name and value and skip leading whitespace in the value.
|
|
valuePtr = linePtr + matchLen + 1;
|
|
valueEnd = lineEnd;
|
|
while( ( valuePtr < valueEnd ) && ( ( ( c = *valuePtr ) == ' ' ) || ( c == '\t' ) ) ) ++valuePtr;
|
|
|
|
// If the next line is a continuation line then keep parsing until we get to the true end.
|
|
while( ( src < end ) && ( ( ( c = *src ) == ' ' ) || ( c == '\t' ) ) )
|
|
{
|
|
++src;
|
|
while( ( src < end ) && ( ( c = *src ) != '\r' ) && ( c != '\n' ) ) ++src;
|
|
valueEnd = src;
|
|
if( ( src < end ) && ( *src == '\r' ) ) ++src;
|
|
if( ( src < end ) && ( *src == '\n' ) ) ++src;
|
|
}
|
|
|
|
if( outNamePtr ) *outNamePtr = linePtr;
|
|
if( outNameLen ) *outNameLen = matchLen;
|
|
if( outValuePtr ) *outValuePtr = valuePtr;
|
|
if( outValueLen ) *outValueLen = (size_t)( valueEnd - valuePtr );
|
|
if( outNext ) *outNext = src;
|
|
return( kNoErr );
|
|
}
|
|
return kNotFoundErr;
|
|
}
|
|
|
|
int HTTPScanFHeaderValue( const char *inHeaderPtr, size_t inHeaderLen, const char *inName, const char *inFormat, ... )
|
|
{
|
|
int n;
|
|
const char * valuePtr;
|
|
size_t valueLen;
|
|
va_list args;
|
|
|
|
n = (int) HTTPGetHeaderField( inHeaderPtr, inHeaderLen, inName, NULL, NULL, &valuePtr, &valueLen, NULL );
|
|
require_noerr_quiet( n, exit );
|
|
|
|
va_start( args, inFormat );
|
|
n = VSNScanF( valuePtr, valueLen, inFormat, args );
|
|
va_end( args );
|
|
|
|
exit:
|
|
return( n );
|
|
}
|
|
|
|
OSStatus HTTPHeaderMatchMethod( HTTPHeader_t *inHeader, const char *method )
|
|
{
|
|
if( strnicmpx( inHeader->methodPtr, inHeader->methodLen, method ) == 0 )
|
|
return kNoErr;
|
|
|
|
return kNotFoundErr;
|
|
}
|
|
|
|
OSStatus HTTPHeaderMatchURL( HTTPHeader_t *inHeader, const char *url )
|
|
{
|
|
if( strnicmp_suffix( inHeader->url.pathPtr, inHeader->url.pathLen, url ) == 0 )
|
|
return kNoErr;
|
|
|
|
return kNotFoundErr;
|
|
}
|
|
|
|
char* HTTPHeaderMatchPartialURL( HTTPHeader_t *inHeader, const char *url )
|
|
{
|
|
return strnstr_suffix( inHeader->url.pathPtr, inHeader->url.pathLen, url);
|
|
}
|
|
|
|
HTTPHeader_t * HTTPHeaderCreate( size_t bufLen )
|
|
{
|
|
return HTTPHeaderCreateWithCallback( bufLen, NULL, NULL, NULL );
|
|
}
|
|
|
|
HTTPHeader_t * HTTPHeaderCreateWithCallback( size_t bufLen, onReceivedDataCallback inRecvFunc, onClearCallback onClearFunc, void * context )
|
|
{
|
|
OSStatus err = kNoErr;
|
|
HTTPHeader_t *httpHeader = NULL;
|
|
|
|
httpHeader = calloc(1, sizeof(HTTPHeader_t));
|
|
require_action(httpHeader, exit, err = kNoMemoryErr);
|
|
|
|
httpHeader->buf = calloc(bufLen, sizeof(char));
|
|
require_action(httpHeader->buf, exit, err = kNoMemoryErr);
|
|
|
|
httpHeader->bufLen = bufLen;
|
|
httpHeader->userContext = context;
|
|
httpHeader->onReceivedDataCallback = inRecvFunc;
|
|
httpHeader->onClearCallback = onClearFunc;
|
|
|
|
exit:
|
|
if(err != kNoErr && httpHeader != NULL){ //httpHeader->buf cannot be created
|
|
free(httpHeader);
|
|
}
|
|
return httpHeader;
|
|
}
|
|
|
|
void HTTPHeaderClear( HTTPHeader_t *inHeader )
|
|
{
|
|
char *nextPackagePtr;
|
|
size_t chunckheaderLen = inHeader->extraDataPtr - inHeader->chunkedDataBufferPtr;
|
|
|
|
if(inHeader->onClearCallback)
|
|
(inHeader->onClearCallback)(inHeader, inHeader->userContext);
|
|
|
|
if(inHeader->chunkedData && (uint32_t *)inHeader->chunkedDataBufferPtr){ //chunk data
|
|
/* Possible to read the header of the next http package */
|
|
if(findCRLF( inHeader->extraDataPtr, inHeader->extraDataLen - chunckheaderLen, &nextPackagePtr ) ){
|
|
if( nextPackagePtr <= inHeader->chunkedDataBufferPtr + inHeader->extraDataLen ){ //We get some data belongs to next http package
|
|
inHeader->len = inHeader->extraDataLen - (nextPackagePtr - inHeader->chunkedDataBufferPtr);
|
|
if(inHeader->len > 512)
|
|
inHeader->len = 0;
|
|
else
|
|
memcpy(inHeader->buf, nextPackagePtr, inHeader->len);
|
|
} else
|
|
inHeader->len = 0;
|
|
}
|
|
|
|
inHeader->extraDataLen = 0;
|
|
free((uint32_t *)inHeader->chunkedDataBufferPtr);
|
|
inHeader->chunkedDataBufferPtr = NULL;
|
|
inHeader->extraDataPtr = NULL;
|
|
inHeader->chunkedData = false;
|
|
}else{
|
|
|
|
/* We get some data belongs to next http package, this only could happen two or more
|
|
packages are received by SocketReadHTTPHeader */
|
|
if( inHeader->extraDataLen > inHeader->contentLength ){
|
|
inHeader->len = inHeader->extraDataLen - inHeader->contentLength;
|
|
memcpy(inHeader->buf, inHeader->extraDataPtr + inHeader->contentLength, inHeader->len);
|
|
} else
|
|
inHeader->len = 0;
|
|
|
|
inHeader->extraDataLen = 0;
|
|
if((uint32_t *)inHeader->extraDataPtr) {
|
|
free((uint32_t *)inHeader->extraDataPtr);
|
|
inHeader->extraDataPtr = NULL;
|
|
}
|
|
inHeader->dataEndedbyClose = false;
|
|
}
|
|
|
|
inHeader->isCallbackSupported = false;
|
|
}
|
|
|
|
void HTTPHeaderDestory( HTTPHeader_t **inHeader )
|
|
{
|
|
if( *inHeader != NULL ){
|
|
HTTPHeaderClear( *inHeader );
|
|
free( (*inHeader)->buf );
|
|
free( *inHeader );
|
|
*inHeader = NULL;
|
|
}
|
|
}
|
|
|
|
OSStatus CreateSimpleHTTPOKMessage( uint8_t **outMessage, size_t *outMessageSize )
|
|
{
|
|
OSStatus err = kNoMemoryErr;
|
|
*outMessage = malloc( 200 );
|
|
require( *outMessage, exit );
|
|
|
|
sprintf( (char*)*outMessage,
|
|
"%s %s %s%s",
|
|
"HTTP/1.1", "200", "OK", kCRLFLineEnding );
|
|
*outMessageSize = strlen( (char*)*outMessage );
|
|
|
|
err = kNoErr;
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
|
|
|
|
OSStatus CreateSimpleHTTPMessage( const char *contentType, uint8_t *inData, size_t inDataLen, uint8_t **outMessage, size_t *outMessageSize )
|
|
{
|
|
uint8_t *endOfHTTPHeader;
|
|
OSStatus err = kParamErr;
|
|
|
|
|
|
require( contentType, exit );
|
|
require( inData, exit );
|
|
require( inDataLen, exit );
|
|
|
|
|
|
err = kNoMemoryErr;
|
|
*outMessage = malloc( inDataLen + 200 );
|
|
require( *outMessage, exit );
|
|
|
|
// Create HTTP Response
|
|
snprintf( (char*)*outMessage, 200,
|
|
"%s %d %s%s%s %s%s%s %d%s",
|
|
"HTTP/1.1", 200, "OK", kCRLFNewLine,
|
|
"Content-Type:", contentType, kCRLFNewLine,
|
|
"Content-Length:", (int)inDataLen, kCRLFLineEnding );
|
|
|
|
// outMessageSize will be the length of the HTTP Header plus the data length
|
|
*outMessageSize = strlen( (char*)*outMessage ) + inDataLen;
|
|
|
|
endOfHTTPHeader = *outMessage + strlen( (char*)*outMessage );
|
|
memcpy( endOfHTTPHeader, inData, inDataLen );
|
|
err = kNoErr;
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
OSStatus CreateSimpleHTTPMessageNoCopy( const char *contentType, size_t inDataLen, uint8_t **outMessage, size_t *outMessageSize )
|
|
{
|
|
OSStatus err = kParamErr;
|
|
|
|
require( contentType, exit );
|
|
require( inDataLen, exit );
|
|
|
|
err = kNoMemoryErr;
|
|
*outMessage = malloc( 200 );
|
|
require( *outMessage, exit );
|
|
|
|
// Create HTTP Response
|
|
snprintf( (char*)*outMessage, 200,
|
|
"%s %s %s%s%s %s%s%s %d%s",
|
|
"HTTP/1.1", "200", "OK", kCRLFNewLine,
|
|
"Content-Type:", contentType, kCRLFNewLine,
|
|
"Content-Length:", (int)inDataLen, kCRLFLineEnding );
|
|
|
|
// outMessageSize will be the length of the HTTP Header plus the data length
|
|
*outMessageSize = strlen( (char*)*outMessage );
|
|
err = kNoErr;
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
char * getStatusString(int status)
|
|
{
|
|
if(status == kStatusOK)
|
|
return "OK";
|
|
else if(status == kStatusNoConetnt)
|
|
return "No Content";
|
|
else if(status == kStatusPartialContent)
|
|
return "Multi0Status";
|
|
else if(status == kStatusBadRequest)
|
|
return "Bad Request";
|
|
else if(status == kStatusNotFound)
|
|
return "Not Found";
|
|
else if(status == kStatusMethodNotAllowed)
|
|
return "Not Allowed";
|
|
else if(status == kStatusForbidden)
|
|
return "Forbidden";
|
|
else if(status == kStatusAuthenticationErr)
|
|
return "Authentication Error";
|
|
else if(status == kStatusInternalServerErr)
|
|
return "Internal Server Error";
|
|
else
|
|
return "OK";
|
|
}
|
|
|
|
OSStatus CreateHTTPRespondMessageNoCopy( int status, const char *contentType, size_t inDataLen, uint8_t **outMessage, size_t *outMessageSize )
|
|
{
|
|
OSStatus err = kParamErr;
|
|
char *statusString = getStatusString(status);
|
|
|
|
err = kNoMemoryErr;
|
|
*outMessage = malloc( 200 );
|
|
require( *outMessage, exit );
|
|
|
|
// Create HTTP Response
|
|
if(inDataLen)
|
|
snprintf( (char*)*outMessage, 200,
|
|
"%s %d %s%s%s %s%s%s %d%s",
|
|
"HTTP/1.1", status, statusString, kCRLFNewLine,
|
|
"Content-Type:", contentType, kCRLFNewLine,
|
|
"Content-Length:", (int)inDataLen, kCRLFLineEnding );
|
|
else
|
|
snprintf( (char*)*outMessage, 200,
|
|
"%s %d %s%s",
|
|
"HTTP/1.1", status, statusString, kCRLFLineEnding);
|
|
|
|
// outMessageSize will be the length of the HTTP Header plus the data length
|
|
*outMessageSize = strlen( (char*)*outMessage );
|
|
err = kNoErr;
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
|
|
OSStatus CreateHTTPMessage( const char *methold, const char *url, const char *contentType, uint8_t *inData, size_t inDataLen, uint8_t **outMessage, size_t *outMessageSize )
|
|
{
|
|
uint8_t *endOfHTTPHeader;
|
|
OSStatus err = kParamErr;
|
|
|
|
err = kNoMemoryErr;
|
|
*outMessage = malloc( inDataLen + 500 );
|
|
require( *outMessage, exit );
|
|
|
|
// Create HTTP Response
|
|
if(inDataLen)
|
|
sprintf( (char*)*outMessage,
|
|
"%s %s\? %s%s%s %s%s%s %d%s",
|
|
methold, url, "HTTP/1.1", kCRLFNewLine,
|
|
"Content-Type:", contentType, kCRLFNewLine,
|
|
"Content-Length:", (int)inDataLen, kCRLFLineEnding );
|
|
else
|
|
sprintf( (char*)*outMessage,
|
|
"%s %s\? %s %s",
|
|
methold, url, "HTTP/1.1", kCRLFLineEnding );
|
|
|
|
// outMessageSize will be the length of the HTTP Header plus the data length
|
|
*outMessageSize = strlen( (char*)*outMessage ) + inDataLen;
|
|
|
|
endOfHTTPHeader = *outMessage + strlen( (char*)*outMessage );
|
|
memcpy( endOfHTTPHeader, inData, inDataLen );
|
|
err = kNoErr;
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
OSStatus CreateHTTPMessageWithHost( const char *methold, const char *url,
|
|
const char* host, uint16_t port,
|
|
const char *contentType,
|
|
uint8_t *inData, size_t inDataLen,
|
|
uint8_t **outMessage, size_t *outMessageSize )
|
|
{
|
|
uint8_t *endOfHTTPHeader;
|
|
OSStatus err = kParamErr;
|
|
|
|
err = kNoMemoryErr;
|
|
*outMessage = malloc( inDataLen + 500 );
|
|
require( *outMessage, exit );
|
|
|
|
// Create HTTP Response
|
|
if(inDataLen)
|
|
sprintf( (char*)*outMessage,
|
|
"%s %s %s%s%s %s:%d%s%s %s%s%s %d%s",
|
|
methold, url, "HTTP/1.1", kCRLFNewLine,
|
|
"Host:", host, port, kCRLFNewLine,
|
|
"Content-Type:", contentType, kCRLFNewLine,
|
|
"Content-Length:", (int)inDataLen, kCRLFLineEnding );
|
|
else
|
|
sprintf( (char*)*outMessage,
|
|
"%s %s %s%s%s %s:%d%s",
|
|
methold, url, "HTTP/1.1", kCRLFNewLine,
|
|
"Host:", host, port, kCRLFLineEnding);
|
|
|
|
// outMessageSize will be the length of the HTTP Header plus the data length
|
|
*outMessageSize = strlen( (char*)*outMessage ) + inDataLen;
|
|
|
|
endOfHTTPHeader = *outMessage + strlen( (char*)*outMessage );
|
|
memcpy( endOfHTTPHeader, inData, inDataLen );
|
|
err = kNoErr;
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
void PrintHTTPHeader( HTTPHeader_t *inHeader )
|
|
{
|
|
char temp[20];
|
|
//(void)inHeader; // Fix warning when debug=0
|
|
//http_utils_log("Header:\n %s", inHeader->buf);
|
|
//http_utils_log("Length: %d", (int)inHeader->len);
|
|
strncpy(temp, inHeader->methodPtr, 8);
|
|
http_utils_log("Method: %s", temp);
|
|
strncpy(temp, inHeader->urlPtr, 20);
|
|
http_utils_log("URL: %s", temp);
|
|
strncpy(temp, inHeader->protocolPtr, 8);
|
|
http_utils_log("Protocol: %s", temp);
|
|
http_utils_log("Status Code: %d", inHeader->statusCode);
|
|
http_utils_log("ChannelID: %d", inHeader->channelID);
|
|
http_utils_log("Content length: %ld", (uint32_t)(inHeader->contentLength));
|
|
http_utils_log("Persistent: %s", YesOrNo( inHeader->persistent ));
|
|
}
|
|
|