move to new cspot

This commit is contained in:
philippe44
2023-03-25 16:48:41 -07:00
parent c712b78931
commit 008c36facf
2983 changed files with 465270 additions and 13569 deletions

View File

@@ -0,0 +1,106 @@
#pragma once
#include <BellLogger.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fstream>
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <utility>
#include <optional>
#include <regex>
#include <sstream>
#include <string>
#include <mutex>
#include <unordered_map>
#include "CivetServer.h"
#include "civetweb.h"
using namespace bell;
namespace bell {
class BellHTTPServer : public CivetHandler {
public:
BellHTTPServer(int serverPort);
enum class WSState { CONNECTED, READY, CLOSED };
struct HTTPResponse {
uint8_t* body;
size_t bodySize;
std::map<std::string, std::string> headers;
int status;
HTTPResponse() {
body = nullptr;
bodySize = 0;
status = 200;
}
~HTTPResponse() {
if (body != nullptr) {
free(body);
body = nullptr;
}
}
};
typedef std::function<std::unique_ptr<HTTPResponse>(struct mg_connection* conn)> HTTPHandler;
typedef std::function<void(struct mg_connection* conn, WSState)>
WSStateHandler;
typedef std::function<void(struct mg_connection* conn, char*, size_t)>
WSDataHandler;
class Router {
public:
struct RouterNode {
std::unordered_map<std::string, std::unique_ptr<RouterNode>> children;
HTTPHandler value = nullptr;
std::string paramName = "";
bool isParam = false;
bool isCatchAll = false;
};
RouterNode root = RouterNode();
typedef std::unordered_map<std::string, std::string> Params;
typedef std::pair<HTTPHandler, Params> HandlerAndParams;
std::vector<std::string> split(const std::string str,
const std::string regex_str);
void insert(const std::string& route, HTTPHandler& value);
HandlerAndParams find(const std::string& route);
};
std::vector<int> getListeningPorts() { return server->getListeningPorts(); };
void close() { server->close(); }
std::unique_ptr<HTTPResponse> makeJsonResponse(const std::string& json, int status = 200);
std::unique_ptr<HTTPResponse> makeEmptyResponse();
void registerNotFound(HTTPHandler handler);
void registerGet(const std::string&, HTTPHandler handler);
void registerPost(const std::string&, HTTPHandler handler);
void registerWS(const std::string&, WSDataHandler dataHandler,
WSStateHandler stateHandler);
static std::unordered_map<std::string, std::string> extractParams(struct mg_connection* conn);
private:
std::unique_ptr<CivetServer> server;
int serverPort = 8080;
Router getRequestsRouter;
Router postRequestsRouter;
std::mutex responseMutex;
HTTPHandler notFoundHandler;
bool handleGet(CivetServer* server, struct mg_connection* conn);
bool handlePost(CivetServer* server, struct mg_connection* conn);
};
} // namespace bell

View File

@@ -0,0 +1,19 @@
#pragma once
#include <string>
namespace bell {
class Socket {
public:
Socket(){};
virtual ~Socket() = default;
virtual void open(const std::string& host, uint16_t port) = 0;
virtual size_t poll() = 0;
virtual size_t write(uint8_t* buf, size_t len) = 0;
virtual size_t read(uint8_t* buf, size_t len) = 0;
virtual bool isOpen() = 0;
virtual void close() = 0;
};
} // namespace bell

View File

@@ -0,0 +1,76 @@
#pragma once
#include <iostream>
#include <string>
#include <vector>
namespace bell::BellTar {
typedef long long unsigned file_size_t;
////////////////////////////////////////
// Writing raw data
////////////////////////////////////////
class writer {
std::ostream& _dst;
public:
writer(std::ostream& dst) : _dst(dst) {}
~writer() { finish(); }
// Append the data specified by |data| and |file_size| into the
// tar file represented by |dst|.
// In the tar file, the data will be available at |path_in_tar|.
void put(std::string path_in_tar, char const* const data,
const file_size_t data_size);
// Write empty folder at |path_in_tar|.
// NOTE: to specify folders for files, just use / in the path
// passed to |put()|.
void put_directory(std::string path_in_tar);
// Call after everything has been added to the tar represented
// by |dst| to make it a valid tar file.
void finish();
};
////////////////////////////////////////
// Reading raw data
////////////////////////////////////////
class reader {
std::istream& _inp;
struct {
std::string file_name;
file_size_t file_size;
char file_type;
} _cached_header_data;
bool _cached_header_data_valid;
void _cache_header();
int _number_of_files;
public:
// Constructor, pass input stream |inp| pointing to a tar file.
reader(std::istream& inp)
: _inp(inp), _cached_header_data_valid(false), _number_of_files(-1) {}
// Returns true iff another file can be read from |inp|.
bool contains_another_file();
// Returns file name of next file in |inp|.
std::string get_next_file_name();
// Returns file size of next file in |inp|. Use to allocate
// memory for the |read_next_file()| call.
file_size_t get_next_file_size();
// Read next file in |inp| to |data|.
void read_next_file(char* const data);
char get_next_file_type();
void extract_all_files(std::string output_dir);
// Skip next file in |inp|.
void skip_next_file();
// Returns number of files in tar at |inp|.
int number_of_files();
};
} // namespace bell::BellTar

View File

@@ -0,0 +1,31 @@
#pragma once
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <cstring>
#include <memory>
#include "ByteStream.h"
namespace bell
{
class BinaryReader
{
std::shared_ptr<ByteStream> stream;
size_t currentPos = 0;
public:
BinaryReader(std::shared_ptr<ByteStream> stream);
int32_t readInt();
int16_t readShort();
uint32_t readUInt();
long long readLong();
void close();
uint8_t readByte();
size_t size();
size_t position();
std::vector<uint8_t> readBytes(size_t);
void skip(size_t);
};
}

View File

@@ -0,0 +1,77 @@
#pragma once
#ifndef ESP_PLATFORM
#include <bit>
#endif
#include <iostream>
#include <vector>
namespace bell {
class BinaryStream {
private:
std::endian byteOrder;
std::istream* istr = nullptr;
std::ostream* ostr = nullptr;
void ensureReadable();
void ensureWritable();
bool flipBytes = false;
template <typename T>
T swap16(T value) {
#ifdef _WIN32
return _byteswap_ushort(value);
#else
return __builtin_bswap16(value);
#endif
}
template <typename T>
T swap32(T value) {
#ifdef _WIN32
return _byteswap_ulong(value);
#else
return __builtin_bswap32(value);
#endif
}
template <typename T>
T swap64(T value) {
#ifdef _WIN32
return _byteswap_uint64(value);
#else
return __builtin_bswap64(value);
#endif
}
public:
BinaryStream(std::ostream* ostr);
BinaryStream(std::istream* istr);
/**
* @brief Set byte order used by stream.
*
* @param byteOrder stream's byteorder. Defaults to native.
*/
void setByteOrder(std::endian byteOrder);
// Read operations
BinaryStream& operator>>(char& value);
BinaryStream& operator>>(std::byte& value);
BinaryStream& operator>>(int16_t& value);
BinaryStream& operator>>(uint16_t& value);
BinaryStream& operator>>(int32_t& value);
BinaryStream& operator>>(uint32_t& value);
BinaryStream& operator>>(int64_t& value);
BinaryStream& operator>>(uint64_t& value);
// Write operations
BinaryStream& operator<<(char value);
BinaryStream& operator<<(std::byte value);
BinaryStream& operator<<(int16_t value);
BinaryStream& operator<<(uint16_t value);
BinaryStream& operator<<(int32_t value);
BinaryStream& operator<<(uint32_t value);
BinaryStream& operator<<(int64_t value);
BinaryStream& operator<<(uint64_t value);
};
} // namespace bell

View File

@@ -0,0 +1,125 @@
#pragma once
#include "ByteStream.h"
#include "BellTask.h"
#include "WrappedSemaphore.h"
#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
/**
* This class implements a wrapper around an arbitrary bell::ByteStream,
* providing a circular reading buffer with configurable thresholds.
*
* The BufferedStream runs a bell::Task when it's started, so the caller can
* access the buffer's data asynchronously, whenever needed. The buffer is refilled
* automatically from source stream.
*
* The class implements bell::ByteStream's methods, although for proper functioning,
* the caller code should be modified to check isReady() and isNotReady() flags.
*
* If the actual reading code can't be modified, waitForReady allows to wait for buffer readiness
* during reading. Keep in mind that using the semaphore is probably more resource effective.
*
* The source stream (passed to open() or returned by the reader) should implement the read()
* method correctly, such as that 0 is returned if, and only if the stream ends.
*/
class BufferedStream : public bell::ByteStream, bell::Task {
public:
typedef std::shared_ptr<bell::ByteStream> StreamPtr;
typedef std::function<StreamPtr(uint32_t rangeStart)> StreamReader;
public:
/**
* @param taskName name to use for the reading task
* @param bufferSize total size of the reading buffer
* @param readThreshold how much can be read before refilling the buffer
* @param readSize amount of bytes to read from the source each time
* @param readyThreshold minimum amount of available bytes to report isReady()
* @param notReadyThreshold maximum amount of available bytes to report isNotReady()
* @param waitForReady whether to wait for the buffer to be ready during reading
* @param endWithSource whether to end the streaming as soon as source returns 0 from read()
*/
BufferedStream(
const std::string &taskName,
uint32_t bufferSize,
uint32_t readThreshold,
uint32_t readSize,
uint32_t readyThreshold,
uint32_t notReadyThreshold,
bool waitForReady = false);
~BufferedStream() override;
bool open(const StreamPtr &stream);
bool open(const StreamReader &newReader, uint32_t initialOffset = 0);
void close() override;
// inherited methods
public:
/**
* Read len bytes from the buffer to dst. If waitForReady is enabled
* and readAvailable is lower than notReadyThreshold, the function
* will block until readyThreshold bytes is available.
*
* @returns number of bytes copied to dst (might be lower than len,
* if the buffer does not contain len bytes available), or 0 if the source
* stream is already closed and there is no reader attached.
*/
size_t read(uint8_t *dst, size_t len) override;
size_t skip(size_t len) override;
size_t position() override;
size_t size() override;
// stream status
public:
/**
* Total amount of bytes served to read().
*/
uint32_t readTotal;
/**
* Total amount of bytes read from source.
*/
uint32_t bufferTotal;
/**
* Amount of bytes available to read from the buffer.
*/
std::atomic<uint32_t> readAvailable;
/**
* Whether the caller should start reading the data. This indicates that a safe
* amount (determined by readyThreshold) of data is available in the buffer.
*/
bool isReady() const;
/**
* Whether the caller should stop reading the data. This indicates that the amount of data
* available for reading is decreasing to a non-safe value, as data is being read
* faster than it can be buffered.
*/
bool isNotReady() const;
/**
* Semaphore that is given when the buffer becomes ready (isReady() == true). Caller can
* wait for the semaphore instead of continuously querying isReady().
*/
bell::WrappedSemaphore readySem;
private:
std::mutex runningMutex;
bool running = false;
bool terminate = false;
bell::WrappedSemaphore readSem; // signal to start writing to buffer after reading from it
std::mutex readMutex; // mutex for locking read operations during writing, and vice versa
uint32_t bufferSize;
uint32_t readAt;
uint32_t readSize;
uint32_t readyThreshold;
uint32_t notReadyThreshold;
bool waitForReady;
uint8_t *buf;
uint8_t *bufEnd;
uint8_t *bufReadPtr;
uint8_t *bufWritePtr;
StreamPtr source;
StreamReader reader;
void runTask() override;
void reset();
uint32_t lengthBetween(uint8_t *me, uint8_t *other);
};

View File

@@ -0,0 +1,27 @@
#ifndef BELL_BYTE_READER_H
#define BELL_BYTE_READER_H
#include <stdlib.h>
#include <stdint.h>
/**
* A class for reading bytes from a stream. Further implemented in HTTPStream.h
*/
namespace bell
{
class ByteStream
{
public:
ByteStream(){};
virtual ~ByteStream() = default;
virtual size_t read(uint8_t *buf, size_t nbytes) = 0;
virtual size_t skip(size_t nbytes) = 0;
virtual size_t position() = 0;
virtual size_t size() = 0;
virtual void close() = 0;
};
}
#endif

View File

@@ -0,0 +1,39 @@
#pragma once
#include <algorithm>
#include <memory>
#include <cstring>
#include <mutex>
#include <vector>
#include "WrappedSemaphore.h"
namespace bell {
class CircularBuffer {
public:
CircularBuffer(size_t dataCapacity);
std::unique_ptr<bell::WrappedSemaphore> dataSemaphore;
size_t size() const {
return dataSize;
}
size_t capacity() const {
return dataCapacity;
}
size_t write(const uint8_t *data, size_t bytes);
size_t read(uint8_t *data, size_t bytes);
void emptyBuffer();
void emptyExcept(size_t size);
private:
std::mutex bufferMutex;
size_t begIndex = 0;
size_t endIndex = 0;
size_t dataSize = 0;
size_t dataCapacity = 0;
std::vector<uint8_t> buffer;
};
} // namespace bell

View File

@@ -0,0 +1,55 @@
#pragma once
#include <string>
#include <vector>
#include "BellLogger.h"
#include "ByteStream.h"
#include "DecoderGlobals.h"
#include "aacdec.h"
#include "mp3dec.h"
namespace bell {
class EncodedAudioStream {
public:
EncodedAudioStream();
~EncodedAudioStream();
// Codecs supported by this stream class
enum class AudioCodec { AAC, MP3, OGG, NONE };
void openWithStream(std::unique_ptr<bell::ByteStream> byteStream);
size_t decodeFrame(uint8_t* dst);
bool isReadable();
private:
std::shared_ptr<ByteStream> innerStream;
std::vector<uint8_t> inputBuffer;
std::vector<short> outputBuffer;
std::string TAG = "EncryptedAudioStream";
uint8_t* decodePtr = 0;
int bytesInBuffer = 0;
size_t offset = 0;
size_t decodedSampleRate = 44100;
AudioCodec codec = AudioCodec::NONE;
void guessDataFormat();
void readFully(uint8_t* dst, size_t nbytes);
bool vectorStartsWith(std::vector<uint8_t>&, std::vector<uint8_t>&);
std::vector<uint8_t> aacMagicBytes = {0xFF, 0xF1};
std::vector<uint8_t> aacMagicBytes4 = {0xFF, 0xF9};
std::vector<uint8_t> mp3MagicBytesUntagged = {0xFF, 0xFB};
std::vector<uint8_t> mp3MagicBytesIdc = {0x49, 0x44, 0x33};
AACFrameInfo aacFrameInfo;
MP3FrameInfo mp3FrameInfo;
size_t decodeFrameMp3(uint8_t* dst);
size_t decodeFrameAAC(uint8_t* dst);
};
} // namespace bell

View File

@@ -0,0 +1,47 @@
#pragma once
#include <string>
#include <stdexcept>
#include <BellLogger.h>
#include <ByteStream.h>
#include <stdio.h>
/*
* FileStream
*
* A class for reading and writing to files implementing the ByteStream interface.
*
*/
namespace bell
{
class FileStream : public ByteStream
{
public:
FileStream(const std::string& path, std::string mode);
~FileStream();
FILE* file;
/*
* Reads data from the stream.
*
* @param buf The buffer to read data into.
* @param nbytes The size of the buffer.
* @return The number of bytes read.
* @throws std::runtime_error if the stream is closed.
*/
size_t read(uint8_t *buf, size_t nbytes);
/*
* Skips nbytes bytes in the stream.
*/
size_t skip(size_t nbytes);
size_t position();
size_t size();
// Closes the connection
void close();
};
}

View File

@@ -0,0 +1,99 @@
#pragma once
#include <memory>
#include <stdexcept>
#include <string>
#include <string_view>
#include <unordered_map>
#include <variant>
#include <vector>
#include <cassert>
#include "BellSocket.h"
#include "ByteStream.h"
#include "SocketStream.h"
#include "URLParser.h"
#include "fmt/core.h"
#include "picohttpparser.h"
namespace bell {
class HTTPClient {
public:
// most basic header type, represents by a key-val
typedef std::pair<std::string, std::string> ValueHeader;
typedef std::vector<ValueHeader> Headers;
// Helper over ValueHeader, formatting a HTTP bytes range
struct RangeHeader {
static ValueHeader range(int32_t from, int32_t to) {
return ValueHeader{"Range", fmt::format("bytes={}-{}", from, to)};
}
static ValueHeader last(int32_t nbytes) {
return ValueHeader{"Range", fmt::format("bytes=-{}", nbytes)};
}
};
class Response {
public:
Response(){};
~Response();
/**
* Initializes a connection with a given url.
*/
void connect(const std::string& url);
void rawRequest(const std::string& method, const std::string& url,
const std::string& content, Headers& headers);
void get(const std::string& url, Headers headers = {});
std::string_view body();
std::vector<uint8_t> bytes();
std::string_view header(const std::string& headerName);
bell::SocketStream& stream() { return this->socketStream; }
size_t contentLength();
size_t totalLength();
private:
bell::URLParser urlParser;
bell::SocketStream socketStream;
struct phr_header phResponseHeaders[32];
const size_t HTTP_BUF_SIZE = 1024;
std::vector<uint8_t> httpBuffer = std::vector<uint8_t>(HTTP_BUF_SIZE);
std::vector<uint8_t> rawBody = std::vector<uint8_t>();
size_t httpBufferAvailable;
size_t contentSize = 0;
bool hasContentSize = false;
Headers responseHeaders;
void readResponseHeaders();
void readRawBody();
};
enum class Method : uint8_t { GET = 0, POST = 1 };
struct Request {
std::string url;
Method method;
Headers headers;
};
static std::unique_ptr<Response> get(const std::string& url,
Headers headers = {}) {
auto response = std::make_unique<Response>();
response->connect(url);
response->get(url, headers);
return response;
}
};
} // namespace bell

View File

@@ -0,0 +1,69 @@
#pragma once
#include <iostream>
#include "TCPSocket.h"
#include "TLSSocket.h"
namespace bell {
class SocketBuffer : public std::streambuf {
private:
std::unique_ptr<bell::Socket> internalSocket;
static const int bufLen = 1024;
char ibuf[bufLen], obuf[bufLen];
public:
SocketBuffer() { internalSocket = nullptr; }
SocketBuffer(const std::string& hostname, int port, bool isSSL = false) {
open(hostname, port);
}
int open(const std::string& hostname, int port, bool isSSL = false);
int close();
bool isOpen() {
return internalSocket != nullptr && internalSocket->isOpen();
}
~SocketBuffer() { close(); }
protected:
virtual int sync();
virtual int_type underflow();
virtual int_type overflow(int_type c = traits_type::eof());
virtual std::streamsize xsgetn(char_type* __s, std::streamsize __n);
virtual std::streamsize xsputn(const char_type* __s, std::streamsize __n);
};
class SocketStream : public std::iostream {
private:
SocketBuffer socketBuf;
public:
SocketStream() : std::iostream(&socketBuf) {}
SocketStream(const std::string& hostname, int port, bool isSSL = false)
: std::iostream(&socketBuf) {
open(hostname, port, isSSL);
}
SocketBuffer* rdbuf() { return &socketBuf; }
int open(const std::string& hostname, int port, bool isSSL = false) {
int err = socketBuf.open(hostname, port, isSSL);
if (err)
setstate(std::ios::failbit);
return err;
}
int close() { return socketBuf.close(); }
bool isOpen() { return socketBuf.isOpen(); }
};
} // namespace bell

View File

@@ -0,0 +1,19 @@
#pragma once
#include <cstddef>
#include <istream>
#include <streambuf>
namespace bell {
struct MemoryBuffer : std::streambuf {
MemoryBuffer(std::byte const* base, size_t size) {
std::byte* p(const_cast<std::byte*>(base));
this->setg((char*)p, (char*)p, (char*)p + size);
}
};
struct IMemoryStream : virtual MemoryBuffer, std::istream {
IMemoryStream(std::byte const* base, size_t size)
: MemoryBuffer(base, size),
std::istream(static_cast<std::streambuf*>(this)) {}
};
} // namespace bell

View File

@@ -0,0 +1,121 @@
#ifndef BELL_BASIC_SOCKET_H
#define BELL_BASIC_SOCKET_H
#include <ctype.h>
#include <stdlib.h>
#include <sys/types.h>
#include <cstring>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include "BellSocket.h"
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include "win32shim.h"
#else
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#ifdef __sun
#include <sys/filio.h>
#endif
#endif
#include <BellLogger.h>
#include <fstream>
#include <sstream>
namespace bell {
class TCPSocket : public bell::Socket {
private:
int sockFd;
bool isClosed = true;
public:
TCPSocket(){};
~TCPSocket() { close(); };
void open(const std::string& host, uint16_t port) {
int err;
int domain = AF_INET;
int socketType = SOCK_STREAM;
struct addrinfo hints {
}, *addr;
//fine-tune hints according to which socket you want to open
hints.ai_family = domain;
hints.ai_socktype = socketType;
hints.ai_protocol =
IPPROTO_IP; // no enum : possible value can be read in /etc/protocols
hints.ai_flags = AI_CANONNAME | AI_ALL | AI_ADDRCONFIG;
// BELL_LOG(info, "http", "%s %d", host.c_str(), port);
char portStr[6];
sprintf(portStr, "%u", port);
err = getaddrinfo(host.c_str(), portStr, &hints, &addr);
if (err != 0) {
throw std::runtime_error("Resolve failed");
}
sockFd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
err = connect(sockFd, addr->ai_addr, addr->ai_addrlen);
if (err < 0) {
close();
BELL_LOG(error, "http", "Could not connect to %s. Error %d", host.c_str(),
errno);
throw std::runtime_error("Resolve failed");
}
int flag = 1;
setsockopt(sockFd, /* socket affected */
IPPROTO_TCP, /* set option at TCP level */
TCP_NODELAY, /* name of option */
(char*)&flag, /* the cast is historical cruft */
sizeof(int)); /* length of option value */
freeaddrinfo(addr);
isClosed = false;
}
size_t read(uint8_t* buf, size_t len) {
return recv(sockFd, (char*)buf, len, 0);
}
size_t write(uint8_t* buf, size_t len) {
return send(sockFd, (char*)buf, len, 0);
}
size_t poll() {
#ifdef _WIN32
unsigned long value;
ioctlsocket(sockFd, FIONREAD, &value);
#else
int value;
ioctl(sockFd, FIONREAD, &value);
#endif
return value;
}
bool isOpen() { return !isClosed; }
void close() {
if (!isClosed) {
#ifdef _WIN32
closesocket(sockFd);
#else
::close(sockFd);
#endif
sockFd = -1;
isClosed = true;
}
}
};
} // namespace bell
#endif

View File

@@ -0,0 +1,60 @@
#ifndef BELL_TLS_SOCKET_H
#define BELL_TLS_SOCKET_H
#include <ctype.h>
#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>
#include "BellLogger.h"
#include "BellSocket.h"
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <unistd.h>
#endif
#include <stdlib.h>
#include <sys/types.h>
#include <sstream>
#include <string>
#include <vector>
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/debug.h"
#include "mbedtls/entropy.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/ssl.h"
namespace bell {
class TLSSocket : public bell::Socket {
private:
mbedtls_net_context server_fd;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
bool isClosed = true;
public:
TLSSocket();
~TLSSocket() { close(); };
void open(const std::string& host, uint16_t port);
size_t read(uint8_t* buf, size_t len);
size_t write(uint8_t* buf, size_t len);
size_t poll();
bool isOpen();
void close();
};
} // namespace bell
#endif

View File

@@ -0,0 +1,100 @@
#pragma once
#include <iostream>
#include <regex>
#include <stdexcept>
#include <string>
namespace bell {
class URLParser {
public:
static std::string urlEncode(const std::string& value) {
std::string new_str = "";
static auto hex_digt = "0123456789ABCDEF";
std::string result;
result.reserve(value.size() << 1);
for (auto ch : value) {
if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
(ch >= 'a' && ch <= 'z') || ch == '-' || ch == '_' || ch == '!' ||
ch == '\'' || ch == '(' || ch == ')' || ch == '*' || ch == '~' ||
ch == '.') // !'()*-._~
{
result.push_back(ch);
} else {
result += std::string("%") +
hex_digt[static_cast<unsigned char>(ch) >> 4] +
hex_digt[static_cast<unsigned char>(ch) & 15];
}
}
return result;
}
static std::string urlDecode(const std::string& value) {
std::string result;
result.reserve(value.size());
for (std::size_t i = 0; i < value.size(); ++i) {
auto ch = value[i];
if (ch == '%' && (i + 2) < value.size()) {
auto hex = value.substr(i + 1, 2);
auto dec = static_cast<char>(std::strtol(hex.c_str(), nullptr, 16));
result.push_back(dec);
i += 2;
} else if (ch == '+') {
result.push_back(' ');
} else {
result.push_back(ch);
}
}
return result;
}
std::string host;
int port = -1;
std::string schema = "http";
std::string path;
std::regex urlParseRegex = std::regex(
"^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(\\?(?:[^#]*))?(#(?:.*))?");
static URLParser parse(const std::string& url) {
URLParser parser;
// apply parser.urlParseRegex to url
std::cmatch match;
std::regex_match(url.c_str(), match, parser.urlParseRegex);
if (match.size() < 3) {
throw std::invalid_argument("Invalid URL");
}
parser.schema = match[1];
parser.host = match[2];
parser.path = match[3];
if (match[4] != "") {
parser.path += match[4];
}
// check if parser.host contains ':'
if (parser.host.find(':') != std::string::npos) {
auto port = std::stoi(
parser.host.substr(parser.host.find(':') + 1, parser.host.size()));
auto host = parser.host.substr(0, parser.host.find(':'));
parser.port = port;
parser.host = host;
}
if (parser.port == -1) {
parser.port = parser.schema == "http" ? 80 : 443;
}
return parser;
}
};
} // namespace bell

View File

@@ -0,0 +1,40 @@
#pragma once
#include <stdexcept>
#include "BellLogger.h"
#include "mbedtls/ssl.h"
namespace bell::X509Bundle {
typedef struct crt_bundle_t {
const uint8_t** crts;
uint16_t num_certs;
size_t x509_crt_bundle_len;
} crt_bundle_t;
static crt_bundle_t s_crt_bundle;
static constexpr auto TAG = "X509Bundle";
static constexpr auto CRT_HEADER_OFFSET = 4;
static constexpr auto BUNDLE_HEADER_OFFSET = 2;
int crtCheckCertificate(mbedtls_x509_crt* child, const uint8_t* pub_key_buf,
size_t pub_key_len);
/* This callback is called for every certificate in the chain. If the chain
* is proper each intermediate certificate is validated through its parent
* in the x509_crt_verify_chain() function. So this callback should
* only verify the first untrusted link in the chain is signed by the
* root certificate in the trusted bundle
*/
int crtVerifyCallback(void* buf, mbedtls_x509_crt* crt, int depth,
uint32_t* flags);
/* Initialize the bundle into an array so we can do binary search for certs,
the bundle generated by the python utility is already presorted by subject name
*/
void init(const uint8_t* x509_bundle, size_t bundle_size);
void attach(mbedtls_ssl_config* conf);
bool shouldVerify();
}; // namespace bell::X509Bundle

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
* Shigeo Mitsunari
*
* The software is licensed under either the MIT License (below) or the Perl
* license.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef picohttpparser_h
#define picohttpparser_h
#include <sys/types.h>
#if defined(_MSC_VER) && !defined(ssize_t)
#define ssize_t intptr_t
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* contains name and value of a header (name == NULL if is a continuing line
* of a multiline header */
struct phr_header {
const char *name;
size_t name_len;
const char *value;
size_t value_len;
};
/* returns number of bytes consumed if successful, -2 if request is partial,
* -1 if failed */
int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len,
int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len);
/* ditto */
int phr_parse_response(const char *_buf, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len,
struct phr_header *headers, size_t *num_headers, size_t last_len);
/* ditto */
int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len);
/* should be zero-filled before start */
struct phr_chunked_decoder {
size_t bytes_left_in_chunk; /* number of bytes left in current chunk */
char consume_trailer; /* if trailing headers should be consumed */
char _hex_count;
char _state;
};
/* the function rewrites the buffer given as (buf, bufsz) removing the chunked-
* encoding headers. When the function returns without an error, bufsz is
* updated to the length of the decoded data available. Applications should
* repeatedly call the function while it returns -2 (incomplete) every time
* supplying newly arrived data. If the end of the chunked-encoded data is
* found, the function returns a non-negative number indicating the number of
* octets left undecoded, that starts from the offset returned by `*bufsz`.
* Returns -1 on error.
*/
ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *bufsz);
/* returns if the chunked decoder is in middle of chunked data */
int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder);
#ifdef __cplusplus
}
#endif
#endif