mirror of
https://github.com/Fishwaldo/minihttp.git
synced 2025-03-15 19:41:27 +00:00
experimental (unsafe) SSL support using polarssl/mbedtls. also cmake cleanup.
This commit is contained in:
parent
f71e1cc396
commit
587deb0e0e
5 changed files with 453 additions and 93 deletions
|
@ -2,29 +2,24 @@ cmake_minimum_required(VERSION 2.6)
|
|||
project(minihttp)
|
||||
|
||||
|
||||
if(MSVC)
|
||||
if(NOT CMAKE_C_STANDARD_LIBRARIES MATCHES ".*lws2_32.lib.*")
|
||||
set(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES} ws2_32.lib" CACHE STRING "Default C libs" FORCE)
|
||||
endif()
|
||||
if(NOT CMAKE_CXX_STANDARD_LIBRARIES MATCHES ".*lws2_32.lib.*")
|
||||
set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} ws2_32.lib" CACHE STRING "Default C++ libs" FORCE)
|
||||
endif()
|
||||
else()
|
||||
if(WIN32)
|
||||
if(NOT CMAKE_C_STANDARD_LIBRARIES MATCHES ".*-lws2_32.*")
|
||||
set(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES} -lws2_32" CACHE STRING "Default C libs" FORCE)
|
||||
endif()
|
||||
if(NOT CMAKE_CXX_STANDARD_LIBRARIES MATCHES ".*-lws2_32.*")
|
||||
set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} -lws2_32" CACHE STRING "Default C++ libs" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# non-msvc needs build type - if no build type was provided, set a default one
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build (Debug, Release, RelWithDebInfo, MinSizeRel)" FORCE)
|
||||
endif()
|
||||
option(MINIHTTP_USE_POLARSSL FALSE)
|
||||
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
|
||||
|
||||
set(EXTRA_LIBS "")
|
||||
|
||||
if(WIN32)
|
||||
set(EXTRA_LIBS ${EXTRA_LIBS} ws2_32)
|
||||
endif()
|
||||
|
||||
if(MINIHTTP_USE_POLARSSL)
|
||||
add_definitions(-DMINIHTTP_USE_POLARSSL)
|
||||
find_package(polarssl)
|
||||
include_directories(${POLARSSL_INCLUDE_DIRS})
|
||||
set(EXTRA_LIBS ${EXTRA_LIBS} ${POLARSSL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
add_executable(htt minihttp.cpp minihttp.h main.cpp)
|
||||
target_link_libraries(htt ${EXTRA_LIBS})
|
||||
|
|
28
cmake/Findpolarssl.cmake
Normal file
28
cmake/Findpolarssl.cmake
Normal file
|
@ -0,0 +1,28 @@
|
|||
|
||||
#find_package(PkgConfig)
|
||||
#pkg_check_modules(PC_polarssl QUIET polarssl)
|
||||
#set(POLARSSL_DEFINITIONS ${PC_polarssl_CFLAGS_OTHER})
|
||||
|
||||
find_path(POLARSSL_INCLUDE_DIR ssl_ciphersuites.h ssl.h ssl_ciphersuites.h
|
||||
HINTS $ENV{POLARSSL_DIR} $ENV{MBEDTLS_DIR}
|
||||
PATH_SUFFIXES include/polarssl include
|
||||
PATHS ~/Library/Frameworks /Library/Frameworks /usr/local /usr /sw /opt/local /opt/csw /opt
|
||||
)
|
||||
find_library(POLARSSL_LIBRARY
|
||||
NAMES polarssl mbedtls
|
||||
HINTS $ENV{POLARSSL_DIR} $ENV{MBEDTLS_DIR}
|
||||
PATH_SUFFIXES lib64 lib
|
||||
PATHS ~/Library/Frameworks /Library/Frameworks /usr/local /usr /sw /opt/local /opt/csw /opt
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_package_handle_standard_args(polarssl DEFAULT_MSG POLARSSL_LIBRARY POLARSSL_INCLUDE_DIR)
|
||||
|
||||
if(POLARSSL_FOUND)
|
||||
set(POLARSSL_INCLUDE_DIRS "${POLARSSL_INCLUDE_DIR}" CACHE STRING "" FORCE)
|
||||
set(POLARSSL_LIBRARIES "${POLARSSL_LIBRARY}" CACHE STRING "" FORCE)
|
||||
endif(POLARSSL_FOUND)
|
||||
|
||||
mark_as_advanced(POLARSSL_INCLUDE_DIR POLARSSL_INCLUDE_DIRS)
|
||||
mark_as_advanced(POLARSSL_LIBRARY POLARSSL_LIBRARIES)
|
11
main.cpp
11
main.cpp
|
@ -22,6 +22,8 @@ protected:
|
|||
virtual void _OnOpen()
|
||||
{
|
||||
puts("_OnOpen()");
|
||||
minihttp::SSLResult sr = verifySSL();
|
||||
printf("SSL status flags (0 is good): 0x%x\n", sr);
|
||||
minihttp::HttpSocket::_OnOpen();
|
||||
}
|
||||
|
||||
|
@ -30,7 +32,7 @@ protected:
|
|||
printf("_OnRequestDone(): %s\n", GetCurrentRequest().resource.c_str());
|
||||
}
|
||||
|
||||
virtual void _OnRecv(char *buf, unsigned int size)
|
||||
virtual void _OnRecv(void *buf, unsigned int size)
|
||||
{
|
||||
if(!size)
|
||||
return;
|
||||
|
@ -48,9 +50,10 @@ int main(int argc, char *argv[])
|
|||
ht->SetKeepAlive(3);
|
||||
|
||||
ht->SetBufsizeIn(64 * 1024);
|
||||
|
||||
ht->Download("http://www.ietf.org/rfc/rfc2616.txt");
|
||||
ht->Download("http://example.com"); // Queue another one
|
||||
//ht->Download("http://www.ietf.org/rfc/rfc2616.txt");
|
||||
//ht->Download("http://example.com"); // Queue another one
|
||||
//ht->Download("https://example.com"); // SSL connection
|
||||
ht->Download("raw.githubusercontent.com/fgenesis/minihttp/master/minihttp.h"); // transparent HTTP -> HTTPS redirection
|
||||
|
||||
minihttp::SocketSet ss;
|
||||
|
||||
|
|
430
minihttp.cpp
430
minihttp.cpp
|
@ -39,6 +39,14 @@
|
|||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef MINIHTTP_USE_POLARSSL
|
||||
# include "polarssl/net.h"
|
||||
# include "polarssl/ssl.h"
|
||||
# include "polarssl/entropy.h"
|
||||
# include "polarssl/ctr_drbg.h"
|
||||
#endif
|
||||
|
||||
#include "minihttp.h"
|
||||
|
||||
|
@ -58,6 +66,71 @@
|
|||
|
||||
namespace minihttp {
|
||||
|
||||
#ifdef MINIHTTP_USE_POLARSSL
|
||||
// ------------------------ SSL STUFF -------------------------
|
||||
bool HasSSL() { return true; }
|
||||
|
||||
struct SSLCtx
|
||||
{
|
||||
SSLCtx() : _inited(0)
|
||||
{
|
||||
entropy_init(&entropy);
|
||||
x509_crt_init(&cacert);
|
||||
memset(&ssl, 0, sizeof(ssl_context));
|
||||
}
|
||||
~SSLCtx()
|
||||
{
|
||||
entropy_free(&entropy);
|
||||
x509_crt_free(&cacert);
|
||||
ssl_free(&ssl);
|
||||
if(_inited & 1)
|
||||
ctr_drbg_free(&ctr_drbg);
|
||||
if(_inited & 2)
|
||||
ssl_free(&ssl);
|
||||
}
|
||||
bool init()
|
||||
{
|
||||
const char *pers = "minihttp";
|
||||
const size_t perslen = strlen(pers);
|
||||
|
||||
int err = ctr_drbg_init(&ctr_drbg, entropy_func, &entropy, (unsigned char *)pers, perslen);
|
||||
if(err)
|
||||
{
|
||||
traceprint("SSLCtx::init(): ctr_drbg_init() returned %d\n", err);
|
||||
return false;
|
||||
}
|
||||
_inited |= 1;
|
||||
|
||||
err = ssl_init(&ssl);
|
||||
if(err)
|
||||
{
|
||||
traceprint("SSLCtx::init(): ssl_init() returned %d\n", err);
|
||||
return false;
|
||||
}
|
||||
_inited |= 2;
|
||||
|
||||
return true;
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
ssl_session_reset(&ssl);
|
||||
}
|
||||
|
||||
entropy_context entropy;
|
||||
ctr_drbg_context ctr_drbg;
|
||||
ssl_context ssl;
|
||||
x509_crt cacert;
|
||||
|
||||
private:
|
||||
unsigned _inited;
|
||||
};
|
||||
|
||||
|
||||
// ------------------------------------------------------------
|
||||
#else// MINIHTTP_USE_POLARSSL
|
||||
bool HasSSL() { return false; }
|
||||
#endif
|
||||
|
||||
#define DEFAULT_BUFSIZE 4096
|
||||
|
||||
inline int _GetError()
|
||||
|
@ -71,14 +144,19 @@ inline int _GetError()
|
|||
|
||||
inline std::string _GetErrorStr(int e)
|
||||
{
|
||||
std::string ret;
|
||||
#ifdef _WIN32
|
||||
LPTSTR s;
|
||||
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, e, 0, (LPTSTR)&s, 0, NULL);
|
||||
std::string ret = s;
|
||||
if(s)
|
||||
ret = s;
|
||||
::LocalFree(s);
|
||||
return ret;
|
||||
#else
|
||||
const char *s = strerror(e);
|
||||
if(s)
|
||||
ret = s;
|
||||
#endif
|
||||
return strerror(e);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool InitNetwork()
|
||||
|
@ -133,18 +211,27 @@ static bool _Resolve(const char *host, unsigned int port, struct sockaddr_in *ad
|
|||
// FIXME: this does currently not handle links like:
|
||||
// http://example.com/index.html#pos
|
||||
|
||||
bool SplitURI(const std::string& uri, std::string& host, std::string& file, int& port)
|
||||
bool SplitURI(const std::string& uri, std::string& host, std::string& file, int& port, bool& useSSL)
|
||||
{
|
||||
const char *p = uri.c_str();
|
||||
const char *sl = strstr(p, "//");
|
||||
unsigned int offs = 0;
|
||||
bool ssl = false;
|
||||
if(sl)
|
||||
{
|
||||
offs = 7;
|
||||
if(strncmp(p, "http://", offs))
|
||||
if(strncmp(p, "http://", 7) == 0)
|
||||
offs = 7;
|
||||
else if(strncmp(p, "https://", 8) == 0)
|
||||
{
|
||||
ssl = true;
|
||||
offs = 8;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
p = sl + 2;
|
||||
}
|
||||
|
||||
sl = strchr(p, '/');
|
||||
if(!sl)
|
||||
{
|
||||
|
@ -164,6 +251,7 @@ bool SplitURI(const std::string& uri, std::string& host, std::string& file, int&
|
|||
port = atoi(host.c_str() + colon);
|
||||
host.erase(port);
|
||||
}
|
||||
useSSL = ssl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -172,7 +260,12 @@ static bool _SetNonBlocking(SOCKET s, bool nonblock)
|
|||
{
|
||||
if(!SOCKETVALID(s))
|
||||
return false;
|
||||
#ifdef _WIN32
|
||||
#ifdef MINIHTTP_USE_POLARSSL
|
||||
if(nonblock)
|
||||
return net_set_nonblock(s) == 0;
|
||||
else
|
||||
return net_set_block(s) == 0;
|
||||
#elif defined(_WIN32)
|
||||
ULONG tmp = !!nonblock;
|
||||
if(::ioctlsocket(s, FIONBIO, &tmp) == SOCKET_ERROR)
|
||||
return false;
|
||||
|
@ -188,7 +281,7 @@ static bool _SetNonBlocking(SOCKET s, bool nonblock)
|
|||
|
||||
TcpSocket::TcpSocket()
|
||||
: _s(INVALID_SOCKET), _inbuf(NULL), _inbufSize(0), _recvSize(0),
|
||||
_readptr(NULL), _lastport(0)
|
||||
_readptr(NULL), _lastport(0), _sslctx(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -197,6 +290,9 @@ TcpSocket::~TcpSocket()
|
|||
close();
|
||||
if(_inbuf)
|
||||
free(_inbuf);
|
||||
#ifdef MINIHTTP_USE_POLARSSL
|
||||
shutdownSSL();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool TcpSocket::isOpen(void)
|
||||
|
@ -211,11 +307,18 @@ void TcpSocket::close(void)
|
|||
|
||||
_OnCloseInternal();
|
||||
|
||||
#ifdef _WIN32
|
||||
::closesocket((SOCKET)_s);
|
||||
#ifdef MINIHTTP_USE_POLARSSL
|
||||
if(_sslctx)
|
||||
((SSLCtx*)_sslctx)->reset();
|
||||
net_close(_s);
|
||||
#else
|
||||
# ifdef _WIN32
|
||||
::closesocket((SOCKET)_s);
|
||||
# else
|
||||
::close(_s);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
_s = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
|
@ -241,33 +344,18 @@ void TcpSocket::SetBufsizeIn(unsigned int s)
|
|||
_readptr = _writeptr = _inbuf;
|
||||
}
|
||||
|
||||
bool TcpSocket::open(const char *host /* = NULL */, unsigned int port /* = 0 */)
|
||||
static bool _openSocket(SOCKET *ps, const char *host, unsigned port)
|
||||
{
|
||||
if(isOpen())
|
||||
#ifdef MINIHTTP_USE_POLARSSL
|
||||
int s;
|
||||
int err = net_connect(&s, host, port);
|
||||
if(err)
|
||||
{
|
||||
if( (host && host != _host) || (port && port != _lastport) )
|
||||
close();
|
||||
// ... and continue connecting to new host/port
|
||||
else
|
||||
return true; // still connected, to same host and port.
|
||||
traceprint("open_ssl: net_connect(%s, %u) returned %d\n", host, port, err);
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
sockaddr_in addr;
|
||||
|
||||
if(host)
|
||||
_host = host;
|
||||
else
|
||||
host = _host.c_str();
|
||||
|
||||
if(port)
|
||||
_lastport = port;
|
||||
else
|
||||
{
|
||||
port = _lastport;
|
||||
if(!port)
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!_Resolve(host, port, &addr))
|
||||
{
|
||||
traceprint("RESOLV ERROR: %s\n", _GetErrorStr(_GetError()).c_str());
|
||||
|
@ -287,22 +375,222 @@ bool TcpSocket::open(const char *host /* = NULL */, unsigned int port /* = 0 */)
|
|||
traceprint("CONNECT ERROR: %s\n", _GetErrorStr(_GetError()).c_str());
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
_SetNonBlocking(s, _nonblocking); // restore setting if it was set in invalid state. static call because _s is intentionally still invalid here.
|
||||
_s = s; // set the socket handle when we are really sure we are connected, and things are set up
|
||||
*ps = s;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef MINIHTTP_USE_POLARSSL
|
||||
void traceprint_ssl(void *ctx, int level, const char *str )
|
||||
{
|
||||
(void)ctx;
|
||||
printf("ssl: [%d] %s\n", level, str);
|
||||
}
|
||||
static bool _openSSL(void *ps, SSLCtx *ctx)
|
||||
{
|
||||
ssl_set_endpoint(&ctx->ssl, SSL_IS_CLIENT);
|
||||
ssl_set_authmode(&ctx->ssl, SSL_VERIFY_OPTIONAL);
|
||||
ssl_set_ca_chain(&ctx->ssl, &ctx->cacert, NULL, NULL);
|
||||
|
||||
/* SSLv3 is deprecated, set minimum to TLS 1.0 */
|
||||
ssl_set_min_version(&ctx->ssl, SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1);
|
||||
/* RC4 is deprecated, disable it */
|
||||
ssl_set_arc4_support(&ctx->ssl, SSL_ARC4_DISABLED );
|
||||
|
||||
ssl_set_rng(&ctx->ssl, ctr_drbg_random, &ctx->ctr_drbg);
|
||||
ssl_set_dbg(&ctx->ssl, traceprint_ssl, NULL);
|
||||
//ssl_set_ciphersuites( &ctx->ssl, ssl_default_ciphersuites); // FIXME
|
||||
ssl_set_bio(&ctx->ssl, net_recv, ps, net_send, ps);
|
||||
|
||||
int err;
|
||||
while( (err = ssl_handshake(&ctx->ssl)) )
|
||||
{
|
||||
if(err != POLARSSL_ERR_NET_WANT_READ && err != POLARSSL_ERR_NET_WANT_WRITE)
|
||||
{
|
||||
traceprint("open_ssl: ssl_handshake returned -0x%x\n\n", -err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool TcpSocket::open(const char *host /* = NULL */, unsigned int port /* = 0 */)
|
||||
{
|
||||
if(isOpen())
|
||||
{
|
||||
if( (host && host != _host) || (port && port != _lastport) )
|
||||
close();
|
||||
// ... and continue connecting to new host/port
|
||||
else
|
||||
return true; // still connected, to same host and port.
|
||||
}
|
||||
|
||||
if(host)
|
||||
_host = host;
|
||||
else
|
||||
host = _host.c_str();
|
||||
|
||||
if(port)
|
||||
_lastport = port;
|
||||
else
|
||||
{
|
||||
port = _lastport;
|
||||
if(!port)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
assert(!SOCKETVALID(_s));
|
||||
|
||||
{
|
||||
SOCKET s;
|
||||
if(!_openSocket(&s, host, port))
|
||||
return false;
|
||||
_s = s;
|
||||
}
|
||||
|
||||
_SetNonBlocking(_s, _nonblocking); // restore setting if it was set in invalid state. static call because _s is intentionally still invalid here.
|
||||
|
||||
#ifdef MINIHTTP_USE_POLARSSL
|
||||
if(_sslctx)
|
||||
if(!_openSSL(&_s, (SSLCtx*)_sslctx))
|
||||
{
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
_OnOpen();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TcpSocket::SendBytes(const char *str, unsigned int len)
|
||||
#ifdef MINIHTTP_USE_POLARSSL
|
||||
void TcpSocket::shutdownSSL()
|
||||
{
|
||||
delete ((SSLCtx*)_sslctx);
|
||||
_sslctx = NULL;
|
||||
}
|
||||
|
||||
bool TcpSocket::initSSL(const char *certs)
|
||||
{
|
||||
SSLCtx *ctx = (SSLCtx*)_sslctx;
|
||||
if(ctx)
|
||||
ctx->reset();
|
||||
else
|
||||
{
|
||||
ctx = new SSLCtx();
|
||||
_sslctx = ctx;
|
||||
if(!ctx->init())
|
||||
{
|
||||
shutdownSSL();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(certs)
|
||||
{
|
||||
int err = x509_crt_parse(&ctx->cacert, (const unsigned char*)certs, strlen(certs));
|
||||
if(err)
|
||||
{
|
||||
shutdownSSL();
|
||||
traceprint("x509_crt_parse() returned %d\n", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SSLResult TcpSocket::verifySSL()
|
||||
{
|
||||
if(!_sslctx)
|
||||
return SSLR_NO_SSL;
|
||||
|
||||
SSLCtx *ctx = (SSLCtx*)_sslctx;
|
||||
unsigned r = SSLR_OK;
|
||||
int res = ssl_get_verify_result(&ctx->ssl);
|
||||
if(res)
|
||||
{
|
||||
if(res & BADCERT_EXPIRED)
|
||||
r |= SSLR_CERT_EXPIRED;
|
||||
|
||||
if(res & BADCERT_REVOKED)
|
||||
r |= SSLR_CERT_REVOKED;
|
||||
|
||||
if(res & BADCERT_CN_MISMATCH)
|
||||
r |= SSLR_CERT_CN_MISMATCH;
|
||||
|
||||
if(res & BADCERT_NOT_TRUSTED)
|
||||
r |= SSLR_CERT_NOT_TRUSTED;
|
||||
|
||||
if(res & BADCERT_MISSING)
|
||||
r |= SSLR_CERT_MISSING;
|
||||
|
||||
if(res & BADCERT_SKIP_VERIFY)
|
||||
r |= SSLR_CERT_SKIP_VERIFY;
|
||||
|
||||
if(res & BADCERT_FUTURE)
|
||||
r |= SSLR_CERT_FUTURE;
|
||||
|
||||
// More than just this?
|
||||
if(res != BADCERT_SKIP_VERIFY)
|
||||
r |= SSLR_FAIL;
|
||||
}
|
||||
|
||||
return (SSLResult)r;
|
||||
}
|
||||
#else // MINIHTTP_USE_POLARSSL
|
||||
void TcpSocket::shutdownSSL() {}
|
||||
bool TcpSocket::initSSL(const char *certs) { return false; }
|
||||
SSLResult TcpSocket::verifySSL() { return SSLR_NO_SSL; }
|
||||
#endif
|
||||
|
||||
bool TcpSocket::SendBytes(const void *str, unsigned int len)
|
||||
{
|
||||
if(!SOCKETVALID(_s))
|
||||
return false;
|
||||
//traceprint("SEND: '%s'\n", str);
|
||||
return ::send(_s, str, len, 0) >= 0;
|
||||
// TODO: check _GetError()
|
||||
|
||||
unsigned written = 0;
|
||||
while(true) // FIXME: buffer bytes to an internal queue instead?
|
||||
{
|
||||
int ret = _writeBytes((const unsigned char*)str + written, len - written);
|
||||
if(ret >= 0)
|
||||
{
|
||||
assert((unsigned)ret <= len);
|
||||
written += (unsigned)ret;
|
||||
if(written >= len)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef MINIHTTP_USE_POLARSSL
|
||||
if(ret == POLARSSL_ERR_NET_WANT_WRITE) // FIXME: wait? queue? try later?
|
||||
continue;
|
||||
#endif
|
||||
traceprint("SendBytes: error %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
assert(written == len);
|
||||
return true;
|
||||
}
|
||||
|
||||
int TcpSocket::_writeBytes(const unsigned char *buf, size_t len)
|
||||
{
|
||||
#ifdef MINIHTTP_USE_POLARSSL
|
||||
if(_sslctx)
|
||||
return ssl_write(&((SSLCtx*)_sslctx)->ssl, buf, len);
|
||||
else
|
||||
return net_send(&_s, buf, len);
|
||||
#else
|
||||
return ::send(_s, buf, len, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void TcpSocket::_ShiftBuffer(void)
|
||||
|
@ -319,6 +607,18 @@ void TcpSocket::_OnData()
|
|||
_OnRecv(_readptr, _recvSize);
|
||||
}
|
||||
|
||||
int TcpSocket::_readBytes(unsigned char *buf, size_t maxlen)
|
||||
{
|
||||
#ifdef MINIHTTP_USE_POLARSSL
|
||||
if(_sslctx)
|
||||
return ssl_read(&((SSLCtx*)_sslctx)->ssl, buf, maxlen);
|
||||
else
|
||||
return net_recv(&_s, buf, maxlen);
|
||||
#else
|
||||
return recv(_s, buf, maxlen, 0); // last char is used as string terminator
|
||||
#endif
|
||||
}
|
||||
|
||||
bool TcpSocket::update(void)
|
||||
{
|
||||
if(!_OnUpdate())
|
||||
|
@ -330,8 +630,7 @@ bool TcpSocket::update(void)
|
|||
if(!_inbuf)
|
||||
SetBufsizeIn(DEFAULT_BUFSIZE);
|
||||
|
||||
int bytes = recv(_s, _writeptr, _writeSize, 0); // last char is used as string terminator
|
||||
|
||||
int bytes = _readBytes((unsigned char*)_writeptr, _writeSize);
|
||||
if(bytes > 0) // we received something
|
||||
{
|
||||
_inbuf[bytes] = 0;
|
||||
|
@ -342,36 +641,38 @@ bool TcpSocket::update(void)
|
|||
_readptr = _writeptr = _inbuf;
|
||||
|
||||
_OnData();
|
||||
return true;
|
||||
}
|
||||
else if(bytes == 0) // remote has closed the connection
|
||||
{
|
||||
_recvSize = 0;
|
||||
close();
|
||||
return true;
|
||||
}
|
||||
else // whoops, error?
|
||||
{
|
||||
int e = _GetError();
|
||||
switch(e)
|
||||
{
|
||||
case EWOULDBLOCK:
|
||||
#if defined(EAGAIN) && (EWOULDBLOCK != EAGAIN)
|
||||
case EAGAIN: // linux man pages say this can also happen instead of EWOULDBLOCK
|
||||
#endif
|
||||
return false;
|
||||
|
||||
default:
|
||||
traceprint("SOCKET UPDATE ERROR: (%d): %s\n", e, _GetErrorStr(e).c_str());
|
||||
case ECONNRESET:
|
||||
case ENOTCONN:
|
||||
case ETIMEDOUT:
|
||||
#ifdef _WIN32
|
||||
case WSAECONNABORTED:
|
||||
case WSAESHUTDOWN:
|
||||
#endif
|
||||
#ifdef MINIHTTP_USE_POLARSSL
|
||||
case 0: // no error from the network API, but notification that the SSL connection was terminated
|
||||
#endif
|
||||
close();
|
||||
break;
|
||||
|
||||
case EWOULDBLOCK:
|
||||
#if defined(EAGAIN) && (EWOULDBLOCK != EAGAIN)
|
||||
case EAGAIN: // linux man pages say this can also happen instead of EWOULDBLOCK
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
traceprint("SOCKET UPDATE ERROR: (%d): %s\n", e, _GetErrorStr(e).c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -429,9 +730,9 @@ bool HttpSocket::Download(const std::string& url, void *user /* = NULL */)
|
|||
{
|
||||
Request req;
|
||||
req.user = user;
|
||||
SplitURI(url, req.host, req.resource, req.port);
|
||||
SplitURI(url, req.host, req.resource, req.port, req.useSSL);
|
||||
if(req.port < 0)
|
||||
req.port = 80;
|
||||
req.port = req.useSSL ? 443 : 80;
|
||||
return SendGet(req, false);
|
||||
}
|
||||
|
||||
|
@ -512,6 +813,11 @@ bool HttpSocket::_OpenRequest(const Request& req)
|
|||
traceprint("HttpSocket::_OpenRequest(): _inProgress == true, should not be called.");
|
||||
return false;
|
||||
}
|
||||
if(req.useSSL && !hasSSL())
|
||||
{
|
||||
traceprint("HttpSocket::_OpenRequest(): Is an SSL connection, but SSL was not inited, doing that now\n");
|
||||
initSSL(NULL); // FIXME: supply cert list?
|
||||
}
|
||||
if(!open(req.host.c_str(), req.port))
|
||||
return false;
|
||||
_inProgress = true;
|
||||
|
@ -672,16 +978,16 @@ bool HttpSocket::_HandleStatus()
|
|||
|
||||
bool HttpSocket::IsRedirecting() const
|
||||
{
|
||||
switch(_status)
|
||||
{
|
||||
case 301:
|
||||
case 302:
|
||||
case 303:
|
||||
case 307:
|
||||
case 308:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
switch(_status)
|
||||
{
|
||||
case 301:
|
||||
case 302:
|
||||
case 303:
|
||||
case 307:
|
||||
case 308:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -769,7 +1075,7 @@ void HttpSocket::_OnClose()
|
|||
_FinishRequest();
|
||||
}
|
||||
|
||||
void HttpSocket::_OnRecvInternal(char *buf, unsigned int size)
|
||||
void HttpSocket::_OnRecvInternal(void *buf, unsigned int size)
|
||||
{
|
||||
if(_status == 200 || _alwaysHandle)
|
||||
_OnRecv(buf, size);
|
||||
|
|
38
minihttp.h
38
minihttp.h
|
@ -22,9 +22,25 @@ namespace minihttp
|
|||
|
||||
bool InitNetwork();
|
||||
void StopNetwork();
|
||||
bool HasSSL();
|
||||
|
||||
bool SplitURI(const std::string& uri, std::string& host, std::string& file, int& port);
|
||||
|
||||
enum SSLResult
|
||||
{
|
||||
SSLR_OK = 0x0,
|
||||
SSLR_NO_SSL = 0x1,
|
||||
SSLR_FAIL = 0x2,
|
||||
SSLR_CERT_EXPIRED = 0x4,
|
||||
SSLR_CERT_REVOKED = 0x8,
|
||||
SSLR_CERT_CN_MISMATCH = 0x10,
|
||||
SSLR_CERT_NOT_TRUSTED = 0x20,
|
||||
SSLR_CERT_MISSING = 0x40,
|
||||
SSLR_CERT_SKIP_VERIFY = 0x80,
|
||||
SSLR_CERT_FUTURE = 0x100,
|
||||
|
||||
_SSLR_FORCE32BIT = 0x7fffffff
|
||||
};
|
||||
|
||||
class TcpSocket
|
||||
{
|
||||
|
@ -44,13 +60,19 @@ public:
|
|||
bool SetNonBlocking(bool nonblock);
|
||||
unsigned int GetBufSize() { return _inbufSize; }
|
||||
const char *GetHost(void) { return _host.c_str(); }
|
||||
bool SendBytes(const char *str, unsigned int len);
|
||||
bool SendBytes(const void *buf, unsigned int len);
|
||||
|
||||
// SSL related
|
||||
bool initSSL(const char *certs);
|
||||
bool hasSSL() const { return !!_sslctx; }
|
||||
void shutdownSSL();
|
||||
SSLResult verifySSL();
|
||||
|
||||
protected:
|
||||
virtual void _OnCloseInternal();
|
||||
virtual void _OnData(); // data received callback. Internal, should only be overloaded to call _OnRecv()
|
||||
|
||||
virtual void _OnRecv(char *buf, unsigned int size) = 0;
|
||||
virtual void _OnRecv(void *buf, unsigned int size) = 0;
|
||||
virtual void _OnClose() {}; // close callback
|
||||
virtual void _OnOpen() {} // called when opened
|
||||
virtual bool _OnUpdate() { return true; } // called before reading from the socket
|
||||
|
@ -72,6 +94,11 @@ protected:
|
|||
intptr_t _s; // socket handle. really an int, but to be sure its 64 bit compatible as it seems required on windows, we use this.
|
||||
|
||||
std::string _host;
|
||||
|
||||
private:
|
||||
int _writeBytes(const unsigned char *buf, size_t len);
|
||||
int _readBytes(unsigned char *buf, size_t maxlen);
|
||||
void *_sslctx;
|
||||
};
|
||||
|
||||
} // end namespace minihttp
|
||||
|
@ -97,13 +124,14 @@ struct Request
|
|||
{
|
||||
Request() : port(80), user(NULL) {}
|
||||
Request(const std::string& h, const std::string& res, int p = 80, void *u = NULL)
|
||||
: host(h), resource(res), port(80), user(u) {}
|
||||
: host(h), resource(res), port(80), user(u), useSSL(false) {}
|
||||
|
||||
std::string host;
|
||||
std::string header; // set by socket
|
||||
std::string resource;
|
||||
int port;
|
||||
void *user;
|
||||
bool useSSL;
|
||||
};
|
||||
|
||||
class HttpSocket : public TcpSocket
|
||||
|
@ -145,7 +173,7 @@ protected:
|
|||
virtual void _OnCloseInternal();
|
||||
virtual void _OnClose();
|
||||
virtual void _OnData(); // data received callback. Internal, should only be overloaded to call _OnRecv()
|
||||
virtual void _OnRecv(char *buf, unsigned int size) = 0;
|
||||
virtual void _OnRecv(void *buf, unsigned int size) = 0;
|
||||
virtual void _OnOpen(); // called when opene
|
||||
virtual bool _OnUpdate(); // called before reading from the socket
|
||||
|
||||
|
@ -160,7 +188,7 @@ protected:
|
|||
void _ParseHeaderFields(const char *s, size_t size);
|
||||
bool _HandleStatus(); // Returns whether the processed request was successful, or not
|
||||
void _FinishRequest();
|
||||
void _OnRecvInternal(char *buf, unsigned int size);
|
||||
void _OnRecvInternal(void *buf, unsigned int size);
|
||||
|
||||
std::string _user_agent;
|
||||
std::string _accept_encoding; // Default empty.
|
||||
|
|
Loading…
Add table
Reference in a new issue