mirror of
https://github.com/lammertb/libhttp.git
synced 2025-12-22 04:02:04 +03:00
501 lines
13 KiB
C++
501 lines
13 KiB
C++
/* Copyright (c) 2013-2014 the Civetweb developers
|
|
* Copyright (c) 2013 No Face Press, LLC
|
|
*
|
|
* License http://opensource.org/licenses/mit-license.php MIT License
|
|
*/
|
|
|
|
#include "CivetServer.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <stdexcept>
|
|
|
|
#ifndef UNUSED_PARAMETER
|
|
#define UNUSED_PARAMETER(x) (void)(x)
|
|
#endif
|
|
|
|
bool
|
|
CivetHandler::handleGet(CivetServer *server, struct mg_connection *conn)
|
|
{
|
|
UNUSED_PARAMETER(server);
|
|
UNUSED_PARAMETER(conn);
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
CivetHandler::handlePost(CivetServer *server, struct mg_connection *conn)
|
|
{
|
|
UNUSED_PARAMETER(server);
|
|
UNUSED_PARAMETER(conn);
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
CivetHandler::handlePut(CivetServer *server, struct mg_connection *conn)
|
|
{
|
|
UNUSED_PARAMETER(server);
|
|
UNUSED_PARAMETER(conn);
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
CivetHandler::handleDelete(CivetServer *server, struct mg_connection *conn)
|
|
{
|
|
UNUSED_PARAMETER(server);
|
|
UNUSED_PARAMETER(conn);
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
CivetHandler::handleOptions(CivetServer *server, struct mg_connection *conn)
|
|
{
|
|
UNUSED_PARAMETER(server);
|
|
UNUSED_PARAMETER(conn);
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
CivetWebSocketHandler::handleConnection(CivetServer *server,
|
|
const struct mg_connection *conn)
|
|
{
|
|
UNUSED_PARAMETER(server);
|
|
UNUSED_PARAMETER(conn);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
CivetWebSocketHandler::handleReadyState(CivetServer *server,
|
|
struct mg_connection *conn)
|
|
{
|
|
UNUSED_PARAMETER(server);
|
|
UNUSED_PARAMETER(conn);
|
|
return;
|
|
}
|
|
|
|
bool
|
|
CivetWebSocketHandler::handleData(CivetServer *server,
|
|
struct mg_connection *conn,
|
|
int bits,
|
|
char *data,
|
|
size_t data_len)
|
|
{
|
|
UNUSED_PARAMETER(server);
|
|
UNUSED_PARAMETER(conn);
|
|
UNUSED_PARAMETER(bits);
|
|
UNUSED_PARAMETER(data);
|
|
UNUSED_PARAMETER(data_len);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
CivetWebSocketHandler::handleClose(CivetServer *server,
|
|
const struct mg_connection *conn)
|
|
{
|
|
UNUSED_PARAMETER(server);
|
|
UNUSED_PARAMETER(conn);
|
|
return;
|
|
}
|
|
|
|
int
|
|
CivetServer::requestHandler(struct mg_connection *conn, void *cbdata)
|
|
{
|
|
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
|
assert(request_info != NULL);
|
|
CivetServer *me = (CivetServer *)(request_info->user_data);
|
|
assert(me != NULL);
|
|
|
|
// Happens when a request hits the server before the context is saved
|
|
if (me->context == NULL)
|
|
return 0;
|
|
|
|
mg_lock_context(me->context);
|
|
me->connections[conn] = CivetConnection();
|
|
mg_unlock_context(me->context);
|
|
|
|
CivetHandler *handler = (CivetHandler *)cbdata;
|
|
|
|
if (handler) {
|
|
if (strcmp(request_info->request_method, "GET") == 0) {
|
|
return handler->handleGet(me, conn) ? 1 : 0;
|
|
} else if (strcmp(request_info->request_method, "POST") == 0) {
|
|
return handler->handlePost(me, conn) ? 1 : 0;
|
|
} else if (strcmp(request_info->request_method, "PUT") == 0) {
|
|
return handler->handlePut(me, conn) ? 1 : 0;
|
|
} else if (strcmp(request_info->request_method, "DELETE") == 0) {
|
|
return handler->handleDelete(me, conn) ? 1 : 0;
|
|
} else if (strcmp(request_info->request_method, "OPTIONS") == 0) {
|
|
return handler->handleOptions(me, conn) ? 1 : 0;
|
|
}
|
|
}
|
|
|
|
return 0; // No handler found
|
|
}
|
|
|
|
int
|
|
CivetServer::webSocketConnectionHandler(const struct mg_connection *conn,
|
|
void *cbdata)
|
|
{
|
|
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
|
assert(request_info != NULL);
|
|
CivetServer *me = (CivetServer *)(request_info->user_data);
|
|
assert(me != NULL);
|
|
|
|
// Happens when a request hits the server before the context is saved
|
|
if (me->context == NULL)
|
|
return 0;
|
|
|
|
CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;
|
|
|
|
if (handler) {
|
|
return handler->handleConnection(me, conn) ? 0 : 1;
|
|
}
|
|
|
|
return 1; // No handler found, close connection
|
|
}
|
|
|
|
void
|
|
CivetServer::webSocketReadyHandler(struct mg_connection *conn, void *cbdata)
|
|
{
|
|
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
|
assert(request_info != NULL);
|
|
CivetServer *me = (CivetServer *)(request_info->user_data);
|
|
assert(me != NULL);
|
|
|
|
// Happens when a request hits the server before the context is saved
|
|
if (me->context == NULL)
|
|
return;
|
|
|
|
CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;
|
|
|
|
if (handler) {
|
|
handler->handleReadyState(me, conn);
|
|
}
|
|
}
|
|
|
|
int
|
|
CivetServer::webSocketDataHandler(struct mg_connection *conn,
|
|
int bits,
|
|
char *data,
|
|
size_t data_len,
|
|
void *cbdata)
|
|
{
|
|
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
|
assert(request_info != NULL);
|
|
CivetServer *me = (CivetServer *)(request_info->user_data);
|
|
assert(me != NULL);
|
|
|
|
// Happens when a request hits the server before the context is saved
|
|
if (me->context == NULL)
|
|
return 0;
|
|
|
|
CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;
|
|
|
|
if (handler) {
|
|
return handler->handleData(me, conn, bits, data, data_len) ? 1 : 0;
|
|
}
|
|
|
|
return 1; // No handler found
|
|
}
|
|
|
|
void
|
|
CivetServer::webSocketCloseHandler(const struct mg_connection *conn,
|
|
void *cbdata)
|
|
{
|
|
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
|
assert(request_info != NULL);
|
|
CivetServer *me = (CivetServer *)(request_info->user_data);
|
|
assert(me != NULL);
|
|
|
|
// Happens when a request hits the server before the context is saved
|
|
if (me->context == NULL)
|
|
return;
|
|
|
|
CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;
|
|
|
|
if (handler) {
|
|
handler->handleClose(me, conn);
|
|
}
|
|
}
|
|
|
|
CivetServer::CivetServer(const char **options,
|
|
const struct mg_callbacks *_callbacks)
|
|
: context(0)
|
|
{
|
|
struct mg_callbacks callbacks;
|
|
memset(&callbacks, 0, sizeof(callbacks));
|
|
|
|
if (_callbacks) {
|
|
callbacks = *_callbacks;
|
|
userCloseHandler = _callbacks->connection_close;
|
|
} else {
|
|
userCloseHandler = NULL;
|
|
}
|
|
callbacks.connection_close = closeHandler;
|
|
context = mg_start(&callbacks, this, options);
|
|
if (context == NULL)
|
|
throw CivetException("null context when constructing CivetServer. "
|
|
"Possible problem binding to port.");
|
|
}
|
|
|
|
CivetServer::~CivetServer()
|
|
{
|
|
close();
|
|
}
|
|
|
|
void
|
|
CivetServer::closeHandler(const struct mg_connection *conn)
|
|
{
|
|
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
|
assert(request_info != NULL);
|
|
CivetServer *me = (CivetServer *)(request_info->user_data);
|
|
assert(me != NULL);
|
|
|
|
// Happens when a request hits the server before the context is saved
|
|
if (me->context == NULL)
|
|
return;
|
|
|
|
if (me->userCloseHandler)
|
|
me->userCloseHandler(conn);
|
|
mg_lock_context(me->context);
|
|
me->connections.erase(const_cast<struct mg_connection *>(conn));
|
|
mg_unlock_context(me->context);
|
|
}
|
|
|
|
void
|
|
CivetServer::addHandler(const std::string &uri, CivetHandler *handler)
|
|
{
|
|
mg_set_request_handler(context, uri.c_str(), requestHandler, handler);
|
|
}
|
|
|
|
void
|
|
CivetServer::addWebSocketHandler(const std::string &uri,
|
|
CivetWebSocketHandler *handler)
|
|
{
|
|
mg_set_websocket_handler(context,
|
|
uri.c_str(),
|
|
webSocketConnectionHandler,
|
|
webSocketReadyHandler,
|
|
webSocketDataHandler,
|
|
webSocketCloseHandler,
|
|
handler);
|
|
}
|
|
|
|
void
|
|
CivetServer::removeHandler(const std::string &uri)
|
|
{
|
|
mg_set_request_handler(context, uri.c_str(), NULL, NULL);
|
|
}
|
|
|
|
void
|
|
CivetServer::close()
|
|
{
|
|
if (context) {
|
|
mg_stop(context);
|
|
context = 0;
|
|
}
|
|
}
|
|
|
|
int
|
|
CivetServer::getCookie(struct mg_connection *conn,
|
|
const std::string &cookieName,
|
|
std::string &cookieValue)
|
|
{
|
|
// Maximum cookie length as per microsoft is 4096.
|
|
// http://msdn.microsoft.com/en-us/library/ms178194.aspx
|
|
char _cookieValue[4096];
|
|
const char *cookie = mg_get_header(conn, "Cookie");
|
|
int lRead = mg_get_cookie(cookie,
|
|
cookieName.c_str(),
|
|
_cookieValue,
|
|
sizeof(_cookieValue));
|
|
cookieValue.clear();
|
|
cookieValue.append(_cookieValue);
|
|
return lRead;
|
|
}
|
|
|
|
const char *
|
|
CivetServer::getHeader(struct mg_connection *conn,
|
|
const std::string &headerName)
|
|
{
|
|
return mg_get_header(conn, headerName.c_str());
|
|
}
|
|
|
|
void
|
|
CivetServer::urlDecode(const char *src,
|
|
std::string &dst,
|
|
bool is_form_url_encoded)
|
|
{
|
|
urlDecode(src, strlen(src), dst, is_form_url_encoded);
|
|
}
|
|
|
|
void
|
|
CivetServer::urlDecode(const char *src,
|
|
size_t src_len,
|
|
std::string &dst,
|
|
bool is_form_url_encoded)
|
|
{
|
|
int i, j, a, b;
|
|
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
|
|
|
|
dst.clear();
|
|
for (i = j = 0; i < (int)src_len; i++, j++) {
|
|
if (i < (int)src_len - 2 && src[i] == '%'
|
|
&& isxdigit(*(const unsigned char *)(src + i + 1))
|
|
&& isxdigit(*(const unsigned char *)(src + i + 2))) {
|
|
a = tolower(*(const unsigned char *)(src + i + 1));
|
|
b = tolower(*(const unsigned char *)(src + i + 2));
|
|
dst.push_back((char)((HEXTOI(a) << 4) | HEXTOI(b)));
|
|
i += 2;
|
|
} else if (is_form_url_encoded && src[i] == '+') {
|
|
dst.push_back(' ');
|
|
} else {
|
|
dst.push_back(src[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
CivetServer::getParam(struct mg_connection *conn,
|
|
const char *name,
|
|
std::string &dst,
|
|
size_t occurrence)
|
|
{
|
|
const char *formParams = NULL;
|
|
const struct mg_request_info *ri = mg_get_request_info(conn);
|
|
assert(ri != NULL);
|
|
CivetServer *me = (CivetServer *)(ri->user_data);
|
|
assert(me != NULL);
|
|
mg_lock_context(me->context);
|
|
CivetConnection &conobj = me->connections[conn];
|
|
mg_lock_connection(conn);
|
|
mg_unlock_context(me->context);
|
|
|
|
if (conobj.postData != NULL) {
|
|
formParams = conobj.postData;
|
|
} else {
|
|
const char *con_len_str = mg_get_header(conn, "Content-Length");
|
|
if (con_len_str) {
|
|
unsigned long con_len = atoi(con_len_str);
|
|
if (con_len > 0) {
|
|
// Add one extra character: in case the post-data is a text, it
|
|
// is required as 0-termination.
|
|
// Do not increment con_len, since the 0 terminating is not part
|
|
// of the content (text or binary).
|
|
conobj.postData = (char *)malloc(con_len + 1);
|
|
if (conobj.postData != NULL) {
|
|
// malloc may fail for huge requests
|
|
mg_read(conn, conobj.postData, con_len);
|
|
conobj.postData[con_len] = 0;
|
|
formParams = conobj.postData;
|
|
conobj.postDataLen = con_len;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (formParams == NULL) {
|
|
// get requests do store html <form> field values in the http
|
|
// query_string
|
|
formParams = ri->query_string;
|
|
}
|
|
mg_unlock_connection(conn);
|
|
|
|
if (formParams != NULL) {
|
|
return getParam(formParams, strlen(formParams), name, dst, occurrence);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
CivetServer::getParam(const char *data,
|
|
size_t data_len,
|
|
const char *name,
|
|
std::string &dst,
|
|
size_t occurrence)
|
|
{
|
|
const char *p, *e, *s;
|
|
size_t name_len;
|
|
|
|
dst.clear();
|
|
if (data == NULL || name == NULL || data_len == 0) {
|
|
return false;
|
|
}
|
|
name_len = strlen(name);
|
|
e = data + data_len;
|
|
|
|
// data is "var1=val1&var2=val2...". Find variable first
|
|
for (p = data; p + name_len < e; p++) {
|
|
if ((p == data || p[-1] == '&') && p[name_len] == '='
|
|
&& !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) {
|
|
|
|
// Point p to variable value
|
|
p += name_len + 1;
|
|
|
|
// Point s to the end of the value
|
|
s = (const char *)memchr(p, '&', (size_t)(e - p));
|
|
if (s == NULL) {
|
|
s = e;
|
|
}
|
|
assert(s >= p);
|
|
|
|
// Decode variable into destination buffer
|
|
urlDecode(p, (int)(s - p), dst, true);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
CivetServer::urlEncode(const char *src, std::string &dst, bool append)
|
|
{
|
|
urlEncode(src, strlen(src), dst, append);
|
|
}
|
|
|
|
void
|
|
CivetServer::urlEncode(const char *src,
|
|
size_t src_len,
|
|
std::string &dst,
|
|
bool append)
|
|
{
|
|
static const char *dont_escape = "._-$,;~()";
|
|
static const char *hex = "0123456789abcdef";
|
|
|
|
if (!append)
|
|
dst.clear();
|
|
|
|
for (; src_len > 0; src++, src_len--) {
|
|
if (isalnum(*(const unsigned char *)src)
|
|
|| strchr(dont_escape, *(const unsigned char *)src) != NULL) {
|
|
dst.push_back(*src);
|
|
} else {
|
|
dst.push_back('%');
|
|
dst.push_back(hex[(*(const unsigned char *)src) >> 4]);
|
|
dst.push_back(hex[(*(const unsigned char *)src) & 0xf]);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<int>
|
|
CivetServer::getListeningPorts()
|
|
{
|
|
std::vector<int> ports(10);
|
|
std::vector<int> ssl(10);
|
|
size_t size = mg_get_ports(context, ports.size(), &ports[0], &ssl[0]);
|
|
ports.resize(size);
|
|
ssl.resize(size);
|
|
return ports;
|
|
}
|
|
|
|
CivetServer::CivetConnection::CivetConnection()
|
|
{
|
|
postData = NULL;
|
|
postDataLen = 0;
|
|
}
|
|
|
|
CivetServer::CivetConnection::~CivetConnection()
|
|
{
|
|
free(postData);
|
|
}
|