version 1.2, 2015/04/20 18:10:13
|
version 1.3, 2015/04/20 18:19:52
|
|
|
| |
#include <assert.h> | #include <assert.h> |
#include <ctype.h> | #include <ctype.h> |
//#include "http.h" |
#include "httpcommon.h" |
#include "httpclient.h" | #include "httpclient.h" |
#include <sock/addr.h> | #include <sock/addr.h> |
#include <sock/sock.h> | #include <sock/sock.h> |
#include <sock/selector.h> | #include <sock/selector.h> |
#include <base/time.h> |
#include <pal/sleep.h> |
#include <base/buf.h> | #include <base/buf.h> |
#include <base/log.h> | #include <base/log.h> |
#include <base/result.h> | #include <base/result.h> |
#include <base/strings.h> |
#include <pal/strings.h> |
#include <base/io.h> |
#include <pal/format.h> |
|
#include <pal/sleep.h> |
#include <base/paths.h> | #include <base/paths.h> |
| |
|
// #define ENABLE_TRACING 1 |
|
#ifdef ENABLE_TRACING |
|
# define TRACING_LEVEL 4 |
|
# include <deprecated/logging/logging.h> |
|
#else |
|
# define LOGE2(a) |
|
# define LOGW2(a) |
|
# define LOGD2(a) |
|
# define LOGX2(a) |
|
#endif |
|
|
#ifdef CONFIG_POSIX | #ifdef CONFIG_POSIX |
#include <openssl/ssl.h> | #include <openssl/ssl.h> |
#include <openssl/err.h> | #include <openssl/err.h> |
|
|
| |
#endif | #endif |
| |
#define T MI_T |
|
|
|
/* #define ENABLE_TRACING // */ |
|
#ifdef ENABLE_TRACING |
|
#define PRINTF(a) TIMESTAMP(); printf a |
|
#define TIMESTAMP() \ |
|
{\ |
|
MI_Uint64 currentTimeUsec = 0;\ |
|
\ |
|
Time_Now(¤tTimeUsec);\ |
|
currentTimeUsec /= 1000; /* ms */ \ |
|
printf("%ds%03dms ", (int)(currentTimeUsec / 1000 % 1000), (int)(currentTimeUsec % 1000));\ |
|
} |
|
|
|
#define PRINTF_2(a) |
|
#else |
|
#define PRINTF(a) |
|
#define PRINTF_2(a) |
|
#endif |
|
|
|
|
|
|
|
/* | /* |
**============================================================================== | **============================================================================== |
** | ** |
|
|
} | } |
Http_RecvState; | Http_RecvState; |
| |
typedef struct _Http_SR_SocketData |
typedef struct _HttpClient_SR_SocketData |
{ | { |
/* based member*/ | /* based member*/ |
Handler base; | Handler base; |
|
|
MI_Boolean connectDone; | MI_Boolean connectDone; |
| |
/* receiving data */ | /* receiving data */ |
char* recvBuffer; |
__field_ecount(recvBufferSize) char* recvBuffer; |
size_t recvBufferSize; | size_t recvBufferSize; |
size_t recvievedSize; |
size_t receivedSize; |
Http_RecvState recvingState; | Http_RecvState recvingState; |
HttpClientHeaderField recvHeaderFields[64]; | HttpClientHeaderField recvHeaderFields[64]; |
HttpClientResponseHeader recvHeaders; | HttpClientResponseHeader recvHeaders; |
MI_Sint64 contentLength; | MI_Sint64 contentLength; |
|
MI_Sint64 contentBegin; |
|
MI_Sint64 contentEnd; |
|
MI_Sint64 contentTotalLength; |
Page* recvPage; | Page* recvPage; |
| |
|
/* flag for a response from a HEAD request */ |
|
MI_Boolean headVerb; |
|
|
/* sending part */ | /* sending part */ |
Page* sendPage; | Page* sendPage; |
Page* sendHeader; | Page* sendHeader; |
|
|
MI_Result status; | MI_Result status; |
| |
} | } |
Http_SR_SocketData; |
HttpClient_SR_SocketData; |
| |
struct _HttpClient | struct _HttpClient |
{ | { |
|
|
void* callbackData; | void* callbackData; |
SSL_CTX* sslContext; | SSL_CTX* sslContext; |
| |
Http_SR_SocketData* connector; |
HttpClient_SR_SocketData* connector; |
| |
MI_Boolean internalSelectorUsed; | MI_Boolean internalSelectorUsed; |
}; | }; |
|
|
/* helper functions result */ | /* helper functions result */ |
typedef enum _Http_CallbackResult | typedef enum _Http_CallbackResult |
{ | { |
PRT_CONTINUE, |
PRT_RETURN_FALSE, |
PRT_RETURN_TRUE, | PRT_RETURN_TRUE, |
PRT_RETURN_FALSE |
PRT_CONTINUE |
} | } |
Http_CallbackResult; | Http_CallbackResult; |
| |
|
|
| |
#define _HashCode(first,last,len) ( (((MI_Uint8)first) << 16) | (((MI_Uint8)last) << 8) | (((MI_Uint16)len)) ) | #define _HashCode(first,last,len) ( (((MI_Uint8)first) << 16) | (((MI_Uint8)last) << 8) | (((MI_Uint16)len)) ) |
| |
|
_Return_type_success_(return == MI_TRUE) |
static MI_Boolean _getNameValuePair( | static MI_Boolean _getNameValuePair( |
char ** line, |
_Inout_ CharPtr* line, |
char ** value, |
_Out_ CharPtr* value, |
int* nameHashCode ) |
_Out_ int* nameHashCode) |
{ | { |
int len = 0; | int len = 0; |
char* p; | char* p; |
/* find name end /hash-code */ | /* find name end /hash-code */ |
| |
|
if ((*line)[0] == '\0') |
|
{ |
|
return MI_FALSE; |
|
} |
|
|
*nameHashCode = _ToLower((MI_Uint8)(*line)[0])<<16; | *nameHashCode = _ToLower((MI_Uint8)(*line)[0])<<16; |
| |
for (len = 1; (*line)[len] != ':' && (*line)[len] != '\r'; len++ ) |
for (len = 1; (*line)[len] != '\0' && (*line)[len] != ':' && (*line)[len] != '\r'; len++) |
; | ; |
| |
if ((*line)[len] != ':') | if ((*line)[len] != ':') |
|
|
*value = p; | *value = p; |
| |
/* skip to end of line */ | /* skip to end of line */ |
for ( ; ; ) |
for ( ; p[0]; ) |
{ | { |
if (p[0] == '\r' && p[1] == '\n' && |
if (p[0] != '\0' && p[1] != '\0' && p[2] != '\0' && |
(p[2] != ' ' && p[2] != '\t') ) |
p[0] == '\r' && p[1] == '\n' && |
|
p[2] != ' ' && p[2] != '\t') |
{ | { |
p[0] = 0; |
p[0] = '\0'; |
(*line) = p + 2; | (*line) = p + 2; |
break; | break; |
} | } |
|
|
| |
/* remove trailing spaces */ | /* remove trailing spaces */ |
p--; | p--; |
|
#ifdef _PREFAST_ |
|
#pragma prefast (push) |
|
#pragma prefast (disable: 26001) |
|
#endif |
|
/* disabling IPv6 OACR warnings - D3M bug 56 */ |
while (p[0] == ' ' || p[0] == '\t') | while (p[0] == ' ' || p[0] == '\t') |
p--; | p--; |
|
#ifdef _PREFAST_ |
|
#pragma prefast (pop) |
|
#endif |
| |
p[1] = 0; |
p[1] = '\0'; |
| |
return MI_TRUE; | return MI_TRUE; |
} | } |
| |
|
|
static MI_Boolean _getHeaderField( | static MI_Boolean _getHeaderField( |
Http_SR_SocketData* handler, |
HttpClient_SR_SocketData* handler, |
char ** line) |
_Inout_ CharPtr* line) |
{ | { |
/* expecting Request-Line = Method SP Request-URI SP HTTP-Version CRLF | /* expecting Request-Line = Method SP Request-URI SP HTTP-Version CRLF |
Read more: http://www.faqs.org/rfcs/rfc2616.html#ixzz0jKdjJdZv | Read more: http://www.faqs.org/rfcs/rfc2616.html#ixzz0jKdjJdZv |
|
|
if (!_getNameValuePair(line, &value, &nameHashCode)) | if (!_getNameValuePair(line, &value, &nameHashCode)) |
return MI_FALSE; | return MI_FALSE; |
| |
|
|
if (nameHashCode == _HashCode('c','h',14) && /*Content-Length*/ | if (nameHashCode == _HashCode('c','h',14) && /*Content-Length*/ |
Strcasecmp(name,"Content-Length") == 0) | Strcasecmp(name,"Content-Length") == 0) |
{ | { |
handler->contentLength = Strtoull(value, NULL, 10); | handler->contentLength = Strtoull(value, NULL, 10); |
/*if ( handler->contentLength > HTTP_MAX_CONTENT ) | /*if ( handler->contentLength > HTTP_MAX_CONTENT ) |
|
handler->contentBegin = -1; |
|
handler->contentEnd = -1; |
|
handler->contentTotalLength = -1; |
return MI_FALSE;*/ | return MI_FALSE;*/ |
} | } |
else if (nameHashCode == _HashCode('t','g',17) && /*Transfer-Encoding*/ | else if (nameHashCode == _HashCode('t','g',17) && /*Transfer-Encoding*/ |
Strcasecmp(name,"Transfer-Encoding") == 0) | Strcasecmp(name,"Transfer-Encoding") == 0) |
{ | { |
handler->contentLength = -1; | handler->contentLength = -1; |
|
handler->contentBegin = -1; |
|
handler->contentEnd = -1; |
|
handler->contentTotalLength = -1; |
|
} |
|
else if (nameHashCode == _HashCode('c','e',13) && /*Content-Range*/ |
|
Strcasecmp(name, "Content-Range") == 0) |
|
{ |
|
char* delimptr; |
|
char* endptr; |
|
handler->contentEnd = -1; |
|
handler->contentBegin = (MI_Sint64)Strtoull(value, NULL, 10); |
|
delimptr = strchr(value, '-'); |
|
endptr = strchr(value, '\n'); |
|
if (delimptr != NULL && (endptr == NULL || endptr > delimptr)) |
|
handler->contentEnd = Strtoull(++delimptr, NULL, 10); |
|
delimptr = strchr(value, '/'); |
|
if (delimptr != NULL && (endptr == NULL || endptr > delimptr)) |
|
handler->contentTotalLength = Strtoull(++delimptr, NULL, 10); |
} | } |
|
|
else |
|
{ |
|
if (handler->recvHeaders.sizeHeaders < MI_COUNT(handler->recvHeaderFields)) | if (handler->recvHeaders.sizeHeaders < MI_COUNT(handler->recvHeaderFields)) |
{ | { |
handler->recvHeaderFields[handler->recvHeaders.sizeHeaders].name = name; | handler->recvHeaderFields[handler->recvHeaders.sizeHeaders].name = name; |
|
|
} | } |
else | else |
{ | { |
LOGW_CHAR(("too many http headers; skipping %s: %s\n", name, value)); |
trace_TooManyHttpHeaders(scs(name), scs(value)); |
} |
|
} | } |
| |
return MI_TRUE; | return MI_TRUE; |
|
|
| |
| |
static MI_Boolean _getRequestLine( | static MI_Boolean _getRequestLine( |
Http_SR_SocketData* handler, |
HttpClient_SR_SocketData* handler, |
char ** line) |
_Inout_ CharPtr* line) |
{ | { |
size_t index; | size_t index; |
/* expecting Request-Line = Method SP Request-URI SP HTTP-Version CRLF | /* expecting Request-Line = Method SP Request-URI SP HTTP-Version CRLF |
|
|
| |
Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF | Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF |
*/ | */ |
const char* s = *line + 9; /*+ HTTP/1.1<sp>*/ |
const char* s = *line; /*+ HTTP/1.1<sp>*/ |
|
size_t skips = 9; |
|
for (; skips > 0; s++) |
|
{ |
|
skips--; |
|
if (!*s) |
|
{ |
|
return MI_FALSE; |
|
} |
|
} |
| |
handler->recvHeaders.httpError = Strtoul(s, NULL, 10); | handler->recvHeaders.httpError = Strtoul(s, NULL, 10); |
} | } |
| |
/* skip to end of line */ | /* skip to end of line */ |
for ( index = 1; index < handler->recvievedSize; index++ ) |
for (index = 1; (*line)[index] && index < handler->receivedSize; index++) |
{ | { |
if ((*line)[index-1] == '\r' && (*line)[index] == '\n' ) | if ((*line)[index-1] == '\r' && (*line)[index] == '\n' ) |
{ | { |
|
|
} | } |
| |
static MI_Result _Sock_Read( | static MI_Result _Sock_Read( |
Http_SR_SocketData* handler, |
HttpClient_SR_SocketData* handler, |
void* buf, | void* buf, |
size_t buf_size, | size_t buf_size, |
size_t* sizeRead) | size_t* sizeRead) |
{ | { |
int res; | int res; |
|
int sslError; |
| |
if (!handler->ssl) |
if (handler->ssl == NULL) |
return Sock_Read(handler->base.sock, buf, buf_size, sizeRead); |
{ |
|
MI_Result res = Sock_Read(handler->base.sock, buf, buf_size, sizeRead); |
|
|
|
LOGD2((ZT("_Sock_Read - After regular read. socket: %d, result: %d (%s), bytes read: %u / %u"), handler->base.sock, (int)res, mistrerror(res), (unsigned int)*sizeRead, (unsigned int)buf_size)); |
|
return res; |
|
} |
| |
handler->base.mask &= ~SELECTOR_WRITE; | handler->base.mask &= ~SELECTOR_WRITE; |
handler->base.mask |= SELECTOR_READ; | handler->base.mask |= SELECTOR_READ; |
|
|
*sizeRead = 0; | *sizeRead = 0; |
| |
res = SSL_read(handler->ssl, buf, buf_size); | res = SSL_read(handler->ssl, buf, buf_size); |
PRINTF(("ssl read %d\n", res)); |
LOGD2((ZT("_Sock_Read - SSL_Read returned: %d (< 0 for error) / %u bytes read, errno: %d (%s)"), res, (unsigned int)buf_size, errno, strerror(errno))); |
|
|
if ( res == 0 ) | if ( res == 0 ) |
|
{ |
|
LOGW2((ZT("_Sock_Read - SSL socket connection closed. socket: %d"), handler->base.sock)); |
return MI_RESULT_OK; /* connection closed */ | return MI_RESULT_OK; /* connection closed */ |
|
} |
| |
if ( res > 0 ) | if ( res > 0 ) |
{ | { |
|
LOGD2((ZT("_Sock_read - Bytes read: %d"), res)); |
*sizeRead = res; | *sizeRead = res; |
return MI_RESULT_OK; /* ok */ | return MI_RESULT_OK; /* ok */ |
} | } |
| |
switch (SSL_get_error(handler->ssl, res)) |
sslError = SSL_get_error(handler->ssl, res); |
|
switch (sslError) |
{ | { |
case SSL_ERROR_WANT_WRITE: | case SSL_ERROR_WANT_WRITE: |
handler->reverseOperations = MI_TRUE; /* wait until write is allowed */ | handler->reverseOperations = MI_TRUE; /* wait until write is allowed */ |
handler->base.mask &= ~SELECTOR_READ; | handler->base.mask &= ~SELECTOR_READ; |
handler->base.mask |= SELECTOR_WRITE; | handler->base.mask |= SELECTOR_WRITE; |
PRINTF(("ssl read/accept WANT_WRITE\n")); |
LOGD2((ZT("_Sock_Read - SSL_read/accept returned WANT_WRITE"))); |
return MI_RESULT_WOULD_BLOCK; | return MI_RESULT_WOULD_BLOCK; |
| |
case SSL_ERROR_WANT_READ: | case SSL_ERROR_WANT_READ: |
PRINTF(("ssl read/accept WANT_READ\n")); |
LOGD2((ZT("Sock_Read - SSL_read/accept returned WANT_READ"))); |
return MI_RESULT_WOULD_BLOCK; | return MI_RESULT_WOULD_BLOCK; |
| |
case SSL_ERROR_SYSCALL: | case SSL_ERROR_SYSCALL: |
|
|
EINPROGRESS == errno) | EINPROGRESS == errno) |
return MI_RESULT_WOULD_BLOCK; | return MI_RESULT_WOULD_BLOCK; |
| |
LOGW_CHAR(("ssl-read: unexpected sys error %d\n", errno)); |
LOGE2((ZT("Sock_Read - SSL_read returned OS error %d (%s)"), errno, strerror(errno))); |
|
trace_SSLRead_UnexpectedSysError(errno); |
break; | break; |
| |
default: | default: |
|
/* print Open SSL error stack */ |
{ | { |
/* print error */ |
unsigned long err; |
unsigned long err = ERR_get_error(); |
while ((err = ERR_get_error()) != 0) |
while (err) |
|
{ | { |
char err_txt[200]; | char err_txt[200]; |
ERR_error_string_n(err, err_txt, sizeof(err_txt)); |
|
|
|
LOGW_CHAR(("ssl-read error: %d [%s]\n", (int)err, err_txt)); |
|
| |
err = ERR_get_error(); |
ERR_error_string_n(err, err_txt, sizeof (err_txt)); |
|
LOGE2((ZT("_Sock_Read - SSL_read returned OpenSSL error: %lu (%s)"), err, err_txt)); |
} | } |
} | } |
break; | break; |
|
|
} | } |
| |
static MI_Result _Sock_Write( | static MI_Result _Sock_Write( |
Http_SR_SocketData* handler, |
HttpClient_SR_SocketData* handler, |
void* buf, | void* buf, |
size_t buf_size, | size_t buf_size, |
size_t* sizeWritten) | size_t* sizeWritten) |
{ | { |
int res; | int res; |
|
int sslError; |
| |
if (!handler->ssl) | if (!handler->ssl) |
return Sock_Write(handler->base.sock, buf, buf_size, sizeWritten); |
{ |
|
MI_Result res = Sock_Write(handler->base.sock, buf, buf_size, sizeWritten); |
|
LOGD2((ZT("_Sock_Write - Non-SSS write. Sock_Write returned %d (%s). %u / %u bytes sent"), res, mistrerror(res), (unsigned int)*sizeWritten, (unsigned int)buf_size)); |
|
return res; |
|
} |
| |
/* Do not clear READ flag, since 'close' notification | /* Do not clear READ flag, since 'close' notification |
delivered as READ event */ | delivered as READ event */ |
|
|
if (handler->connectDone) | if (handler->connectDone) |
{ | { |
res = SSL_write(handler->ssl, buf, buf_size); | res = SSL_write(handler->ssl, buf, buf_size); |
PRINTF(("ssl write %d\n", res)); |
LOGD2((ZT("_Sock_Write - SSL_write using socket %d returned %d (< 0 for error) / %u bytes written, errno: %d (%s)"), handler->base.sock, res, (unsigned int)buf_size, errno, strerror(errno))); |
} | } |
else | else |
{ | { |
res = SSL_connect(handler->ssl); | res = SSL_connect(handler->ssl); |
PRINTF(("ssl connect %d\n", res)); |
LOGD2((ZT("_Sock_Write - SSL connect using socket %d returned result: %d, errno: %d (%s)"), handler->base.sock, res, errno, strerror(errno))); |
if ( res > 0 ) | if ( res > 0 ) |
{ | { |
/* we are done with accpet */ |
/* we are done with accept */ |
handler->connectDone = MI_TRUE; | handler->connectDone = MI_TRUE; |
return _Sock_Write(handler,buf,buf_size,sizeWritten); | return _Sock_Write(handler,buf,buf_size,sizeWritten); |
} | } |
|
|
| |
| |
if ( res == 0 ) | if ( res == 0 ) |
|
{ |
|
LOGW2((ZT("_Sock_Write - SSL socket connection closed"))); |
return MI_RESULT_OK; /* connection closed */ | return MI_RESULT_OK; /* connection closed */ |
|
} |
| |
if ( res > 0 ) | if ( res > 0 ) |
{ | { |
*sizeWritten = res; | *sizeWritten = res; |
|
LOGD2((ZT("_Sock_Write - SSL socket successful write of %d / %u bytes"), res, (unsigned int)buf_size)); |
return MI_RESULT_OK; /* ok */ | return MI_RESULT_OK; /* ok */ |
} | } |
| |
switch (SSL_get_error(handler->ssl, res)) |
sslError = SSL_get_error(handler->ssl, res); |
|
switch (sslError) |
{ | { |
case SSL_ERROR_WANT_WRITE: | case SSL_ERROR_WANT_WRITE: |
PRINTF(("ssl write/connetc WANT_WRITE\n")); |
LOGD2((ZT("_Sock_Write - SSL_write/connect returned WANT_WRITE"))); |
return MI_RESULT_WOULD_BLOCK; | return MI_RESULT_WOULD_BLOCK; |
| |
case SSL_ERROR_WANT_READ: | case SSL_ERROR_WANT_READ: |
PRINTF(("ssl write/connetc WANT_READ\n")); |
LOGD2((ZT("_Sock_Write - SSL_write/connect returned WANT_READ"))); |
handler->reverseOperations = MI_TRUE; /* wait until write is allowed */ | handler->reverseOperations = MI_TRUE; /* wait until write is allowed */ |
handler->base.mask |= SELECTOR_READ; | handler->base.mask |= SELECTOR_READ; |
handler->base.mask &= ~SELECTOR_WRITE; | handler->base.mask &= ~SELECTOR_WRITE; |
|
|
if (EAGAIN == errno || | if (EAGAIN == errno || |
EWOULDBLOCK == errno || | EWOULDBLOCK == errno || |
EINPROGRESS == errno) | EINPROGRESS == errno) |
|
{ |
|
LOGD2((ZT("_Sock_Write - Returning WOULD_BLOCK. errno: %d (%s)"), errno, strerror(errno))); |
return MI_RESULT_WOULD_BLOCK; | return MI_RESULT_WOULD_BLOCK; |
|
} |
|
|
|
LOGE2((ZT("_Sock_Write - SSL_write/connect returned unexpected OS error %d (%s)"), errno, strerror(errno))); |
|
trace_SSLWrite_UnexpectedSysError(errno); |
|
break; |
| |
LOGW_CHAR(("ssl-write: unexpected sys error %d\n", errno)); |
case SSL_ERROR_SSL: |
|
LOGE2((ZT("_Sock_Write - SSL_write/connect returned OpenSSL error %d (%s)"), sslError, ERR_error_string(sslError, NULL))); |
break; | break; |
| |
default: | default: |
|
LOGD2((ZT("_Sock_Write - SSL_write/connect returned uncategorized OpenSSL error: %d"), res)); |
break; | break; |
} | } |
|
|
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
| |
static Http_CallbackResult _ReadHeader( | static Http_CallbackResult _ReadHeader( |
Http_SR_SocketData* handler) |
HttpClient_SR_SocketData* handler) |
{ | { |
char* buf; | char* buf; |
char* currentLine; | char* currentLine; |
char* data; | char* data; |
|
size_t contentSize; |
size_t buf_size, received, index; | size_t buf_size, received, index; |
MI_Result r; | MI_Result r; |
MI_Boolean fullHeaderReceived = MI_FALSE; | MI_Boolean fullHeaderReceived = MI_FALSE; |
|
|
if (handler->recvingState != RECV_STATE_HEADER) | if (handler->recvingState != RECV_STATE_HEADER) |
return PRT_CONTINUE; | return PRT_CONTINUE; |
| |
buf = handler->recvBuffer + handler->recvievedSize; |
buf = handler->recvBuffer + handler->receivedSize; |
buf_size = handler->recvBufferSize - handler->recvievedSize; |
buf_size = handler->recvBufferSize - handler->receivedSize; |
received = 0; | received = 0; |
| |
r = _Sock_Read(handler, buf, buf_size, &received); | r = _Sock_Read(handler, buf, buf_size, &received); |
PRINTF(("%d: read r = %d, recv = %d; reverse %d\n", (int)handler->base.sock, (int)r, (int)received, (int)handler->reverseOperations )); |
LOGD2((ZT("_ReadHeader - Begin. _Sock_read result: %d (%s), socket: %d, %u / %u bytes read, reverse: %d"), (int)r, mistrerror(r), (int)handler->base.sock, (unsigned int)received, (unsigned int)buf_size, (int)handler->reverseOperations)); |
| |
if ( r == MI_RESULT_OK && 0 == received ) | if ( r == MI_RESULT_OK && 0 == received ) |
return PRT_RETURN_FALSE; /* conection closed */ |
{ |
|
LOGW2((ZT("_ReadHeader - 0 bytes received without error. Socket closed?"))); |
|
return PRT_RETURN_FALSE; /* connection closed */ |
|
} |
| |
if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK ) | if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK ) |
|
{ |
|
LOGE2((ZT("_ReadHeader - Error %d (%s)"), r, mistrerror(r))); |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
|
} |
| |
if (!received) |
if (received == 0) |
|
{ |
|
LOGD2((ZT("_ReadHeader - 0 bytes received. Waiting..."))); |
return PRT_RETURN_TRUE; | return PRT_RETURN_TRUE; |
|
} |
| |
handler->recvievedSize += received; |
handler->receivedSize += received; |
| |
/* check header */ | /* check header */ |
PRINTF_2(("%s\n",buf)); |
LOGD2((ZT("_ReadHeader - Received buffer: %s"), buf)); |
| |
/* did we get full header? */ | /* did we get full header? */ |
buf = handler->recvBuffer; | buf = handler->recvBuffer; |
for ( index = 3; index < handler->recvievedSize; index++ ) |
LOGD2((ZT("_ReadHeader - Checking for full header..."))); |
|
for ( index = 3; index < handler->receivedSize; index++ ) |
{ | { |
|
_Analysis_assume_(handler->recvBufferSize > 3); |
if (buf[index-3] == '\r' && buf[index-1] == '\r' && | if (buf[index-3] == '\r' && buf[index-1] == '\r' && |
buf[index-2] == '\n' && buf[index] == '\n' ) | buf[index-2] == '\n' && buf[index] == '\n' ) |
{ | { |
fullHeaderReceived = MI_TRUE; | fullHeaderReceived = MI_TRUE; |
|
LOGD2((ZT("_ReadHeader - Full header has been received"))); |
break; | break; |
} | } |
} | } |
| |
if (!fullHeaderReceived ) | if (!fullHeaderReceived ) |
{ | { |
if ( handler->recvievedSize < handler->recvBufferSize ) |
if (handler->receivedSize < handler->recvBufferSize) |
|
{ |
|
LOGD2((ZT("_ReadHeader - Full header not received. Waiting..."))); |
return PRT_RETURN_TRUE; /* continue reading */ | return PRT_RETURN_TRUE; /* continue reading */ |
|
} |
| |
if ( handler->recvBufferSize < MAX_HEADER_SIZE ) | if ( handler->recvBufferSize < MAX_HEADER_SIZE ) |
{ | { |
buf = realloc(handler->recvBuffer, handler->recvBufferSize * 2); |
LOGD2((ZT("_ReadHeader - Reallocating buffer..."))); |
|
buf = PAL_Realloc(handler->recvBuffer, handler->recvBufferSize * 2); |
| |
if (!buf) | if (!buf) |
|
{ |
|
LOGE2((ZT("_ReadHeader - Cannot allocate memory for larger header"))); |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
|
} |
| |
handler->recvBufferSize *= 2; | handler->recvBufferSize *= 2; |
handler->recvBuffer = buf; | handler->recvBuffer = buf; |
|
LOGD2((ZT("_ReadHeader - Going recursive..."))); |
return _ReadHeader(handler); | return _ReadHeader(handler); |
} | } |
else | else |
{ | { |
/* http header is too big - drop connection */ |
/* Http header is too big - drop connection */ |
LOGW((T("http header is too big; dropping connection\n"))); |
trace_HttpHeaderIsTooBig(); |
|
LOGE2((ZT("_ReadHeader - HTTP header is too big. Dropping connection"))); |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
} | } |
} | } |
|
|
data = buf + index + 1; /* pointer to data in case we got some */ | data = buf + index + 1; /* pointer to data in case we got some */ |
| |
if (!_getRequestLine(handler, ¤tLine)) | if (!_getRequestLine(handler, ¤tLine)) |
|
{ |
|
LOGE2((ZT("_ReadHeader - Cannot find request line in HTTP header"))); |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
|
} |
| |
while ((data-currentLine) > 3) | while ((data-currentLine) > 3) |
{ | { |
if (!_getHeaderField(handler, ¤tLine)) | if (!_getHeaderField(handler, ¤tLine)) |
|
{ |
|
LOGE2((ZT("_ReadHeader - Cannot find HTTP header field"))); |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
|
} |
} | } |
| |
/* Check if we have to deal with chunked-encoded data */ | /* Check if we have to deal with chunked-encoded data */ |
if (handler->contentLength < 0) | if (handler->contentLength < 0) |
{ | { |
handler->recvievedSize -= index + 1; |
handler->receivedSize -= index + 1; |
| |
/* Invoke user's callback with header information */ | /* Invoke user's callback with header information */ |
{ | { |
|
|
| |
if (!(*self->callbackOnResponse)( self, self->callbackData, &handler->recvHeaders, | if (!(*self->callbackOnResponse)( self, self->callbackData, &handler->recvHeaders, |
handler->contentLength, handler->contentLength == 0, 0)) | handler->contentLength, handler->contentLength == 0, 0)) |
|
{ |
|
LOGD2((ZT("_ReadHeader - On response callback for chunked data header failed"))); |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
} | } |
|
} |
| |
/* remove consumed header part */ | /* remove consumed header part */ |
memmove(handler->recvBuffer, data, handler->recvievedSize); |
memmove(handler->recvBuffer, data, handler->receivedSize); |
| |
handler->recvingState = RECV_STATE_CHUNKHEADER; | handler->recvingState = RECV_STATE_CHUNKHEADER; |
return PRT_CONTINUE; | return PRT_CONTINUE; |
} | } |
| |
|
contentSize = (size_t)handler->contentLength; |
|
if (handler->headVerb) |
|
{ |
|
LOGD2((ZT("_ReadHeader - HEAD response received. Download will contain %u bytes"), (unsigned int)contentSize)); |
|
contentSize = 0; |
|
} |
|
|
/* Allocate zero-terminated buffer */ | /* Allocate zero-terminated buffer */ |
handler->recvPage = (Page*)malloc(sizeof(Page) + (size_t)handler->contentLength + 1); |
handler->recvPage = (Page*)PAL_Malloc(sizeof (Page) + (size_t)contentSize + 1); |
| |
if (!handler->recvPage) |
if (handler->recvPage == NULL) |
|
{ |
|
LOGD2((ZT("_ReadHeader - Cannot allocate memory for received page"))); |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
|
} |
|
((char*)(handler->recvPage + 1))[contentSize] = '\0'; |
| |
((char*)(handler->recvPage + 1))[handler->contentLength] = 0; |
handler->recvPage->u.s.size = (unsigned int)contentSize; |
|
|
handler->recvPage->u.s.size = (unsigned int)handler->contentLength; |
|
handler->recvPage->u.s.next = 0; | handler->recvPage->u.s.next = 0; |
|
handler->receivedSize -= index + 1; |
handler->recvievedSize -= index + 1; |
|
| |
/* Verify that we have not more than 'content-length' bytes in buffer left | /* Verify that we have not more than 'content-length' bytes in buffer left |
If we have more, assuming http client is invalid and drop connection */ | If we have more, assuming http client is invalid and drop connection */ |
if (handler->recvievedSize > (size_t)handler->contentLength) |
if (handler->receivedSize > contentSize) |
{ | { |
LOGW((T("http payload is bigger than content-length\n"))); |
trace_HttpPayloadIsBiggerThanContentLength(); |
|
LOGE2((ZT("_ReadHeader - HTTP payload is bigger than content-length (%u > %u bytes)"), (unsigned int)handler->receivedSize, (unsigned int)contentSize)); |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
} | } |
| |
memcpy( handler->recvPage + 1, data, handler->recvievedSize ); |
if (handler->receivedSize != 0) |
|
memcpy(handler->recvPage + 1, data, handler->receivedSize); |
handler->recvingState = RECV_STATE_CONTENT; | handler->recvingState = RECV_STATE_CONTENT; |
| |
PRINTF_2(("full header read; page size %d, position %d\n", handler->recvPage->u.s.size, handler->recvievedSize)); |
|
|
|
/* Invoke user's callback with header information */ | /* Invoke user's callback with header information */ |
{ | { |
HttpClient* self = (HttpClient*)handler->base.data; | HttpClient* self = (HttpClient*)handler->base.data; |
| |
if (!(*self->callbackOnResponse)( self, self->callbackData, &handler->recvHeaders, | if (!(*self->callbackOnResponse)( self, self->callbackData, &handler->recvHeaders, |
handler->contentLength, handler->contentLength == 0, 0)) | handler->contentLength, handler->contentLength == 0, 0)) |
|
{ |
|
LOGE2((ZT("_ReadHeader - On response callback for header failed"))); |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
} | } |
|
} |
| |
|
LOGD2((ZT("_ReadHeader - OK exit"))); |
return PRT_CONTINUE; | return PRT_CONTINUE; |
} | } |
| |
static Http_CallbackResult _ReadData( | static Http_CallbackResult _ReadData( |
Http_SR_SocketData* handler) |
HttpClient_SR_SocketData* handler) |
{ | { |
//HttpClient* self = (HttpClient*)handler->base.data; |
/* HttpClient* self = (HttpClient*)handler->base.data; */ |
char* buf; | char* buf; |
size_t buf_size, received; | size_t buf_size, received; |
MI_Result r; | MI_Result r; |
|
|
if (handler->recvingState != RECV_STATE_CONTENT) | if (handler->recvingState != RECV_STATE_CONTENT) |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
| |
buf = ((char*)(handler->recvPage + 1)) + handler->recvievedSize; |
LOGD2((ZT("_ReadData - Begin. Head? %d"), handler->headVerb)); |
buf_size = (size_t)(handler->contentLength - handler->recvievedSize); |
if (!handler->headVerb) |
|
{ |
|
buf = (char*)(handler->recvPage + 1) + handler->receivedSize; |
|
buf_size = (size_t)(handler->contentLength - handler->receivedSize); |
received = 0; | received = 0; |
| |
if (buf_size) |
if (buf_size != 0) |
{ | { |
r = _Sock_Read(handler, buf, buf_size, &received); | r = _Sock_Read(handler, buf, buf_size, &received); |
|
LOGD2((ZT("_ReadData - _Sock_Read result: %d (%s), socket: %d, recv: %u"), (int)r, mistrerror(r), (int)handler->base.sock, (unsigned int)received)); |
PRINTF(("%d: read r = %d, recv = %d\n", (int)handler->base.sock, (int)r, (int)received )); |
|
| |
if ( r == MI_RESULT_OK && 0 == received ) | if ( r == MI_RESULT_OK && 0 == received ) |
return PRT_RETURN_FALSE; /* conection closed */ |
return PRT_RETURN_FALSE; /* connection closed */ |
| |
if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK ) | if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK ) |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
| |
handler->recvievedSize += received; |
handler->receivedSize += received; |
|
|
|
LOGD2((ZT("_RequestCallback - Called _ReadData. %d / %d bytes read"), (int)handler->receivedSize, (int)handler->contentLength)); |
|
|
|
if (handler->contentLength > 0 && handler->receivedSize < (size_t)handler->contentLength) |
|
{ /* assume 500 bytes per millisecond transmission */ |
|
/* wait to avoid spinning on _Sock_Read */ |
|
unsigned int bytesLeft = (unsigned int)handler->contentLength - (unsigned int)handler->receivedSize; |
|
unsigned long msec = (unsigned long)(bytesLeft / 500 + 1); |
|
|
|
Sleep_Milliseconds(msec); |
|
} |
} | } |
| |
/* did we get all data? */ | /* did we get all data? */ |
PRINTF_2(("dt status - %d / %d\n", (int)handler->recvievedSize, (int)handler->contentLength)); |
LOGD2((ZT("_ReadData - Received size: %d / %d"), (int)handler->receivedSize, (int)handler->contentLength)); |
|
if (handler->receivedSize != (size_t)handler->contentLength) |
if ( handler->recvievedSize != (size_t)handler->contentLength ) |
|
return PRT_RETURN_TRUE; | return PRT_RETURN_TRUE; |
|
} |
| |
/* Invoke user's callback with header information */ | /* Invoke user's callback with header information */ |
{ | { |
HttpClient* self = (HttpClient*)handler->base.data; | HttpClient* self = (HttpClient*)handler->base.data; |
|
MI_Boolean lastChunk = MI_TRUE; |
|
|
|
if (handler->contentEnd >= 0 && |
|
handler->contentEnd + 1 < handler->contentTotalLength) |
|
{ |
|
lastChunk = MI_FALSE; |
|
} |
| |
if (!(*self->callbackOnResponse)( self, self->callbackData, 0, | if (!(*self->callbackOnResponse)( self, self->callbackData, 0, |
handler->contentLength, MI_TRUE, &handler->recvPage)) |
handler->contentLength, lastChunk, &handler->recvPage)) |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
| |
/* status callback */ | /* status callback */ |
|
|
MI_RESULT_OK ); | MI_RESULT_OK ); |
} | } |
| |
|
if (handler->recvPage != NULL) |
|
{ |
|
LOGD2((ZT("_ReadData - Freeing recvPage. socket: %d"), (int)handler->base.sock)); |
|
PAL_Free(handler->recvPage); |
|
} |
| |
if (handler->recvPage) |
handler->recvPage = NULL; |
free(handler->recvPage); |
handler->receivedSize = 0; |
|
|
handler->recvPage = 0; |
|
handler->recvievedSize = 0; |
|
memset(&handler->recvHeaders, 0, sizeof(handler->recvHeaders)); | memset(&handler->recvHeaders, 0, sizeof(handler->recvHeaders)); |
handler->recvingState = RECV_STATE_HEADER; | handler->recvingState = RECV_STATE_HEADER; |
|
LOGD2((ZT("_ReadData - OK exit"))); |
return PRT_CONTINUE; | return PRT_CONTINUE; |
} | } |
| |
|
|
static Http_CallbackResult _ReadChunkHeader( | static Http_CallbackResult _ReadChunkHeader( |
Http_SR_SocketData* handler) |
HttpClient_SR_SocketData* handler) |
{ | { |
char* buf; | char* buf; |
char* currentLine; | char* currentLine; |
|
|
if (handler->recvingState != RECV_STATE_CHUNKHEADER) | if (handler->recvingState != RECV_STATE_CHUNKHEADER) |
return PRT_CONTINUE; | return PRT_CONTINUE; |
| |
buf = handler->recvBuffer + handler->recvievedSize; |
buf = handler->recvBuffer + handler->receivedSize; |
buf_size = handler->recvBufferSize - handler->recvievedSize; |
buf_size = handler->recvBufferSize - handler->receivedSize; |
received = 0; | received = 0; |
| |
r = _Sock_Read(handler, buf, buf_size, &received); | r = _Sock_Read(handler, buf, buf_size, &received); |
PRINTF(("%d: read r = %d, recv = %d; reverse %d\n", (int)handler->base.sock, (int)r, (int)received, (int)handler->reverseOperations )); |
|
| |
if ( r == MI_RESULT_OK && 0 == received ) | if ( r == MI_RESULT_OK && 0 == received ) |
{ | { |
if (!handler->recvBufferSize) | if (!handler->recvBufferSize) |
return PRT_RETURN_FALSE; /* conection closed */ |
return PRT_RETURN_FALSE; /* connection closed */ |
| |
connectionClosed = MI_TRUE; | connectionClosed = MI_TRUE; |
} | } |
|
|
if (!received && !handler->recvBufferSize) | if (!received && !handler->recvBufferSize) |
return PRT_RETURN_TRUE; | return PRT_RETURN_TRUE; |
| |
handler->recvievedSize += received; |
handler->receivedSize += received; |
|
|
/* check header */ |
|
PRINTF_2(("%s\n",buf)); |
|
| |
/* did we get full header? */ | /* did we get full header? */ |
buf = handler->recvBuffer; | buf = handler->recvBuffer; |
for ( index = 1; index < handler->recvievedSize; index++ ) |
|
|
_Analysis_assume_(handler->recvBufferSize > 2); |
|
for (index = 1; index < handler->receivedSize && buf[index]; index++) |
{ | { |
if (buf[index-1] == '\r' && buf[index] == '\n' ) | if (buf[index-1] == '\r' && buf[index] == '\n' ) |
{ | { |
|
|
if (!fullHeaderReceived ) | if (!fullHeaderReceived ) |
{ | { |
if (connectionClosed) | if (connectionClosed) |
return PRT_RETURN_FALSE; /* conection closed */ |
return PRT_RETURN_FALSE; /* connection closed */ |
| |
if ( handler->recvievedSize < handler->recvBufferSize ) |
if (handler->receivedSize < handler->recvBufferSize) |
return PRT_RETURN_TRUE; /* continue reading */ | return PRT_RETURN_TRUE; /* continue reading */ |
| |
if ( handler->recvBufferSize < MAX_HEADER_SIZE ) | if ( handler->recvBufferSize < MAX_HEADER_SIZE ) |
{ | { |
buf = realloc(handler->recvBuffer, handler->recvBufferSize * 2); |
buf = PAL_Realloc(handler->recvBuffer, handler->recvBufferSize * 2); |
| |
if (!buf) | if (!buf) |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
|
|
else | else |
{ | { |
/* http chunk header is too big - drop connection */ | /* http chunk header is too big - drop connection */ |
LOGW((T("http chunk header is too big; dropping connection\n"))); |
trace_HttpChunkHeaderIsTooBig(); |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
} | } |
} | } |
|
|
| |
/* clean up state */ | /* clean up state */ |
handler->recvPage = 0; | handler->recvPage = 0; |
handler->recvievedSize = 0; |
handler->receivedSize = 0; |
memset(&handler->recvHeaders, 0, sizeof(handler->recvHeaders)); | memset(&handler->recvHeaders, 0, sizeof(handler->recvHeaders)); |
handler->recvingState = RECV_STATE_HEADER; | handler->recvingState = RECV_STATE_HEADER; |
| |
if (connectionClosed) | if (connectionClosed) |
return PRT_RETURN_FALSE; /* conection closed */ |
return PRT_RETURN_FALSE; /* connection closed */ |
| |
return PRT_CONTINUE; | return PRT_CONTINUE; |
} | } |
| |
/* Allocate zero-terminated buffer */ | /* Allocate zero-terminated buffer */ |
handler->recvPage = (Page*)malloc(sizeof(Page) + (size_t)chunkSize + 2 /*CR-LF*/ + 1 /* \0 */); |
handler->recvPage = (Page*)PAL_Malloc(sizeof(Page) + (size_t)chunkSize + 2 /*CR-LF*/ + 1 /* \0 */); |
| |
if (!handler->recvPage) | if (!handler->recvPage) |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
|
|
handler->recvPage->u.s.next = 0; | handler->recvPage->u.s.next = 0; |
| |
/* subtract header size */ | /* subtract header size */ |
handler->recvievedSize -= index + 1; |
handler->receivedSize -= index + 1; |
| |
/* in case of small chunks we may receive more than one chunk already */ | /* in case of small chunks we may receive more than one chunk already */ |
if (handler->recvievedSize > (size_t)(chunkSize+2)) |
if (handler->receivedSize > (size_t)(chunkSize+2)) |
{ | { |
/* copy page size to page */ | /* copy page size to page */ |
memcpy( handler->recvPage + 1, data, chunkSize+2 ); | memcpy( handler->recvPage + 1, data, chunkSize+2 ); |
|
|
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
| |
if (handler->recvPage) | if (handler->recvPage) |
free(handler->recvPage); |
PAL_Free(handler->recvPage); |
| |
handler->recvPage = 0; | handler->recvPage = 0; |
} | } |
| |
/* remove consumed part */ | /* remove consumed part */ |
memmove(handler->recvBuffer, data + chunkSize+2, handler->recvievedSize - (chunkSize+2)); |
memmove(handler->recvBuffer, data + chunkSize+2, handler->receivedSize - (chunkSize+2)); |
handler->recvievedSize -= (chunkSize+2); |
handler->receivedSize -= (chunkSize+2); |
| |
/* consume next chunk */ | /* consume next chunk */ |
return _ReadChunkHeader(handler); | return _ReadChunkHeader(handler); |
} | } |
| |
memcpy( handler->recvPage + 1, data, handler->recvievedSize ); |
memcpy(handler->recvPage + 1, data, handler->receivedSize); |
handler->recvingState = RECV_STATE_CHUNKDATA; | handler->recvingState = RECV_STATE_CHUNKDATA; |
| |
if (connectionClosed) | if (connectionClosed) |
return PRT_RETURN_FALSE; /* conection closed */ |
return PRT_RETURN_FALSE; /* connection closed */ |
| |
return PRT_CONTINUE; | return PRT_CONTINUE; |
} | } |
static Http_CallbackResult _ReadChunkData( | static Http_CallbackResult _ReadChunkData( |
Http_SR_SocketData* handler) |
HttpClient_SR_SocketData* handler) |
{ | { |
//HttpClient* self = (HttpClient*)handler->base.data; | //HttpClient* self = (HttpClient*)handler->base.data; |
char* buf; | char* buf; |
|
|
if (handler->recvingState != RECV_STATE_CHUNKDATA) | if (handler->recvingState != RECV_STATE_CHUNKDATA) |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
| |
buf = ((char*)(handler->recvPage + 1)) + handler->recvievedSize; |
buf = ((char*)(handler->recvPage + 1)) + handler->receivedSize; |
buf_size = (size_t)(handler->recvPage->u.s.size + 2 /* CR-LF */ - handler->recvievedSize); |
buf_size = (size_t)(handler->recvPage->u.s.size + 2 /* CR-LF */ - handler->receivedSize); |
received = 0; | received = 0; |
| |
if (buf_size) | if (buf_size) |
{ | { |
r = _Sock_Read(handler, buf, buf_size, &received); | r = _Sock_Read(handler, buf, buf_size, &received); |
| |
PRINTF(("%d: read r = %d, recv = %d\n", (int)handler->base.sock, (int)r, (int)received )); |
|
|
|
if ( r == MI_RESULT_OK && 0 == received ) | if ( r == MI_RESULT_OK && 0 == received ) |
return PRT_RETURN_FALSE; /* conection closed */ |
return PRT_RETURN_FALSE; /* connection closed */ |
| |
if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK ) | if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK ) |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
| |
handler->recvievedSize += received; |
handler->receivedSize += received; |
} | } |
| |
/* did we get all data? */ |
if (handler->receivedSize != (size_t)(handler->recvPage->u.s.size + 2 /* CR-LF */)) |
PRINTF_2(("dt status - %d / %d\n", (int)handler->recvievedSize, (int)handler->contentLength)); |
|
|
|
if ( handler->recvievedSize != (size_t)(handler->recvPage->u.s.size + 2 /* CR-LF */) ) |
|
return PRT_RETURN_TRUE; | return PRT_RETURN_TRUE; |
| |
/* Invoke user's callback with header information */ | /* Invoke user's callback with header information */ |
|
|
| |
} | } |
| |
|
|
if (handler->recvPage) | if (handler->recvPage) |
free(handler->recvPage); |
PAL_Free(handler->recvPage); |
| |
handler->recvPage = 0; | handler->recvPage = 0; |
handler->recvievedSize = 0; |
handler->receivedSize = 0; |
memset(&handler->recvHeaders, 0, sizeof(handler->recvHeaders)); | memset(&handler->recvHeaders, 0, sizeof(handler->recvHeaders)); |
handler->recvingState = RECV_STATE_CHUNKHEADER; | handler->recvingState = RECV_STATE_CHUNKHEADER; |
return PRT_CONTINUE; | return PRT_CONTINUE; |
} | } |
| |
|
|
static Http_CallbackResult _WriteHeader( | static Http_CallbackResult _WriteHeader( |
Http_SR_SocketData* handler) |
HttpClient_SR_SocketData* handler) |
{ | { |
char* buf; | char* buf; |
size_t buf_size, sent; | size_t buf_size, sent; |
|
|
if (handler->sendingState == RECV_STATE_CONTENT) | if (handler->sendingState == RECV_STATE_CONTENT) |
return PRT_CONTINUE; | return PRT_CONTINUE; |
| |
|
LOGD2((ZT("_WriteHeader - Begin"))); |
|
|
buf = ((char*)(handler->sendHeader + 1)) + handler->sentSize; | buf = ((char*)(handler->sendHeader + 1)) + handler->sentSize; |
buf_size = handler->sendHeader->u.s.size - handler->sentSize; | buf_size = handler->sendHeader->u.s.size - handler->sentSize; |
sent = 0; | sent = 0; |
| |
r = _Sock_Write(handler, buf, buf_size, &sent); | r = _Sock_Write(handler, buf, buf_size, &sent); |
|
LOGD2((ZT("_WriteHeader - _Sock_Write result: %d (%s), socket: %d, sent: %d"), (int)r, mistrerror(r), (int)handler->base.sock, (int)sent)); |
PRINTF(("%d: write r = %d, sent = %d\n", (int)handler->base.sock, (int)r, (int)sent )); |
|
| |
if ( r == MI_RESULT_OK && 0 == sent ) | if ( r == MI_RESULT_OK && 0 == sent ) |
return PRT_RETURN_FALSE; /* conection closed */ |
{ |
|
LOGE2((ZT("_WriteHeader - Connection closed"))); |
|
return PRT_RETURN_FALSE; /* connection closed */ |
|
} |
| |
if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK ) | if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK ) |
|
{ |
|
LOGE2((ZT("_WriteHeader - _Sock_Write returned error: %d (%s)"), (int)r, mistrerror(r))); |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
|
} |
| |
handler->sentSize += sent; | handler->sentSize += sent; |
|
handler->headVerb = buf_size > 4 && Strncasecmp(buf, "HEAD", 4) == 0; |
| |
/* did we get all data? */ |
/* did we send all data? */ |
|
|
if ( handler->sentSize != handler->sendHeader->u.s.size ) | if ( handler->sentSize != handler->sendHeader->u.s.size ) |
|
{ |
|
LOGD2((ZT("_WriteHeader - Partial write. %u sent this time, %u / %d written, result: %d (%s)"), (unsigned int)sent, (unsigned int)handler->sentSize, (unsigned int)handler->sendHeader->u.s.size, r, mistrerror(r))); |
return PRT_RETURN_TRUE; | return PRT_RETURN_TRUE; |
|
} |
| |
free(handler->sendHeader); |
PAL_Free(handler->sendHeader); |
handler->sendHeader = 0; | handler->sendHeader = 0; |
handler->sentSize = 0; | handler->sentSize = 0; |
handler->sendingState = RECV_STATE_CONTENT; | handler->sendingState = RECV_STATE_CONTENT; |
| |
|
LOGD2((ZT("_WriteHeader - OK exit"))); |
return PRT_CONTINUE; | return PRT_CONTINUE; |
} | } |
| |
static Http_CallbackResult _WriteData( | static Http_CallbackResult _WriteData( |
Http_SR_SocketData* handler) |
HttpClient_SR_SocketData* handler) |
{ | { |
char* buf; | char* buf; |
size_t buf_size, sent; | size_t buf_size, sent; |
MI_Result r; | MI_Result r; |
| |
|
LOGD2((ZT("_WriteData - Begin"))); |
/* are we in the right state? */ | /* are we in the right state? */ |
if (handler->sendingState != RECV_STATE_CONTENT) | if (handler->sendingState != RECV_STATE_CONTENT) |
|
{ |
|
LOGE2((ZT("_WriteData - Wrong state. state: %d"), handler->sendingState)); |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
|
} |
| |
if (!handler->sendPage) | if (!handler->sendPage) |
{ /* no content*/ | { /* no content*/ |
|
|
handler->base.mask &= ~SELECTOR_WRITE; | handler->base.mask &= ~SELECTOR_WRITE; |
handler->base.mask |= SELECTOR_READ; | handler->base.mask |= SELECTOR_READ; |
| |
|
LOGW2((ZT("_WriteData - Content is empty. Continuing"))); |
return PRT_CONTINUE; | return PRT_CONTINUE; |
} | } |
| |
|
|
sent = 0; | sent = 0; |
| |
r = _Sock_Write(handler, buf, buf_size, &sent); | r = _Sock_Write(handler, buf, buf_size, &sent); |
|
LOGD2((ZT("_WriteData - HTTPClient sent %u / %u bytes with result %d (%s)"), (unsigned int)sent, (unsigned int)buf_size, (int)r, mistrerror(r))); |
PRINTF(("%d: write r = %d, sent = %d\n", (int)handler->base.sock, (int)r, (int)sent )); |
|
| |
if ( r == MI_RESULT_OK && 0 == sent ) | if ( r == MI_RESULT_OK && 0 == sent ) |
return PRT_RETURN_FALSE; /* conection closed */ |
{ |
|
LOGE2((ZT("_WriteData exit. Connection closed"))); |
|
return PRT_RETURN_FALSE; /* connection closed */ |
|
} |
| |
if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK ) | if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK ) |
|
{ |
|
LOGE2((ZT("_WriteData exit - Error: %d (%s)"), r, mistrerror(r))); |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
|
} |
| |
handler->sentSize += sent; | handler->sentSize += sent; |
| |
/* did we get all data? */ |
/* did we send all data? */ |
| |
if ( handler->sentSize != handler->sendPage->u.s.size ) | if ( handler->sentSize != handler->sendPage->u.s.size ) |
|
{ |
|
LOGD2((ZT("_WriteData - Exit. Partial write. %u / %u bytes written"), (unsigned int)handler->sentSize, (unsigned int)handler->sendPage->u.s.size)); |
return PRT_RETURN_TRUE; | return PRT_RETURN_TRUE; |
|
} |
| |
free(handler->sendPage); |
LOGD2((ZT("_WriteData - %u / %u bytes sent"), (unsigned int)handler->sentSize, (unsigned int)handler->sendPage->u.s.size)); |
handler->sendPage = 0; |
PAL_Free(handler->sendPage); |
|
handler->sendPage = NULL; |
handler->sentSize = 0; | handler->sentSize = 0; |
handler->sendingState = RECV_STATE_HEADER; | handler->sendingState = RECV_STATE_HEADER; |
handler->base.mask &= ~SELECTOR_WRITE; | handler->base.mask &= ~SELECTOR_WRITE; |
handler->base.mask |= SELECTOR_READ; | handler->base.mask |= SELECTOR_READ; |
| |
|
LOGD2((ZT("_WriteData - OK exit. returning: %d"), PRT_CONTINUE)); |
|
|
return PRT_CONTINUE; | return PRT_CONTINUE; |
} | } |
| |
|
|
static MI_Boolean _RequestCallbackRead( | static MI_Boolean _RequestCallbackRead( |
Http_SR_SocketData* handler) |
HttpClient_SR_SocketData* handler) |
{ | { |
switch (_ReadHeader(handler)) | switch (_ReadHeader(handler)) |
{ | { |
|
|
} | } |
| |
static MI_Boolean _RequestCallbackWrite( | static MI_Boolean _RequestCallbackWrite( |
Http_SR_SocketData* handler) |
HttpClient_SR_SocketData* handler) |
{ | { |
switch (_WriteHeader(handler)) | switch (_WriteHeader(handler)) |
{ | { |
|
|
MI_Uint32 mask, | MI_Uint32 mask, |
MI_Uint64 currentTimeUsec) | MI_Uint64 currentTimeUsec) |
{ | { |
Http_SR_SocketData* handler = (Http_SR_SocketData*)handlerIn; |
HttpClient_SR_SocketData* handler = (HttpClient_SR_SocketData*)handlerIn; |
sel=sel; |
MI_UNUSED(sel); |
| |
if ( ((mask & SELECTOR_READ) != 0 && !handler->reverseOperations) || | if ( ((mask & SELECTOR_READ) != 0 && !handler->reverseOperations) || |
((mask & SELECTOR_WRITE) != 0 && handler->reverseOperations) ) | ((mask & SELECTOR_WRITE) != 0 && handler->reverseOperations) ) |
{ | { |
if (!_RequestCallbackRead(handler)) | if (!_RequestCallbackRead(handler)) |
|
{ |
|
LOGE2((ZT("_RequestCallback - RequestCallbackRead failed"))); |
return MI_FALSE; | return MI_FALSE; |
} | } |
|
LOGD2((ZT("_RequestCallback - Called _RequestCallbackRead. %u / %u bytes read"), (unsigned int)handler->receivedSize, handler->recvPage == NULL ? 0 : (unsigned int)handler->recvPage->u.s.size)); |
|
} |
| |
if ( ((mask & SELECTOR_WRITE) != 0 && !handler->reverseOperations) || | if ( ((mask & SELECTOR_WRITE) != 0 && !handler->reverseOperations) || |
((mask & SELECTOR_READ) != 0 && handler->reverseOperations) ) | ((mask & SELECTOR_READ) != 0 && handler->reverseOperations) ) |
{ | { |
if (!_RequestCallbackWrite(handler)) | if (!_RequestCallbackWrite(handler)) |
|
{ |
|
LOGE2((ZT("_RequestCallback - _RequestCallbackWrite failed"))); |
|
return MI_FALSE; |
|
} |
|
LOGD2((ZT("_RequestCallback - Called _RequestCallbackWrite. %u / %u bytes sent"), (unsigned int)handler->sentSize, handler->sendPage == NULL ? 0 : (unsigned int)handler->sendPage->u.s.size)); |
|
while (handler->sendPage != NULL && handler->sentSize < handler->sendPage->u.s.size) |
|
{ /* assume 500 bytes per millisecond transmission */ |
|
/* wait after to avoid spinning too much on _WriteData */ |
|
unsigned int bytesLeft = (unsigned int)handler->sendPage->u.s.size - (unsigned int)handler->sentSize; |
|
unsigned long msec = (unsigned long)(bytesLeft / 500 + 1); |
|
|
|
LOGD2((ZT("_RequestCallback - Called _WriteData. %u / %u bytes sent"), (unsigned int)handler->sentSize, handler->sendPage == NULL ? 0 : (unsigned int)handler->sendPage->u.s.size)); |
|
if (_WriteData(handler) == MI_FALSE) |
|
{ |
|
LOGE2((ZT("_RequestCallback - _WriteData failed"))); |
return MI_FALSE; | return MI_FALSE; |
} | } |
|
LOGD2((ZT("_RequestCallback - Called _WriteData. %u bytes written, %u bytes left"), (unsigned int)handler->sentSize, handler->sendPage == NULL ? 0 : (unsigned int)handler->sendPage->u.s.size)); |
|
Sleep_Milliseconds(msec); |
|
} |
|
LOGD2((ZT("_RequestCallback - Called _RequestCallbackWrite. %u / %u bytes sent"), (unsigned int)handler->sentSize, handler->sendPage == NULL ? 0 : (unsigned int)handler->sendPage->u.s.size)); |
|
} |
| |
/* re-set timeout - if we performed R/W operation, set timeout depending where we are in communication */ | /* re-set timeout - if we performed R/W operation, set timeout depending where we are in communication */ |
if (mask & (SELECTOR_READ | SELECTOR_WRITE)) | if (mask & (SELECTOR_READ | SELECTOR_WRITE)) |
|
|
handler->base.fireTimeoutAt = currentTimeUsec + handler->timeoutUsec; | handler->base.fireTimeoutAt = currentTimeUsec + handler->timeoutUsec; |
} | } |
| |
/* Close conenction by timeout */ |
/* Close connection by timeout */ |
if (mask & SELECTOR_TIMEOUT) |
if ((mask & SELECTOR_TIMEOUT) != 0) |
|
{ |
|
if (handler->status != MI_RESULT_OK) |
{ | { |
if (MI_RESULT_OK != handler->status) |
handler->recvHeaders.httpError = 408; |
handler->status = MI_RESULT_TIME_OUT; | handler->status = MI_RESULT_TIME_OUT; |
|
} |
|
LOGE2((ZT("_RequestCallback - Timed out. socket: %d, result: %d (%s)"), handler->base.sock, handler->status, mistrerror(handler->status))); |
return MI_FALSE; | return MI_FALSE; |
} | } |
| |
if ((mask & SELECTOR_REMOVE) != 0 || |
if ((mask & SELECTOR_REMOVE) != 0 || (mask & SELECTOR_DESTROY) != 0) |
(mask & SELECTOR_DESTROY) != 0) |
|
{ | { |
HttpClient* self = (HttpClient*)handler->base.data; | HttpClient* self = (HttpClient*)handler->base.data; |
| |
/* notify next stack layer */ | /* notify next stack layer */ |
if (MI_RESULT_OK != handler->status) |
if (handler->status != MI_RESULT_OK) |
(*self->callbackOnStatus)( |
(*self->callbackOnStatus)(self, self->callbackData, handler->status); |
self, |
|
self->callbackData, |
/* Yeah, this is hokey, but we need to sleep here to let the */ |
handler->status); |
/* subsystems have the opportunity to send the data before we close */ |
|
/* the socket, or we'll get a broken pipe/connection reset */ |
|
#if defined(CONFIG_OS_WINDOWS) |
|
Sleep_Milliseconds(1); |
|
#else |
|
usleep(50); |
|
#endif |
|
|
|
if (handler == NULL) |
|
{ |
|
LOGE2((ZT("_RequestCallback - The handler object was free'd under us!"))); |
|
return MI_TRUE; |
|
} |
| |
self->connector = 0; |
self->connector = NULL; |
| |
if (handler->ssl) | if (handler->ssl) |
SSL_free(handler->ssl); | SSL_free(handler->ssl); |
| |
PRINTF(("%d: close\n", (int)handler->base.sock)); |
|
|
|
Sock_Close(handler->base.sock); | Sock_Close(handler->base.sock); |
| |
if (handler->recvPage) | if (handler->recvPage) |
free(handler->recvPage); |
PAL_Free(handler->recvPage); |
| |
if (handler->sendPage) | if (handler->sendPage) |
free(handler->sendPage); |
PAL_Free(handler->sendPage); |
| |
if (handler->sendHeader) | if (handler->sendHeader) |
free(handler->sendHeader); |
PAL_Free(handler->sendHeader); |
| |
free(handler->recvBuffer); |
PAL_Free(handler->recvBuffer); |
free(handler); |
PAL_Free(handler); |
} | } |
| |
return MI_TRUE; | return MI_TRUE; |
} | } |
| |
#ifdef CONFIG_POSIX | #ifdef CONFIG_POSIX |
static MI_Result _CreateSSLContext(HttpClient* self) |
|
|
/* |
|
Verify callback when the server authentication certificate's chain of trust is checked. This |
|
is the same as the Open SSL verify callback function (return preverify_ok), except that it |
|
logs a diagnostic message when preverify_ok has a failed status. |
|
*/ |
|
static int _ctxVerify( |
|
int preverify_ok, |
|
X509_STORE_CTX* ctx) |
|
{ |
|
if (preverify_ok <= 0) |
{ | { |
SSL_CTX * sslContext = 0; |
X509* certWithError = X509_STORE_CTX_get_current_cert(ctx); |
|
int error = X509_STORE_CTX_get_error(ctx); |
|
char nameBuf[256]; |
| |
sslContext = SSL_CTX_new(SSLv23_method()); |
X509_NAME_oneline(X509_get_subject_name(certWithError), nameBuf, 256); |
|
trace_SSL_VerifyFailed(error, nameBuf); |
|
} |
|
return preverify_ok; |
|
} |
| |
if (!sslContext) |
/* |
|
Create an Open SSL context that will be used for secure communication. Set up server and client |
|
certificate authentication if specified. |
|
*/ |
|
static MI_Result _CreateSSLContext( |
|
HttpClient* self, |
|
const char* trustedCertsDir, |
|
const char* certFile, |
|
const char* privateKeyFile) |
|
{ |
|
SSL_CTX* sslContext = SSL_CTX_new(SSLv23_method()); |
|
if (sslContext == NULL) |
{ | { |
LOGE_CHAR(( |
LOGE2((ZT("_CreateSSLContext - Cannot create SSL context"))); |
"---> SSL: cannot create ssl context")); |
trace_SSL_CannotCreateContext(); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
SSL_CTX_set_quiet_shutdown(sslContext, 1); | SSL_CTX_set_quiet_shutdown(sslContext, 1); |
SSL_CTX_set_mode(sslContext, SSL_MODE_AUTO_RETRY); |
(void)SSL_CTX_set_mode(sslContext, SSL_MODE_AUTO_RETRY | SSL_MODE_ENABLE_PARTIAL_WRITE); |
SSL_CTX_set_mode(sslContext, SSL_MODE_ENABLE_PARTIAL_WRITE); |
|
SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_OFF); | SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_OFF); |
#if 0 |
SSL_CTX_sess_set_remove_cb(sslContext, NULL); |
/* Check if there is a certificate file (file containing server |
|
** certificate) specified. If specified, validate and load the |
if (trustedCertsDir != NULL) |
** certificate. |
{ |
|
/* Cause Open SSL to check the server certificate Subject against its FQDN and |
|
** to check the server certificate chain against the contents of *trustedCertsDir. |
|
*/ |
|
if (SSL_CTX_load_verify_locations(sslContext, NULL, trustedCertsDir) < 0) |
|
{ |
|
LOGE2((ZT("_CreateSSLContext - Cannot set directory containing trusted certificate(s) to %s"), trustedCertsDir)); |
|
trace_SSL_BadTrustDir(trustedCertsDir); |
|
} |
|
SSL_CTX_set_verify(sslContext, SSL_VERIFY_PEER, _ctxVerify); |
|
} |
|
|
|
/* Check if there is a client certificate file (file containing client authentication |
|
** certificate) specified. If specified, validate and load the certificate. |
*/ | */ |
|
if (certFile != NULL && *certFile != '\0') |
{ | { |
/* load the specified server certificates */ |
int err; |
LOGI_CHAR(("---> SSL: Loading server certificate from: %s", |
|
GetPath(ID_PEMFILE))); |
|
| |
if (SSL_CTX_use_certificate_file(sslContext, |
/* load the specified client certificates */ |
GetPath(ID_PEMFILE), SSL_FILETYPE_PEM) <=0) |
LOGD2((ZT("_CreateSSLContext - Loading server certificate from: %s"), certFile)); |
|
|
|
err = SSL_CTX_use_certificate_file(sslContext, |
|
certFile, |
|
strcmp(certFile + strlen(certFile) - 4, ".pem") == 0 ? SSL_FILETYPE_PEM : SSL_FILETYPE_ASN1); |
|
if (err <= 0) |
{ | { |
LOGE_CHAR(("---> SSL: No server certificate found in %s", |
#if defined(ENABLE_TRACING) |
GetPath(ID_PEMFILE))); |
unsigned long error = ERR_peek_last_error(); |
|
#endif |
|
|
|
LOGE2((ZT("_CreateSSLContext - No client certificate found in %s"), certFile)); |
|
LOGE2((ZT("_CreateSSLContext - OpenSSL Error 0x%lX (%s) in SSL_CTX_use_certificate_file"), error, sslstrerror(error))); |
SSL_CTX_free(sslContext); | SSL_CTX_free(sslContext); |
|
sslContext = NULL; |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
} |
|
| |
/* |
if (privateKeyFile != NULL && *privateKeyFile != '\0') |
** Check if there is a key file (file containing server |
{ |
** private key) specified and the key was not already loaded. |
/* load the specified private key */ |
** If specified, validate and load the key. |
LOGD2((ZT("_CreateSSLContext - SSL Loading client private key from: %s"), privateKeyFile)); |
*/ |
|
|
err = SSL_CTX_use_RSAPrivateKey_file(sslContext, |
|
privateKeyFile, |
|
strcmp(privateKeyFile + strlen(privateKeyFile) - 4, ".pem") == 0 ? SSL_FILETYPE_PEM : SSL_FILETYPE_ASN1); |
|
if (err <= 0) |
{ | { |
/* load the specified server certificates */ |
#if defined(ENABLE_TRACING) |
LOGI_CHAR(("---> SSL: Loading certificate's private key from: %s", |
unsigned long error = ERR_peek_last_error(); |
GetPath(ID_KEYFILE))); |
#endif |
|
LOGE2((ZT("_CreateSSLContext - Invalid private key found in %s"), privateKeyFile)); |
// |
LOGE2((ZT("_CreateSSLContext - OpenSSL error 0x%lX (%s) in SSL_CTX_use_PrivateKey_file"), error, sslstrerror(error))); |
// load given private key and check for validity |
|
// |
|
if (!_verifyPrivateKey(sslContext, GetPath(ID_KEYFILE))) |
|
{ |
|
LOGE_CHAR(( |
|
"---> SSL: No server certificate found in %s", |
|
GetPath(ID_KEYFILE))); |
|
SSL_CTX_free(sslContext); | SSL_CTX_free(sslContext); |
|
sslContext = NULL; |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
} | } |
#endif |
} |
|
|
self->sslContext = sslContext; | self->sslContext = sslContext; |
return MI_RESULT_OK; | return MI_RESULT_OK; |
} | } |
|
|
#endif | #endif |
| |
|
static MI_Result _CreateSocketAndConnect( |
|
Sock* s, |
|
Addr* addr) |
|
{ |
|
MI_Result r; |
|
|
|
LOGD2((ZT("_CreateSocketAndConnect - Begin"))); |
|
|
|
/* Create client socket. */ |
|
r = Sock_Create(s, addr->is_ipv6); |
|
if (r != MI_RESULT_OK) |
|
{ |
|
LOGE2((ZT("_CreateSocketAndConnect - Sock_Create failed. result: %d (%s)"), r, mistrerror(r))); |
|
return r; |
|
} |
|
|
|
/* set the socket to be non-blocking */ |
|
r = Sock_SetBlocking(*s, MI_FALSE); |
|
if (r != MI_RESULT_OK) |
|
{ |
|
LOGE2((ZT("_CreateSocketAndConnect - Sock_SetBlocking failed. result: %d (%s)"), r, mistrerror(r))); |
|
return r; |
|
} |
|
|
|
/* connect the socket to the IP address */ |
|
r = Sock_Connect(*s, addr); |
|
if (r != MI_RESULT_OK) |
|
{ |
|
LOGE2((ZT("_CreateSocketAndConnect - Sock_Connect failed. result: %d (%s)"), r, mistrerror(r))); |
|
return r; |
|
} |
|
|
|
LOGD2((ZT("_CreateSocketAndConnect - OK exit"))); |
|
|
|
return MI_RESULT_WOULD_BLOCK; |
|
} |
|
|
static MI_Result _CreateConnectorSocket( | static MI_Result _CreateConnectorSocket( |
HttpClient* self, | HttpClient* self, |
const char* host, | const char* host, |
|
|
Addr addr; | Addr addr; |
MI_Result r; | MI_Result r; |
Sock s; | Sock s; |
Http_SR_SocketData* h; |
HttpClient_SR_SocketData* h; |
MI_Uint64 currentTimeUsec; | MI_Uint64 currentTimeUsec; |
| |
|
LOGD2((ZT("_CreateConnectorSocket - Begin. host: %s, port: %d, secure? %d"), host, port, secure)); |
|
|
/* timeout calculation */ | /* timeout calculation */ |
if (MI_RESULT_OK != Time_Now(¤tTimeUsec)) |
if (PAL_Time(¤tTimeUsec) != PAL_TRUE) |
|
{ |
|
LOGE2((ZT("_CreateConnectorSocket - PAL_Time failed"))); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
|
} |
| |
// Initialize address. |
/* This code tries to connect using the preferred addressing family (IPv4 */ |
r = Addr_Init(&addr, host, port); |
/* or IPv6). If that fails and Addr_Init has a secondary addressing */ |
if (r != MI_RESULT_OK) |
/* family, a connection with the secondary family, it tries using the */ |
return MI_RESULT_FAILED; |
/* secondary family next. */ |
| |
// Create client socket. |
/* Initialize preferred address */ |
r = Sock_Create(&s); |
r = Addr_Init(&addr, host, port, MI_FALSE); |
if (r != MI_RESULT_OK) | if (r != MI_RESULT_OK) |
{ | { |
Sock_Close(s); |
LOGE2((ZT("_CreateConnectorSocket - Addr_Init failed. result: %d (%s)"), r, mistrerror(r))); |
return MI_RESULT_FAILED; |
return r; |
} | } |
| |
r = Sock_SetBlocking(s, MI_FALSE); |
/* Connect to the server */ |
if (r != MI_RESULT_OK) |
r = _CreateSocketAndConnect(&s, &addr); |
|
if (r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK) |
{ | { |
|
MI_Result r2; |
|
|
Sock_Close(s); | Sock_Close(s); |
return MI_RESULT_FAILED; |
|
} |
|
| |
// Connect to the server. |
LOGW2((ZT("_CreateConnectorSocket - _CreateSocketAndConnect of primary address failed. result: %d (%s)"), r, mistrerror(r))); |
r = Sock_Connect(s, &addr); |
|
if (r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK) |
/* Initialize secondary address */ |
|
r2 = Addr_Init(&addr, host, port, MI_TRUE); |
|
if (r2 != MI_RESULT_OK) |
|
return r; /* on error, return original failure */ |
|
r2 = _CreateSocketAndConnect(&s, &addr); |
|
if (r2 != MI_RESULT_OK && r2 != MI_RESULT_WOULD_BLOCK) |
{ | { |
Sock_Close(s); | Sock_Close(s); |
return MI_RESULT_FAILED; |
|
|
LOGE2((ZT("_CreateConnectorSocket - Addr_Init failed. result: %d (%s)"), r, mistrerror(r))); |
|
|
|
return r; /* on error, return original failure */ |
|
} |
|
r = r2; |
} | } |
| |
/* Create handler */ | /* Create handler */ |
h = (Http_SR_SocketData*)calloc(1, sizeof(Http_SR_SocketData)); |
h = (HttpClient_SR_SocketData*)PAL_Calloc(1, sizeof(HttpClient_SR_SocketData)); |
| |
if (!h) |
if (h == NULL) |
{ | { |
Sock_Close(s); | Sock_Close(s); |
|
LOGE2((ZT("_CreateConnectorSocket - calloc failed"))); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
| |
h->recvBufferSize = INITIAL_BUFFER_SIZE; | h->recvBufferSize = INITIAL_BUFFER_SIZE; |
h->recvBuffer = (char*)calloc(1, h->recvBufferSize); |
h->recvBuffer = (char*)PAL_Calloc(1, h->recvBufferSize); |
if (!h->recvBuffer) | if (!h->recvBuffer) |
{ | { |
free(h); |
PAL_Free(h); |
Sock_Close(s); | Sock_Close(s); |
|
LOGE2((ZT("_CreateConnectorSocket - calloc failed"))); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
| |
|
|
h->timeoutUsec = DEFAULT_HTTP_TIMEOUT_USEC; | h->timeoutUsec = DEFAULT_HTTP_TIMEOUT_USEC; |
h->base.fireTimeoutAt = currentTimeUsec + h->timeoutUsec; | h->base.fireTimeoutAt = currentTimeUsec + h->timeoutUsec; |
| |
/* ssl support */ |
/* SSL support */ |
if (secure) | if (secure) |
{ | { |
h->ssl = SSL_new(self->sslContext); | h->ssl = SSL_new(self->sslContext); |
| |
if (!h->ssl) | if (!h->ssl) |
{ | { |
LOGW((T("ssl_new() failed\n"))); |
LOGW2((ZT("_CreateConnectorSocket - SSL_new failed"))); |
free(h); |
trace_SSLNew_Failed(); |
|
PAL_Free(h); |
Sock_Close(s); | Sock_Close(s); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
| |
|
Sock_SetBlocking(s, MI_TRUE); |
if (!(SSL_set_fd(h->ssl, s) )) | if (!(SSL_set_fd(h->ssl, s) )) |
{ | { |
LOGW((T("ssl_set_fd() failed\n"))); |
LOGW2((ZT("_CreateConnectorSocket - SSL_set_fd failed"))); |
|
trace_SSL_setfd_Failed(); |
SSL_free(h->ssl); | SSL_free(h->ssl); |
free(h); |
PAL_Free(h); |
Sock_Close(s); | Sock_Close(s); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
|
|
| |
if (r != MI_RESULT_OK) | if (r != MI_RESULT_OK) |
{ | { |
LOGW((T("Selector_AddHandler() failed\n"))); |
LOGE2((ZT("_CreateConnectorSocket - Selector_AddHandler failed with error: %d (%s)"), (int)r, mistrerror(r))); |
|
trace_SelectorAddHandler_Failed(); |
if (secure) | if (secure) |
SSL_free(h->ssl); | SSL_free(h->ssl); |
free(h); |
PAL_Free(h); |
Sock_Close(s); | Sock_Close(s); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
| |
self->connector = h; | self->connector = h; |
| |
|
LOGD2((ZT("_CreateConnectorSocket - OK exit. socket: %d, secure: %d, timeout: %s"), h->base.sock, secure, FmtInterval(h->base.fireTimeoutAt - currentTimeUsec))); |
|
|
return MI_RESULT_OK; | return MI_RESULT_OK; |
} | } |
| |
|
|
static MI_Result _New_Http( | static MI_Result _New_Http( |
HttpClient** selfOut, | HttpClient** selfOut, |
Selector* selector, /*optional, maybe NULL*/ | Selector* selector, /*optional, maybe NULL*/ |
|
|
| |
/* Allocate structure */ | /* Allocate structure */ |
{ | { |
self = (HttpClient*)calloc(1, sizeof(HttpClient)); |
self = (HttpClient*)PAL_Calloc(1, sizeof(HttpClient)); |
| |
if (!self) | if (!self) |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
|
|
/* Initialize the selector */ | /* Initialize the selector */ |
if (Selector_Init(&self->internalSelector) != MI_RESULT_OK) | if (Selector_Init(&self->internalSelector) != MI_RESULT_OK) |
{ | { |
free(self); |
PAL_Free(self); |
|
LOGE2((ZT("_NewHttp - Selector_Init failed"))); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
self->selector = &self->internalSelector; | self->selector = &self->internalSelector; |
|
|
if (headers) | if (headers) |
pageSize += _GetHeadersSize(headers); | pageSize += _GetHeadersSize(headers); |
| |
page = (Page*)malloc(pageSize + sizeof(Page)); |
page = (Page*)PAL_Malloc(pageSize + sizeof(Page)); |
| |
if (!page) | if (!page) |
return 0; | return 0; |
|
|
| |
if (r < 0) | if (r < 0) |
{ | { |
free(page); |
PAL_Free(page); |
return 0; | return 0; |
} | } |
| |
|
|
MI_Boolean secure, | MI_Boolean secure, |
HttpClientCallbackOnStatus statusCallback, | HttpClientCallbackOnStatus statusCallback, |
HttpClientCallbackOnResponse responseCallback, | HttpClientCallbackOnResponse responseCallback, |
void* callbackData) |
void* callbackData, |
|
const char* trustedCertsDir, |
|
const char* certFile, |
|
const char* privateKeyFile) |
{ | { |
HttpClient* self; | HttpClient* self; |
MI_Result r; | MI_Result r; |
|
|
responseCallback, callbackData); | responseCallback, callbackData); |
| |
if (MI_RESULT_OK != r) | if (MI_RESULT_OK != r) |
|
{ |
|
LOGE2((ZT("HttpClient_New_Connector - _New_Http failed. result: %d (%s)"), r, mistrerror(r))); |
return r; | return r; |
|
} |
self = *selfOut; | self = *selfOut; |
| |
#ifdef CONFIG_POSIX | #ifdef CONFIG_POSIX |
/* Allocate ssl context */ |
/* Allocate SSL context */ |
if (secure) | if (secure) |
{ | { |
/* init ssl */ | /* init ssl */ |
SSL_library_init(); | SSL_library_init(); |
| |
/* create context */ | /* create context */ |
r = _CreateSSLContext(self); |
r = _CreateSSLContext(self, trustedCertsDir, certFile, privateKeyFile); |
| |
if (r != MI_RESULT_OK) | if (r != MI_RESULT_OK) |
{ | { |
HttpClient_Delete(self); | HttpClient_Delete(self); |
|
*selfOut = NULL; |
|
LOGE2((ZT("HttpClient_New_Connector - _CreateSSLContext failed. result: %d (%s)"), r, mistrerror(r))); |
return r; | return r; |
} | } |
} | } |
|
|
if (r != MI_RESULT_OK) | if (r != MI_RESULT_OK) |
{ | { |
HttpClient_Delete(self); | HttpClient_Delete(self); |
|
LOGE2((ZT("HttpClient_New_Connector - _CreateConnectorSocket failed failed. result: %d (%s)"), r, mistrerror(r))); |
return r; | return r; |
} | } |
} | } |
| |
|
|
return MI_RESULT_OK; | return MI_RESULT_OK; |
} | } |
| |
|
|
| |
/* Check magic number */ | /* Check magic number */ |
if (self->magic != _MAGIC) | if (self->magic != _MAGIC) |
|
{ |
|
LOGE2((ZT("HttpClient_Delete - Bad magic number"))); |
return MI_RESULT_INVALID_PARAMETER; | return MI_RESULT_INVALID_PARAMETER; |
|
} |
| |
if (self->internalSelectorUsed) | if (self->internalSelectorUsed) |
{ | { |
/* Release selector; | /* Release selector; |
Note: selector-destory closes all sockects in a list including connector and listener */ |
Note: Selector_Destroy closes all sockets in a list including connector and listener */ |
Selector_Destroy(self->selector); | Selector_Destroy(self->selector); |
| |
/* Shutdown the network */ | /* Shutdown the network */ |
|
|
self->magic = 0xDDDDDDDD; | self->magic = 0xDDDDDDDD; |
| |
/* Free self pointer */ | /* Free self pointer */ |
free(self); |
PAL_Free(self); |
| |
return MI_RESULT_OK; | return MI_RESULT_OK; |
} | } |
|
|
const HttpClientRequestHeaders* headers, | const HttpClientRequestHeaders* headers, |
Page** data) | Page** data) |
{ | { |
|
LOGD2((ZT("HttpClient_StartRequest - Begin. verb: %s, URI: %s"), verb, uri)); |
| |
/* check params */ | /* check params */ |
if (!self || !uri) | if (!self || !uri) |
|
|
| |
if (self->magic != _MAGIC) | if (self->magic != _MAGIC) |
{ | { |
LOGW((T("start-request: invalid magic!") )); |
LOGE2((ZT("HttpClient_Delete - Bad magic number"))); |
|
trace_StartRequest_InvalidMagic(); |
return MI_RESULT_INVALID_PARAMETER; | return MI_RESULT_INVALID_PARAMETER; |
} | } |
| |
if (!self->connector) |
if (self->connector == NULL) |
{ | { |
LOGW((T("start-request: connection was closed") )); |
LOGE2((ZT("HttpClient_Delete - Connection is not open"))); |
|
trace_StartRequest_ConnectionClosed(); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
| |
|
|
self->connector->sendHeader = | self->connector->sendHeader = |
_CreateHttpHeader(verb, uri, headers, (data && *data) ? (*data)->u.s.size : 0); | _CreateHttpHeader(verb, uri, headers, (data && *data) ? (*data)->u.s.size : 0); |
| |
if (data) |
if (data != NULL) |
{ | { |
self->connector->sendPage = *data; | self->connector->sendPage = *data; |
*data = 0; | *data = 0; |
} | } |
else | else |
{ |
self->connector->sendPage = NULL; |
self->connector->sendPage = 0; |
|
} |
|
| |
/* set status to failed, until we know more details */ | /* set status to failed, until we know more details */ |
self->connector->status = MI_RESULT_FAILED; | self->connector->status = MI_RESULT_FAILED; |
|
|
return MI_RESULT_OK; | return MI_RESULT_OK; |
} | } |
| |
|
|
MI_Result HttpClient_Run( | MI_Result HttpClient_Run( |
HttpClient* self, | HttpClient* self, |
MI_Uint64 timeoutUsec) | MI_Uint64 timeoutUsec) |
{ | { |
/* Run the selector */ | /* Run the selector */ |
return Selector_Run(self->selector, timeoutUsec); |
return Selector_Run(self->selector, timeoutUsec, MI_FALSE); |
} | } |
| |
MI_Result HttpClient_SetTimeout( | MI_Result HttpClient_SetTimeout( |
|
|
{ | { |
MI_Uint64 currentTimeUsec = 0; | MI_Uint64 currentTimeUsec = 0; |
| |
Time_Now(¤tTimeUsec); |
PAL_Time(¤tTimeUsec); |
| |
/* check params */ | /* check params */ |
if (!self) | if (!self) |
|
|
| |
if (self->magic != _MAGIC) | if (self->magic != _MAGIC) |
{ | { |
LOGW((T("setTimeout: invalid magic!") )); |
trace_Timeout_InvalidMagic(); |
return MI_RESULT_INVALID_PARAMETER; | return MI_RESULT_INVALID_PARAMETER; |
} | } |
| |
|
|
| |
return MI_RESULT_OK; | return MI_RESULT_OK; |
} | } |
|
|
|
|