mirror of https://github.com/hmatuschek/libsdr
Added a trivial HTTP server class.
parent
7c5cfb2019
commit
a3a6165ede
@ -1,2 +1,4 @@
|
||||
#add_subdirectory(libhttpd)
|
||||
|
||||
add_executable(sdr_cmd main.cc)
|
||||
target_link_libraries(sdr_cmd ${LIBS} libsdr)
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
#include "json.hh"
|
||||
|
||||
JSON::JSON()
|
||||
{
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
#ifndef __SDR_HTTP_JSON_HH__
|
||||
#define __SDR_HTTP_JSON_HH__
|
||||
|
||||
|
||||
#endif // __SDR_HTTP_JSON_HH__
|
||||
@ -1,225 +1,64 @@
|
||||
#include "options.hh"
|
||||
#include <stdio.h>
|
||||
|
||||
#include "queue.hh"
|
||||
#include "http.hh"
|
||||
#include "logger.hh"
|
||||
#include "wavfile.hh"
|
||||
#include "autocast.hh"
|
||||
#include "portaudio.hh"
|
||||
#include "rtlsource.hh"
|
||||
#include "baseband.hh"
|
||||
#include "demod.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <csignal>
|
||||
|
||||
using namespace sdr;
|
||||
|
||||
// On SIGINT -> stop queue properly
|
||||
static void __sigint_handler(int signo) {
|
||||
Queue::get().stop();
|
||||
}
|
||||
class Application
|
||||
{
|
||||
public:
|
||||
Application() {}
|
||||
|
||||
// Command line options
|
||||
static Options::Definition options[] = {
|
||||
{"rtl2832", 'R', Options::FLOAT,
|
||||
"Specifies a RTL2832 USB dongle as the input source"},
|
||||
{"rtl-device", 0, Options::INTEGER,
|
||||
"Specifies the RTL2832 device index. (default 0)"},
|
||||
{"disable-rtl-agc", 0 , Options::FLAG,
|
||||
"Disables the IF AGC of the RTL2832 device, default on."},
|
||||
{"rtl-agc-gain", 0, Options::INTEGER,
|
||||
"In conjecture with --disable-rtl-agc, specifies the fixed IF gain of the RTL2832 device."},
|
||||
{"rtl-ppm", 0, Options::FLOAT,
|
||||
"Specifies the frequency correction for the RTL2832 device in parts-per-million (ppm)."},
|
||||
{"audio", 'a', Options::FLAG,
|
||||
"Specifies the system audio as the input source."},
|
||||
{"audio-iq", 'a', Options::FLAG,
|
||||
"Specifies the system audio as the input source (I/Q channels)."},
|
||||
{"source-rate", 0, Options::FLOAT,
|
||||
"Specifies the sample rate of the input device."},
|
||||
{"file", 'f', Options::ANY,
|
||||
"Specifies a WAV file as input source."},
|
||||
{"demod", 'd', Options::ANY,
|
||||
"Specifies the demodulator (wfm, nfm, am, usb, lsb)."},
|
||||
{"demod-offset", 0, Options::FLOAT,
|
||||
"Specifies the reception offset in Hz. (default 0)"},
|
||||
{"loglevel", 0, Options::INTEGER,
|
||||
"Specifies the log-level. 0 = DEBUG, 1 = INFO, 2 = WARNING, 3 = ERROR."},
|
||||
{"help", '?', Options::FLAG,
|
||||
"Prints this help."},
|
||||
{0,0,Options::FLAG,0}
|
||||
bool echo(const http::JSON &request, http::JSON &response) {
|
||||
response = request;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void print_help() {
|
||||
std::cerr << "USAGE: sdr_cmd SOURCE [OPTIONS] OUTPUT" << std::endl << std::endl;
|
||||
Options::print_help(std::cerr, options);
|
||||
static http::Server *server = 0;
|
||||
|
||||
static void __sigint_handler(int signo) {
|
||||
server->stop(true);
|
||||
}
|
||||
|
||||
const char *index_html = "<html>"
|
||||
"<head></head>"
|
||||
"<body>"
|
||||
"<b>It is alive!</b>"
|
||||
"<body>"
|
||||
"</html>";
|
||||
|
||||
|
||||
bool
|
||||
json_echo(const http::JSON &request, http::JSON &response) {
|
||||
response = request;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Parse command line options.
|
||||
Options opts;
|
||||
if (! Options::parse(options, argc, argv, opts)) {
|
||||
print_help(); return -1;
|
||||
}
|
||||
Application app;
|
||||
server = new http::Server(8080);
|
||||
|
||||
// If help flag is present -> print and done.
|
||||
if (opts.has("help")) { print_help(); return 0; }
|
||||
|
||||
// Install log-handler
|
||||
LogLevel loglevel = LOG_INFO;
|
||||
if (opts.has("loglevel")) {
|
||||
if (0 >= opts.get("loglevel").toInteger()) { loglevel = LOG_DEBUG; }
|
||||
else if (1 >= opts.get("loglevel").toInteger()) { loglevel = LOG_INFO; }
|
||||
else if (2 >= opts.get("loglevel").toInteger()) { loglevel = LOG_WARNING; }
|
||||
else { loglevel = LOG_ERROR; }
|
||||
}
|
||||
// Install log handler
|
||||
sdr::Logger::get().addHandler(
|
||||
new sdr::StreamLogHandler(std::cerr, loglevel));
|
||||
new sdr::StreamLogHandler(std::cerr, sdr::LOG_DEBUG));
|
||||
|
||||
// If no source has been selected -> error
|
||||
if (! (opts.has("rtl2832")|opts.has("audio")|opts.has("file"))) {
|
||||
std::cerr << "No source has been selected!" << std::endl;
|
||||
print_help(); return -1;
|
||||
}
|
||||
|
||||
// If no demodulator has been selected.
|
||||
if (!opts.has("demod")) {
|
||||
std::cerr << "No demodulator has been selected!" << std::endl;
|
||||
print_help(); return -1;
|
||||
}
|
||||
|
||||
double bbFc = 0, bbFf = 0, bbFw = 0, bbFs = 0;
|
||||
// Configure baseband node depending on the selected demodulator
|
||||
if ("wfm" == opts.get("demod").toString()) {
|
||||
bbFw = 100e3; bbFs = 100e3;
|
||||
} else if ("nfm" == opts.get("demod").toString()) {
|
||||
bbFw = 12.5e3; bbFs = 22.05e3;
|
||||
} else if ("am" == opts.get("demod").toString()) {
|
||||
bbFw = 10.0e3; bbFs = 22.05e3;
|
||||
} else if ("usb" == opts.get("demod").toString()) {
|
||||
bbFf = 1.5e3; bbFw = 3e3; bbFs = 22.05e3;
|
||||
} else if ("lsb" == opts.get("demod").toString()) {
|
||||
bbFf = -1.5e3; bbFw = 3e3; bbFs = 22.05e3;
|
||||
} else {
|
||||
std::cerr << "Unknown demodulator '" << opts.get("demod").toString() << "'." << std::endl;
|
||||
print_help(); return -1;
|
||||
}
|
||||
|
||||
// Register signal handler to stop queue on SIGINT
|
||||
// Register signal handler:
|
||||
signal(SIGINT, __sigint_handler);
|
||||
|
||||
// Init audio system
|
||||
PortAudio::init();
|
||||
// Register callbacks
|
||||
server->addStatic("/", index_html, "text/html");
|
||||
server->addJSON("/echo", &app, &Application::echo);
|
||||
|
||||
// Get the global queue
|
||||
Queue &queue = Queue::get();
|
||||
// start server
|
||||
server->start(true);
|
||||
|
||||
// reference to selected source node
|
||||
Source *source = 0;
|
||||
// Nodes for file input
|
||||
WavSource *wav_src=0;
|
||||
AutoCast< std::complex<int16_t> > *wav_cast=0;
|
||||
// nodes for real audio input
|
||||
PortSource<int16_t> *raudio_src=0;
|
||||
AutoCast< std::complex<int16_t> > *raudio_cast=0;
|
||||
// nodes for IQ audio input
|
||||
PortSource< std::complex<int16_t> > *iqaudio_src=0;
|
||||
// nodes for RTL2832 input
|
||||
RTLSource *rtl_source=0;
|
||||
AutoCast< std::complex<int16_t> > *rtl_cast=0;
|
||||
|
||||
/* Demodulator nodes. */
|
||||
Source *demod = 0;
|
||||
FMDemod<int16_t> *fm_demod = 0;
|
||||
FMDeemph<int16_t> *fm_deemph = 0;
|
||||
AMDemod<int16_t> *am_demod = 0;
|
||||
USBDemod<int16_t> *ssb_demod = 0;
|
||||
|
||||
// Dispatch by selected source
|
||||
if (opts.has("rtl2832")) {
|
||||
// Instantiate & config RTL2832 node
|
||||
size_t devIdx = 0;
|
||||
double sampleRate = 1e6;
|
||||
if (opts.has("rtl-device")) { devIdx = opts.get("rtl-device").toInteger(); }
|
||||
if (opts.has("source-rate")) { sampleRate = opts.get("source-rate").toFloat(); }
|
||||
rtl_source = new RTLSource(opts.get("rtl2832").toFloat(), sampleRate, devIdx);
|
||||
if (opts.has("rtl-disable-agc")) { rtl_source->enableAGC(false); }
|
||||
if (opts.has("rtl-gain")) { rtl_source->setGain(opts.get("rtl-gain").toFloat()); }
|
||||
if (opts.has("rtl-ppm")) { rtl_source->setFreqCorrection(opts.get("rtl-ppm").toFloat()); }
|
||||
rtl_cast = new AutoCast< std::complex<int16_t> >();
|
||||
rtl_source->connect(rtl_cast);
|
||||
source = rtl_cast;
|
||||
// Connect start & stop event to RTL2832 device
|
||||
queue.addStart(rtl_source, &RTLSource::start);
|
||||
queue.addStop(rtl_source, &RTLSource::stop);
|
||||
} else if (opts.has("audio")) {
|
||||
double sampleRate = 44100;
|
||||
if (opts.has("source-rate")) { sampleRate = opts.get("source-rate").toFloat(); }
|
||||
raudio_src = new PortSource<int16_t>(sampleRate, 1024);
|
||||
raudio_cast = new AutoCast< std::complex<int16_t> >();
|
||||
source = raudio_cast;
|
||||
// Connect idle event to source
|
||||
queue.addIdle(raudio_src, &PortSource<int16_t>::next);
|
||||
} else if (opts.has("audio-iq")) {
|
||||
double sampleRate = 44100;
|
||||
if (opts.has("source-rate")) { sampleRate = opts.get("source-rate").toFloat(); }
|
||||
iqaudio_src = new PortSource< std::complex<int16_t> >(sampleRate, 1024);
|
||||
source = iqaudio_src;
|
||||
// Connect idle event to source
|
||||
queue.addIdle(iqaudio_src, &PortSource< std::complex<int16_t> >::next);
|
||||
}
|
||||
|
||||
// Shift baseband by offset
|
||||
if (opts.has("demod-offset")) {
|
||||
bbFc += opts.get("demod-offset").toFloat();
|
||||
bbFf += opts.get("demod-offset").toFloat();
|
||||
}
|
||||
|
||||
IQBaseBand< int16_t > baseband(bbFc, bbFf, bbFw, 31, 0, bbFs);
|
||||
source->connect(&baseband, true);
|
||||
|
||||
// Dispatch by selected demodulator
|
||||
if (("wfm" == opts.get("demod").toString()) || ("nfm" == opts.get("demod").toString())) {
|
||||
fm_demod = new FMDemod<int16_t>();
|
||||
fm_deemph = new FMDeemph<int16_t>();
|
||||
source->connect(fm_demod, true);
|
||||
fm_demod->connect(fm_deemph, true);
|
||||
demod = fm_deemph;
|
||||
} else if ("am" == opts.get("demod").toString()) {
|
||||
am_demod = new AMDemod<int16_t>();
|
||||
source->connect(am_demod, true);
|
||||
demod = am_demod;
|
||||
} else if (("usb" == opts.get("demod").toString()) || ("lsb" == opts.get("demod").toString())) {
|
||||
ssb_demod = new USBDemod<int16_t>();
|
||||
source->connect(ssb_demod, true);
|
||||
demod = ssb_demod;
|
||||
}
|
||||
|
||||
// Enable playback
|
||||
PortSink audio_sink;
|
||||
demod->connect(&audio_sink, true);
|
||||
|
||||
// Start queue & processing
|
||||
queue.start();
|
||||
// Wait for queue to to stop
|
||||
queue.wait();
|
||||
|
||||
// Free allocated input nodes
|
||||
if (rtl_source) { delete rtl_source; }
|
||||
if (rtl_cast) { delete rtl_cast; }
|
||||
if (raudio_src) { delete raudio_src; }
|
||||
if (raudio_cast) { delete raudio_cast; }
|
||||
if (iqaudio_src) { delete iqaudio_src; }
|
||||
if (wav_src) { delete wav_src; }
|
||||
if (wav_cast) { delete wav_cast; }
|
||||
if (fm_demod) { delete fm_demod; }
|
||||
if (fm_deemph) { delete fm_deemph; }
|
||||
if (am_demod) { delete am_demod; }
|
||||
if (ssb_demod) { delete ssb_demod; }
|
||||
|
||||
PortAudio::terminate();
|
||||
|
||||
// Done.
|
||||
return 0;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,521 @@
|
||||
/** @defgroup httpd A rather trivia HTTP daemon implementation. */
|
||||
|
||||
#ifndef __SDR_HTTPD_HH__
|
||||
#define __SDR_HTTPD_HH__
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <list>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
|
||||
namespace sdr {
|
||||
namespace http {
|
||||
|
||||
// Forward declarations
|
||||
class Server;
|
||||
|
||||
|
||||
/** Represents a JSON object.
|
||||
* @ingroup http */
|
||||
class JSON
|
||||
{
|
||||
public:
|
||||
/** Specifies the possible json types. */
|
||||
typedef enum {
|
||||
EMPTY, ///< null.
|
||||
BOOLEAN, ///< A boolean value.
|
||||
NUMBER, ///< A number.
|
||||
STRING, ///< A string.
|
||||
ARRAY, ///< An array or list.
|
||||
TABLE ///< A table or object.
|
||||
} Type;
|
||||
|
||||
public:
|
||||
/** Empty constructor (null). */
|
||||
JSON();
|
||||
/** Constructs a boolean value. */
|
||||
JSON(bool value);
|
||||
/** Constructs a number. */
|
||||
JSON(double value);
|
||||
/** Constructs a string. */
|
||||
JSON(const std::string &value);
|
||||
/** Constructs a list. */
|
||||
JSON(const std::list<JSON> &list);
|
||||
/** Constructs a table. */
|
||||
JSON(const std::map<std::string, JSON> &table);
|
||||
/** Copy constructor (deep copy). */
|
||||
JSON(const JSON &other);
|
||||
/** Destructor. */
|
||||
~JSON();
|
||||
/** Assignment operator (deep copy). */
|
||||
JSON &operator=(const JSON &other);
|
||||
|
||||
/** Retruns @c true for the null. */
|
||||
inline bool isNull() const { return EMPTY == _type; }
|
||||
/** Returns @c true if this is a boolean value. */
|
||||
inline bool isBoolean() const { return BOOLEAN == _type; }
|
||||
/** Returns the boolean value. */
|
||||
inline const bool &asBoolean() const { return *_value.boolean; }
|
||||
/** Returns @c true if this is a number. */
|
||||
inline bool isNumber() const { return NUMBER == _type; }
|
||||
/** Returns the number. */
|
||||
inline const double &asNumber() const { return *_value.number; }
|
||||
/** Returns @c true if this is a string. */
|
||||
inline bool isString() const { return STRING == _type; }
|
||||
/** Returns the string. */
|
||||
inline const std::string &asString() const { return *_value.string; }
|
||||
/** Returns @c true if this is an array. */
|
||||
inline bool isArray() const { return ARRAY == _type; }
|
||||
/** Retruns the array as a list. */
|
||||
inline const std::list<JSON> &asArray() const { return *_value.list; }
|
||||
/** Returns @c true if this is a table. */
|
||||
inline bool isTable() const { return TABLE == _type; }
|
||||
/** Returns the table. */
|
||||
inline const std::map<std::string, JSON> &asTable() const { return *_value.table; }
|
||||
/** Resets the JSON object to null. */
|
||||
void clear();
|
||||
|
||||
/** Parses the given string. Retruns @c true on success. */
|
||||
static bool parse(const std::string &text, JSON &obj);
|
||||
/** Serializes the JSON object. */
|
||||
void serialize(std::ostream &stream) const;
|
||||
/** Serializes the JSON object. */
|
||||
inline void serialize(std::string &text) const {
|
||||
std::stringstream buffer; this->serialize(buffer);
|
||||
text = buffer.str();
|
||||
}
|
||||
|
||||
protected:
|
||||
/** The type of the object. */
|
||||
Type _type;
|
||||
/** Union of value pointers. */
|
||||
union {
|
||||
/** The boolean value. */
|
||||
bool *boolean;
|
||||
/** The number. */
|
||||
double *number;
|
||||
/** The string. */
|
||||
std::string *string;
|
||||
/** The array. */
|
||||
std::list<JSON> *list;
|
||||
/** The table. */
|
||||
std::map<std::string, JSON> *table;
|
||||
} _value;
|
||||
};
|
||||
|
||||
|
||||
/** Lists the possible HTTP methods.
|
||||
* @ingroup http */
|
||||
typedef enum {
|
||||
HTTP_UNKNOWN,
|
||||
HTTP_GET,
|
||||
HTTP_HEAD,
|
||||
HTTP_POST
|
||||
} Method;
|
||||
|
||||
/** Lists the possible HTTP versions.
|
||||
* @ingroup http */
|
||||
typedef enum {
|
||||
UNKNOWN_VERSION,
|
||||
HTTP_1_0,
|
||||
HTTP_1_1
|
||||
} Version;
|
||||
|
||||
/** Represents a URL.
|
||||
* @ingroup http */
|
||||
class URL
|
||||
{
|
||||
public:
|
||||
/** Empty constructor. */
|
||||
URL();
|
||||
/** Constructor from protocol, host and path. */
|
||||
URL(const std::string &proto, const std::string &host, const std::string &path);
|
||||
/** Copy constructor. */
|
||||
URL(const URL &other);
|
||||
|
||||
/** Assignment operator. */
|
||||
URL &operator=(const URL &other);
|
||||
|
||||
/** Parses a URL from the given string. */
|
||||
static URL fromString(const std::string &url);
|
||||
/** Serializes the URL into a string. */
|
||||
std::string toString() const;
|
||||
|
||||
/** Returns @c true if the URL specifies a protocol. */
|
||||
inline bool hasProtocol() const { return (0 != _protocol.size()); }
|
||||
/** Returns the protocol. */
|
||||
inline const std::string protocol() const { return _protocol; }
|
||||
/** Sets the protocol. */
|
||||
inline void setProtocol(const std::string &proto) { _protocol = proto; }
|
||||
/** Returns @c true if the URL specified a host name. */
|
||||
inline bool hasHost() const { return (0 != _host.size()); }
|
||||
/** Returns the host name. */
|
||||
inline const std::string &host() const { return _host; }
|
||||
/** Set the host name. */
|
||||
inline void setHost(const std::string &host) { _host = host; }
|
||||
/** Retruns the path of the URL. */
|
||||
inline const std::string &path() const { return _path; }
|
||||
/** Sets the path of the URL. */
|
||||
inline void setPath(const std::string &path) { _path = path; }
|
||||
/** Adds a query pair (key, value) to the URL. */
|
||||
inline void addQuery(const std::string &name, const std::string &value) {
|
||||
_query.push_back(std::pair<std::string, std::string>(name, value));
|
||||
}
|
||||
/** Returns the list of query (key, value) pairs. */
|
||||
inline const std::list< std::pair<std::string, std::string> > &query() const {
|
||||
return _query;
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Holds the protocol. */
|
||||
std::string _protocol;
|
||||
/** Holds the host name. */
|
||||
std::string _host;
|
||||
/** Holds the path. */
|
||||
std::string _path;
|
||||
/** Holds the query pairs. */
|
||||
std::list< std::pair<std::string, std::string> > _query;
|
||||
};
|
||||
|
||||
|
||||
/** Represents a HTTP request.
|
||||
* @ingroup http */
|
||||
class Request
|
||||
{
|
||||
public:
|
||||
/** Constructor. */
|
||||
Request(int socket);
|
||||
|
||||
/** Parses the HTTP request header, returns @c true on success. */
|
||||
bool parse();
|
||||
|
||||
/** Returns @c true if the connection to the client is kept alive after the response
|
||||
* has been send. */
|
||||
bool isKeepAlive() const;
|
||||
|
||||
/** Returns @c true if the given header is present. */
|
||||
bool hasHeader(const std::string &name) const;
|
||||
/** Returns the value of the given header. */
|
||||
std::string header(const std::string &name) const;
|
||||
|
||||
/** Returns @c true if the Content-Length header is present. */
|
||||
inline bool hasContentLength() const { return hasHeader("Content-Length"); }
|
||||
/** Returns the value of the Content-Length header. */
|
||||
size_t contentLength() const { return atol(header("Content-Length").c_str()); }
|
||||
|
||||
/** Retruns the request method. */
|
||||
inline Method method() const { return _method; }
|
||||
/** Returns the request URL. */
|
||||
inline const URL &url() const { return _url; }
|
||||
/** Allows to read data from the connection (i.e. the request body). */
|
||||
inline int read(const char *data, size_t size) {
|
||||
return ::read(_socket, (void *)data, size);
|
||||
}
|
||||
/** Reads the complete body (if Content-Length header is present).
|
||||
* Retruns @c true on success.*/
|
||||
bool readBody(std::string &body) const;
|
||||
|
||||
protected:
|
||||
/** The connection socket. */
|
||||
int _socket;
|
||||
/** The request method. */
|
||||
Method _method;
|
||||
/** The HTTP version. */
|
||||
Version _version;
|
||||
/** The request URL. */
|
||||
URL _url;
|
||||
/** The request headers. */
|
||||
std::map<std::string, std::string> _headers;
|
||||
};
|
||||
|
||||
|
||||
/** Represents a HTTP response. */
|
||||
class Response
|
||||
{
|
||||
public:
|
||||
/** Defines all possible responses. */
|
||||
typedef enum {
|
||||
STATUS_OK = 200,
|
||||
STATUS_BAD_REQUEST = 400,
|
||||
STATUS_NOT_FOUND = 404,
|
||||
STATUS_SERVER_ERROR = 500
|
||||
} Status;
|
||||
|
||||
public:
|
||||
/** Constructor.
|
||||
* @param socket Specifies the socket over which the response will be send.*/
|
||||
Response(int socket);
|
||||
|
||||
/** Specifies the response code. */
|
||||
void setStatus(Status status);
|
||||
/** Returns @c true if the response has the given header. */
|
||||
bool hasHeader(const std::string &name) const;
|
||||
/** Returns the value of the header. */
|
||||
std::string header(const std::string &name) const;
|
||||
/** Sets the header. */
|
||||
void setHeader(const std::string &name, const std::string &value);
|
||||
/** Helper function to set the content length. */
|
||||
void setContentLength(size_t length);
|
||||
|
||||
/** Sends the response code and all defined headers. */
|
||||
bool sendHeaders() const;
|
||||
|
||||
/** Sends some data through the socket. To send the response body, call
|
||||
* @c sendHeaders() first. */
|
||||
bool send(const std::string &data) const;
|
||||
|
||||
/** Returns @c true if the connection will be closed after the response has been send.
|
||||
* @c I.e. if there was an error. */
|
||||
inline bool closeConnection() const { return _close_connection; }
|
||||
/** Marks that the connection will be closed after the response has been send. */
|
||||
inline void setCloseConnection() { _close_connection = true; }
|
||||
|
||||
protected:
|
||||
/** The socket over which the response will be send. */
|
||||
int _socket;
|
||||
/** The response code. */
|
||||
Status _status;
|
||||
/** The response headers. */
|
||||
std::map<std::string, std::string> _headers;
|
||||
/** If @c true, the connection will be closed after the response has been send. */
|
||||
bool _close_connection;
|
||||
};
|
||||
|
||||
|
||||
/** Implements a HTTP connection to a client.
|
||||
* @c ingroup http */
|
||||
class Connection
|
||||
{
|
||||
public:
|
||||
/** Constructor. */
|
||||
Connection(Server *server, int socket);
|
||||
/** Destructor. */
|
||||
~Connection();
|
||||
|
||||
/** Closes the connection.
|
||||
* If @c wait is @c true, the method will wait until the thread listening for incomming
|
||||
* request joined. */
|
||||
void close(bool wait=false);
|
||||
/** Returns @c true if the connection is closed. */
|
||||
bool isClosed() const;
|
||||
|
||||
protected:
|
||||
/** Main loop for incomming requests. */
|
||||
static void *_main(void *ctx);
|
||||
|
||||
protected:
|
||||
/** A weak reference to the server instance. */
|
||||
Server *_server;
|
||||
/** The connection socket. */
|
||||
int _socket;
|
||||
/** The thread processing requests for this connection. */
|
||||
pthread_t _thread;
|
||||
};
|
||||
|
||||
|
||||
/** Base class of all HTTP request handlers.
|
||||
* @ingroup http */
|
||||
class Handler
|
||||
{
|
||||
protected:
|
||||
/** Hidden constructor. */
|
||||
Handler();
|
||||
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~Handler();
|
||||
/** Needs to be implemented to accept the given request.
|
||||
* If this method returns @c true, the server will call the @c handle instance next
|
||||
* in expectation that this instance will process the request. */
|
||||
virtual bool match(const Request &request) = 0;
|
||||
/** Needs to be implemented to process requests, which have been accepted by the @c match()
|
||||
* method. */
|
||||
virtual void handle(const Request &request, Response &response) = 0;
|
||||
};
|
||||
|
||||
|
||||
/** Serves some static content.
|
||||
* @ingroup http */
|
||||
class StaticHandler: public Handler
|
||||
{
|
||||
public:
|
||||
/** Constructor.
|
||||
* @param url Specifies the URL (path) of the static content.
|
||||
* @param text Speciefies the content.
|
||||
* @param mimeType Speficies the mime-type of the content. */
|
||||
StaticHandler(const std::string &url, const std::string &text,
|
||||
const std::string mimeType="text/text");
|
||||
/** Destructor. */
|
||||
virtual ~StaticHandler();
|
||||
|
||||
bool match(const Request &request);
|
||||
void handle(const Request &request, Response &response);
|
||||
|
||||
protected:
|
||||
/** Holds the URL (path) of the content. */
|
||||
std::string _url;
|
||||
/** Holds the mime-type of the content. */
|
||||
std::string _mimeType;
|
||||
/** Holds the content itself. */
|
||||
std::string _text;
|
||||
};
|
||||
|
||||
|
||||
/** Utility function to provide a handler as a delegate.
|
||||
* @ingroup http */
|
||||
template <class T>
|
||||
class DelegateHandler: public Handler
|
||||
{
|
||||
public:
|
||||
/** Constructor.
|
||||
* @param url Specifies the path of the handler.
|
||||
* @param instance Specifies the instance of the delegate.
|
||||
* @param func Specifies the method of the delegate instance being called. */
|
||||
DelegateHandler(const std::string &url, T *instance, void (T::*func)(const Request &, Response &))
|
||||
: Handler(), _url(url), _instance(instance), _callback(func)
|
||||
{
|
||||
// pass...
|
||||
}
|
||||
|
||||
bool match(const Request &request) {
|
||||
return this->_url == request.url().path();
|
||||
}
|
||||
|
||||
void handle(const Request &request, Response &response) {
|
||||
(this->_instance->*(this->_callback))(request, response);
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Holds the path of the handler. */
|
||||
std::string _url;
|
||||
/** Holds the instance of the delegate. */
|
||||
T *_instance;
|
||||
/** Holds the method of the instance being called. */
|
||||
void (T::*_callback)(const Request &, Response &);
|
||||
};
|
||||
|
||||
|
||||
/** Implements a specialized handler to ease the processing of JSON REST-APIs.
|
||||
* @ingroup http */
|
||||
class JSONHandler: public http::Handler
|
||||
{
|
||||
public:
|
||||
/** Constructor.
|
||||
* @param url Speficies the path of the method. */
|
||||
JSONHandler(const std::string &url);
|
||||
|
||||
bool match(const http::Request &request);
|
||||
void handle(const http::Request &request, http::Response &response);
|
||||
|
||||
/** Needs to be implemented to process the request and assemble the result.
|
||||
* An error (400 BAD REQUEST) will be send if the function does not return @c true. */
|
||||
virtual bool process(const JSON &request, JSON &result) = 0;
|
||||
|
||||
protected:
|
||||
/** The URL of the method. */
|
||||
std::string _url;
|
||||
};
|
||||
|
||||
|
||||
/** A delegate JSON handler.
|
||||
* @ingroup http */
|
||||
template <class T>
|
||||
class DelegateJSONHandler: public JSONHandler
|
||||
{
|
||||
public:
|
||||
/** Constructor. */
|
||||
DelegateJSONHandler(const std::string &url, T *instance,
|
||||
bool (T::*func)(const JSON &request, JSON &response))
|
||||
: JSONHandler(url), _instance(instance), _callback(func)
|
||||
{
|
||||
// pass...
|
||||
}
|
||||
|
||||
virtual bool process(const JSON &request, JSON &result) {
|
||||
return (this->_instance->*(this->_callback))(request, result);
|
||||
}
|
||||
|
||||
protected:
|
||||
/** The delegate instance. */
|
||||
T *_instance;
|
||||
/** The method to be called from the delegate instance. */
|
||||
bool (T::*_callback)(const JSON &request, JSON &response);
|
||||
};
|
||||
|
||||
|
||||
/** Implements a trivial HTTP/1.1 server.
|
||||
* @ingroup httpd*/
|
||||
class Server
|
||||
{
|
||||
public:
|
||||
/** Constructor.
|
||||
* @param port Specifies the port number to listen on. */
|
||||
Server(uint port);
|
||||
/** Destructor. */
|
||||
~Server();
|
||||
|
||||
/** Starts the server. */
|
||||
void start(bool wait=false);
|
||||
/** Stops the server. */
|
||||
void stop(bool wait=false);
|
||||
/** Wait for the server thread to join. */
|
||||
void wait();
|
||||
|
||||
/** Adds a generic handler to the dispatcher. */
|
||||
void addHandler(Handler *handler);
|
||||
/** Adds a delegate to the dispatcher. */
|
||||
template <class T>
|
||||
void addHandler(const std::string &url,
|
||||
T *instance, void (T::*func)(const Request &, Response &)) {
|
||||
addHandler(new DelegateHandler<T>(url, instance, func));
|
||||
}
|
||||
/** Adds a JSON delegate to the dispatcher. */
|
||||
template <class T>
|
||||
void addJSON(const std::string &url,
|
||||
T *instance, bool (T::*func)(const JSON &request, JSON &result)) {
|
||||
addHandler(new DelegateJSONHandler<T>(url, instance, func));
|
||||
}
|
||||
/** Adds some static content to the dispatcher. */
|
||||
inline void addStatic(const std::string &url, const std::string &text) {
|
||||
addHandler(new StaticHandler(url, text));
|
||||
}
|
||||
/** Adds a generic handler to the dispatcher. */
|
||||
inline void addStatic(const std::string &url, const std::string &text, const std::string &mimeType) {
|
||||
addHandler(new StaticHandler(url, text, mimeType));
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Dispatches a request. */
|
||||
void dispatch(const Request &request, Response &response);
|
||||
/** The thread waiting for incomming connections. */
|
||||
static void *_listen_main(void *ctx);
|
||||
|
||||
protected:
|
||||
/** Port to bind to. */
|
||||
uint _port;
|
||||
/** The socket to listen on. */
|
||||
int _socket;
|
||||
/** While true, the server is listening on the port for incomming connections. */
|
||||
bool _is_running;
|
||||
/** The listen thread. */
|
||||
pthread_t _thread;
|
||||
/** All registered handler. */
|
||||
std::list<Handler *> _handler;
|
||||
/** All open connections. */
|
||||
std::set<Connection *> _connections;
|
||||
|
||||
/* Allow Connection to access dispatch(). */
|
||||
friend class Connection;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endif // __SDR_HTTPD_HH__
|
||||
Loading…
Reference in New Issue