Added a trivial HTTP server class.

master
Hannes Matuschek 11 years ago
parent 7c5cfb2019
commit a3a6165ede

@ -1,2 +1,4 @@
#add_subdirectory(libhttpd)
add_executable(sdr_cmd main.cc) add_executable(sdr_cmd main.cc)
target_link_libraries(sdr_cmd ${LIBS} libsdr) 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 "queue.hh"
#include "http.hh"
#include "logger.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 <iostream>
#include <csignal> #include <csignal>
using namespace sdr; using namespace sdr;
// On SIGINT -> stop queue properly class Application
static void __sigint_handler(int signo) { {
Queue::get().stop(); public:
} Application() {}
// Command line options bool echo(const http::JSON &request, http::JSON &response) {
static Options::Definition options[] = { response = request;
{"rtl2832", 'R', Options::FLOAT, return true;
"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}
}; };
void print_help() { static http::Server *server = 0;
std::cerr << "USAGE: sdr_cmd SOURCE [OPTIONS] OUTPUT" << std::endl << std::endl;
Options::print_help(std::cerr, options); 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[]) { int main(int argc, char *argv[]) {
// Parse command line options. Application app;
Options opts; server = new http::Server(8080);
if (! Options::parse(options, argc, argv, opts)) {
print_help(); return -1;
}
// If help flag is present -> print and done. // Install log handler
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; }
}
sdr::Logger::get().addHandler( 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 // Register signal handler:
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
signal(SIGINT, __sigint_handler); signal(SIGINT, __sigint_handler);
// Init audio system // Register callbacks
PortAudio::init(); server->addStatic("/", index_html, "text/html");
server->addJSON("/echo", &app, &Application::echo);
// Get the global queue // start server
Queue &queue = Queue::get(); 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; return 0;
} }

@ -1,13 +1,13 @@
# Sources of libsdr # Sources of libsdr
set(LIBSDR_SOURCES set(LIBSDR_SOURCES
buffer.cc node.cc queue.cc traits.cc portaudio.cc utils.cc wavfile.cc exception.cc logger.cc buffer.cc node.cc queue.cc traits.cc portaudio.cc utils.cc wavfile.cc exception.cc logger.cc
psk31.cc options.cc fsk.cc ax25.cc aprs.cc baudot.cc pocsag.cc bch31_21.cc) psk31.cc options.cc fsk.cc ax25.cc aprs.cc baudot.cc pocsag.cc bch31_21.cc http.cc)
set(LIBSDR_HEADERS sdr.hh math.hh set(LIBSDR_HEADERS sdr.hh math.hh
buffer.hh node.hh queue.hh buffernode.hh filternode.hh traits.hh autocast.hh buffer.hh node.hh queue.hh buffernode.hh filternode.hh traits.hh autocast.hh
siggen.hh portaudio.hh utils.hh wavfile.hh demod.hh firfilter.hh siggen.hh portaudio.hh utils.hh wavfile.hh demod.hh firfilter.hh
fftplan.hh fftplan_native.hh exception.hh baseband.hh freqshift.hh subsample.hh fftplan.hh fftplan_native.hh exception.hh baseband.hh freqshift.hh subsample.hh
combine.hh logger.hh psk31.hh interpolate.hh operators.hh options.hh fsk.hh ax25.hh combine.hh logger.hh psk31.hh interpolate.hh operators.hh options.hh fsk.hh ax25.hh
aprs.hh baudot.hh pocsag.hh bch31_21.hh) aprs.hh baudot.hh pocsag.hh bch31_21.hh http.hh)
if(SDR_WITH_PORTAUDIO) if(SDR_WITH_PORTAUDIO)
set(LIBSDR_SOURCES ${LIBSDR_SOURCES} portaudio.cc) set(LIBSDR_SOURCES ${LIBSDR_SOURCES} portaudio.cc)

@ -241,34 +241,28 @@ protected:
/** The actual demodulation. */ /** The actual demodulation. */
void _process(const Buffer< std::complex<iScalar> > &in, const Buffer<oScalar> &out) void _process(const Buffer< std::complex<iScalar> > &in, const Buffer<oScalar> &out)
{ {
// The last input value
std::complex<iScalar> last_value = _last_value;
// calc first value // calc first value
SScalar a = (SScalar(in[0].real())*SScalar(last_value.real()))/2 SScalar a = (SScalar(in[0].real())*SScalar(_last_value.real()))/2
+ (SScalar(in[0].imag())*SScalar(last_value.imag()))/2; + (SScalar(in[0].imag())*SScalar(_last_value.imag()))/2;
SScalar b = (SScalar(in[0].imag())*SScalar(last_value.real()))/2 SScalar b = (SScalar(in[0].imag())*SScalar(_last_value.real()))/2
- (SScalar(in[0].real())*SScalar(last_value.imag()))/2; - (SScalar(in[0].real())*SScalar(_last_value.imag()))/2;
a >>= Traits<iScalar>::shift; b >>= Traits<iScalar>::shift; a >>= Traits<iScalar>::shift; b >>= Traits<iScalar>::shift;
// update last value // update last value
last_value = in[0]; _last_value = in[0];
// calc output (prob. overwriting the last value) // calc output (prob. overwriting the last value)
out[0] = fast_atan2<iScalar, oScalar>(a, b); out[0] = fast_atan2<iScalar, oScalar>(a, b);
//out[0] = (1<<12)*(std::atan2(float(a),float(b))/M_PI);
// Calc remaining values // Calc remaining values
for (size_t i=1; i<in.size(); i++) { for (size_t i=1; i<in.size(); i++) {
a = (SScalar(in[i].real())*SScalar(last_value.real()))/2 a = (SScalar(in[i].real())*SScalar(_last_value.real()))/2
+ (SScalar(in[i].imag())*SScalar(last_value.imag()))/2; + (SScalar(in[i].imag())*SScalar(_last_value.imag()))/2;
b = (SScalar(in[i].imag())*SScalar(last_value.real()))/2 b = (SScalar(in[i].imag())*SScalar(_last_value.real()))/2
- (SScalar(in[i].real())*SScalar(last_value.imag()))/2; - (SScalar(in[i].real())*SScalar(_last_value.imag()))/2;
a >>= Traits<iScalar>::shift; b >>= Traits<iScalar>::shift; a >>= Traits<iScalar>::shift; b >>= Traits<iScalar>::shift;
last_value = in[i]; _last_value = in[i];
out[i] = fast_atan2<iScalar,oScalar>(a, b); out[i] = fast_atan2<iScalar,oScalar>(a, b);
//out[i] = (1<<12)*(std::atan2(float(a),float(b))/M_PI);
} }
// Store last value
_last_value = last_value;
// propergate result // propergate result
this->send(out.head(in.size())); this->send(out.head(in.size()));
} }

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__

@ -29,7 +29,6 @@ template <> inline int16_t fast_atan2<uint8_t, int16_t>(uint8_t ua, uint8_t ub)
/** Implementation of atan2 approximation using integers. */ /** Implementation of atan2 approximation using integers. */
template <> inline int16_t fast_atan2<int16_t, int16_t>(int16_t a, int16_t b) { template <> inline int16_t fast_atan2<int16_t, int16_t>(int16_t a, int16_t b) {
//return (1<<15)*(std::atan2(float(a), float(b))/M_PI);
const int32_t pi4 = (1<<12); const int32_t pi4 = (1<<12);
const int32_t pi34 = 3*(1<<12); const int32_t pi34 = 3*(1<<12);
int32_t aabs, angle; int32_t aabs, angle;

Loading…
Cancel
Save