2012-06-01 15:23:19 +00:00
|
|
|
/* This program is free software. It comes without any warranty, to
|
|
|
|
* the extent permitted by applicable law. You can redistribute it
|
|
|
|
* and/or modify it under the terms of the Do What The Fuck You Want
|
|
|
|
* To Public License, Version 2, as published by Sam Hocevar.
|
|
|
|
* See http://sam.zoy.org/wtfpl/COPYING for more details. */
|
|
|
|
|
|
|
|
#ifndef MINIHTTPSOCKET_H
|
|
|
|
#define MINIHTTPSOCKET_H
|
|
|
|
|
|
|
|
// ---- Compile config -----
|
|
|
|
#define MINIHTTP_SUPPORT_HTTP
|
|
|
|
#define MINIHTTP_SUPPORT_SOCKET_SET
|
|
|
|
// -------------------------
|
|
|
|
|
2017-01-13 12:06:31 +00:00
|
|
|
#include <stdlib.h>
|
2012-06-01 15:23:19 +00:00
|
|
|
#include <string>
|
2017-01-13 12:06:31 +00:00
|
|
|
// Intentionally avoid pulling in any other headers
|
2012-06-18 12:08:21 +00:00
|
|
|
|
2012-06-01 15:23:19 +00:00
|
|
|
namespace minihttp
|
|
|
|
{
|
|
|
|
|
2017-01-13 12:06:31 +00:00
|
|
|
class POST;
|
|
|
|
|
2012-06-01 15:23:19 +00:00
|
|
|
bool InitNetwork();
|
|
|
|
void StopNetwork();
|
2017-01-13 12:06:31 +00:00
|
|
|
bool HasSSL();
|
2012-06-01 15:23:19 +00:00
|
|
|
|
2017-01-13 12:06:31 +00:00
|
|
|
// Simple one-shot API to download stuff via HTTP(S).
|
|
|
|
// Blocks while waiting until all data have arrived.
|
|
|
|
// Optionally, pass a size_t pointer to get the received memory block size (excluding the added zero-terminator).
|
|
|
|
// Optionally, pass a pointer to POST data to send a POST request instead of a GET request.
|
|
|
|
// Returns a pointer to a zero-terminated memory block on success, NULL on failure.
|
|
|
|
// The returned pointer must be free()'d after use.
|
|
|
|
// Note: Avoid using this function if possible. It has no safeguards against a malicious web server!
|
|
|
|
// E.g. A server that sends one byte every few seconds but keeping the connection intact
|
|
|
|
// is perfectly capable of stalling the caller for a VERY LONG TIME.
|
|
|
|
char *Download(const char *url, size_t *sz = NULL, const POST *post = NULL);
|
2012-06-01 15:23:19 +00:00
|
|
|
|
2017-01-13 12:06:31 +00:00
|
|
|
// append to enc
|
|
|
|
void URLEncode(const std::string& s, std::string& enc);
|
|
|
|
|
|
|
|
bool SplitURI(const std::string& uri, std::string& protocol, std::string& host, std::string& file, int& port, bool& useSSL);
|
|
|
|
|
|
|
|
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
|
|
|
|
};
|
2012-06-01 15:23:19 +00:00
|
|
|
|
|
|
|
class TcpSocket
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
TcpSocket();
|
|
|
|
virtual ~TcpSocket();
|
|
|
|
|
|
|
|
virtual bool HasPendingTask() const { return false; }
|
|
|
|
|
|
|
|
bool open(const char *addr = NULL, unsigned int port = 0);
|
|
|
|
void close();
|
|
|
|
bool update(); // returns true if something interesting happened (incoming data, closed connection, etc)
|
|
|
|
|
|
|
|
bool isOpen(void);
|
|
|
|
|
|
|
|
void SetBufsizeIn(unsigned int s);
|
|
|
|
bool SetNonBlocking(bool nonblock);
|
|
|
|
unsigned int GetBufSize() { return _inbufSize; }
|
|
|
|
const char *GetHost(void) { return _host.c_str(); }
|
2017-01-13 12:06:31 +00:00
|
|
|
bool SendBytes(const void *buf, unsigned int len);
|
|
|
|
|
|
|
|
// SSL related
|
|
|
|
bool initSSL(const char *certs);
|
|
|
|
bool hasSSL() const { return !!_sslctx; }
|
|
|
|
void shutdownSSL();
|
|
|
|
SSLResult verifySSL(char *buf = 0, unsigned buflen = 0); // optionally put info string into buf
|
2012-06-01 15:23:19 +00:00
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual void _OnCloseInternal();
|
|
|
|
virtual void _OnData(); // data received callback. Internal, should only be overloaded to call _OnRecv()
|
|
|
|
|
2017-01-13 12:06:31 +00:00
|
|
|
virtual void _OnRecv(void *buf, unsigned int size) = 0;
|
2012-06-01 15:23:19 +00:00
|
|
|
virtual void _OnClose() {}; // close callback
|
|
|
|
virtual void _OnOpen() {} // called when opened
|
|
|
|
virtual bool _OnUpdate() { return true; } // called before reading from the socket
|
|
|
|
|
|
|
|
void _ShiftBuffer();
|
|
|
|
|
|
|
|
char *_inbuf;
|
|
|
|
char *_readptr; // part of inbuf, optionally skipped header
|
|
|
|
char *_writeptr; // passed to recv(). usually equal to _inbuf, but may point inside the buffer in case of a partial transfer.
|
|
|
|
|
|
|
|
unsigned int _inbufSize; // size of internal buffer
|
|
|
|
unsigned int _writeSize; // how many bytes can be written to _writeptr;
|
|
|
|
unsigned int _recvSize; // incoming size, max _inbufSize - 1
|
|
|
|
|
|
|
|
unsigned int _lastport; // port used in last open() call
|
|
|
|
|
|
|
|
bool _nonblocking; // Default true. If false, the current thread is blocked while waiting for input.
|
|
|
|
|
2017-01-13 12:06:31 +00:00
|
|
|
#ifdef _WIN32
|
2012-06-01 15:23:19 +00:00
|
|
|
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.
|
2017-01-13 12:06:31 +00:00
|
|
|
#else
|
|
|
|
long _s;
|
|
|
|
#endif
|
2012-06-01 15:23:19 +00:00
|
|
|
|
|
|
|
std::string _host;
|
2017-01-13 12:06:31 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
int _writeBytes(const unsigned char *buf, size_t len);
|
|
|
|
int _readBytes(unsigned char *buf, size_t maxlen);
|
|
|
|
void *_sslctx;
|
2012-06-01 15:23:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
} // end namespace minihttp
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#ifdef MINIHTTP_SUPPORT_HTTP
|
|
|
|
|
|
|
|
#include <map>
|
|
|
|
#include <queue>
|
|
|
|
|
|
|
|
namespace minihttp
|
|
|
|
{
|
|
|
|
|
|
|
|
enum HttpCode
|
|
|
|
{
|
|
|
|
HTTP_OK = 200,
|
|
|
|
HTTP_NOTFOUND = 404,
|
|
|
|
};
|
|
|
|
|
2017-01-13 12:06:31 +00:00
|
|
|
class POST
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void reserve(size_t res) { data.reserve(res); }
|
|
|
|
POST& add(const char *key, const char *value);
|
|
|
|
const char *c_str() const { return data.c_str(); }
|
|
|
|
const std::string& str() const { return data; }
|
|
|
|
bool empty() const { return data.empty(); }
|
|
|
|
size_t length() const { return data.length(); }
|
|
|
|
private:
|
|
|
|
std::string data;
|
|
|
|
};
|
|
|
|
|
2012-06-01 15:23:19 +00:00
|
|
|
struct Request
|
|
|
|
{
|
|
|
|
Request() : port(80), user(NULL) {}
|
|
|
|
Request(const std::string& h, const std::string& res, int p = 80, void *u = NULL)
|
2017-01-13 12:06:31 +00:00
|
|
|
: host(h), resource(res), port(80), user(u), useSSL(false) {}
|
2012-06-01 15:23:19 +00:00
|
|
|
|
2017-01-13 12:06:31 +00:00
|
|
|
std::string protocol;
|
2012-06-01 15:23:19 +00:00
|
|
|
std::string host;
|
|
|
|
std::string header; // set by socket
|
|
|
|
std::string resource;
|
2017-01-13 12:06:31 +00:00
|
|
|
std::string extraGetHeaders;
|
2012-06-01 15:23:19 +00:00
|
|
|
int port;
|
|
|
|
void *user;
|
2017-01-13 12:06:31 +00:00
|
|
|
bool useSSL;
|
|
|
|
POST post; // if this is empty, it's a GET request, otherwise a POST request
|
2012-06-01 15:23:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class HttpSocket : public TcpSocket
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
HttpSocket();
|
|
|
|
virtual ~HttpSocket();
|
|
|
|
|
|
|
|
virtual bool HasPendingTask() const
|
|
|
|
{
|
|
|
|
return ExpectMoreData() || _requestQ.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetKeepAlive(unsigned int secs) { _keep_alive = secs; }
|
|
|
|
void SetUserAgent(const std::string &s) { _user_agent = s; }
|
|
|
|
void SetAcceptEncoding(const std::string& s) { _accept_encoding = s; }
|
|
|
|
void SetFollowRedirect(bool follow) { _followRedir = follow; }
|
|
|
|
void SetAlwaysHandle(bool h) { _alwaysHandle = h; }
|
|
|
|
|
2017-01-13 12:06:31 +00:00
|
|
|
bool Download(const std::string& url, const char *extraRequest = NULL, void *user = NULL, const POST *post = NULL);
|
|
|
|
bool SendRequest(Request& what, bool enqueue);
|
|
|
|
bool SendRequest(const std::string what, const char *extraRequest = NULL, void *user = NULL);
|
|
|
|
bool QueueRequest(const std::string what, const char *extraRequest = NULL, void *user = NULL);
|
2012-06-01 15:23:19 +00:00
|
|
|
|
|
|
|
unsigned int GetRemaining() const { return _remaining; }
|
|
|
|
|
|
|
|
unsigned int GetStatusCode() const { return _status; }
|
|
|
|
unsigned int GetContentLen() const { return _contentLen; }
|
|
|
|
bool ChunkedTransfer() const { return _chunkedTransfer; }
|
|
|
|
bool ExpectMoreData() const { return _remaining || _chunkedTransfer; }
|
|
|
|
|
|
|
|
const Request &GetCurrentRequest() const { return _curRequest; }
|
|
|
|
const char *Hdr(const char *h) const;
|
|
|
|
|
|
|
|
bool IsRedirecting() const;
|
2017-01-13 12:06:31 +00:00
|
|
|
bool IsSuccess() const;
|
2012-06-01 15:23:19 +00:00
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual void _OnCloseInternal();
|
|
|
|
virtual void _OnClose();
|
|
|
|
virtual void _OnData(); // data received callback. Internal, should only be overloaded to call _OnRecv()
|
2017-01-13 12:06:31 +00:00
|
|
|
virtual void _OnRecv(void *buf, unsigned int size) = 0;
|
2012-06-01 15:23:19 +00:00
|
|
|
virtual void _OnOpen(); // called when opene
|
|
|
|
virtual bool _OnUpdate(); // called before reading from the socket
|
|
|
|
|
|
|
|
// new ones:
|
|
|
|
virtual void _OnRequestDone() {}
|
|
|
|
|
2017-01-13 12:06:31 +00:00
|
|
|
bool _Redirect(std::string loc, bool forceGET);
|
|
|
|
|
2012-06-01 15:23:19 +00:00
|
|
|
void _ProcessChunk();
|
|
|
|
bool _EnqueueOrSend(const Request& req, bool forceQueue = false);
|
|
|
|
void _DequeueMore();
|
|
|
|
bool _OpenRequest(const Request& req);
|
|
|
|
void _ParseHeader();
|
|
|
|
void _ParseHeaderFields(const char *s, size_t size);
|
|
|
|
bool _HandleStatus(); // Returns whether the processed request was successful, or not
|
|
|
|
void _FinishRequest();
|
2017-01-13 12:06:31 +00:00
|
|
|
void _OnRecvInternal(void *buf, unsigned int size);
|
2012-06-01 15:23:19 +00:00
|
|
|
|
|
|
|
std::string _user_agent;
|
|
|
|
std::string _accept_encoding; // Default empty.
|
|
|
|
std::string _tmpHdr; // used to save the http header if the incoming buffer was not large enough
|
|
|
|
|
|
|
|
unsigned int _keep_alive; // http related
|
|
|
|
unsigned int _remaining; // http "Content-Length: X" - already recvd. 0 if ready for next packet.
|
|
|
|
// For chunked transfer encoding, this holds the remaining size of the current chunk
|
|
|
|
unsigned int _contentLen; // as reported by server
|
|
|
|
unsigned int _status; // http status code, HTTP_OK if things are good
|
|
|
|
|
|
|
|
std::queue<Request> _requestQ;
|
|
|
|
std::map<std::string, std::string> _hdrs; // Maps HTTP header fields to their values
|
|
|
|
|
|
|
|
Request _curRequest;
|
|
|
|
|
|
|
|
bool _inProgress;
|
|
|
|
bool _chunkedTransfer;
|
|
|
|
bool _mustClose; // keep-alive specified, or not
|
|
|
|
bool _followRedir; // Default true. Follow 3xx redirects if this is set.
|
|
|
|
bool _alwaysHandle; // Also deliver to _OnRecv() if a non-success code was received.
|
|
|
|
};
|
|
|
|
|
|
|
|
} // end namespace minihttp
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#ifdef MINIHTTP_SUPPORT_SOCKET_SET
|
|
|
|
|
|
|
|
#include <map>
|
|
|
|
|
|
|
|
namespace minihttp
|
|
|
|
{
|
|
|
|
|
|
|
|
class SocketSet
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
virtual ~SocketSet();
|
|
|
|
void deleteAll();
|
|
|
|
bool update();
|
|
|
|
void add(TcpSocket *s, bool deleteWhenDone = true);
|
|
|
|
bool has(TcpSocket *s);
|
|
|
|
void remove(TcpSocket *s);
|
|
|
|
inline size_t size() { return _store.size(); }
|
|
|
|
|
|
|
|
//protected:
|
|
|
|
|
|
|
|
struct SocketSetData
|
|
|
|
{
|
|
|
|
bool deleteWhenDone;
|
|
|
|
// To be extended
|
|
|
|
};
|
2024-04-15 22:12:56 +00:00
|
|
|
|
2012-06-01 15:23:19 +00:00
|
|
|
typedef std::map<TcpSocket*, SocketSetData> Store;
|
|
|
|
|
|
|
|
Store _store;
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
} // end namespace minihttp
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|