31 #define _SSIZE_T_DEFINED 51 void UvAlloc(uv_handle_t *,
size_t suggested_size, uv_buf_t *buf)
54 *buf = uv_buf_init(
new char[suggested_size](), suggested_size);
60 assert(handle !=
nullptr);
67 assert(stream !=
nullptr);
69 auto *tcp =
static_cast<Connection *
>(stream->data);
70 assert(tcp !=
nullptr);
73 tcp->Read(nread, buf);
82 assert(server !=
nullptr);
83 if (status < 0)
return;
85 auto *io =
static_cast<IoCore *
>(server->data);
86 assert(io !=
nullptr);
94 assert(req !=
nullptr);
97 Debug() <<
"UvRespondCallback: got status:" << status
104 auto *buf =
static_cast<char *
>(req->data);
105 assert(buf !=
nullptr);
117 assert(handle !=
nullptr);
119 auto *io =
static_cast<IoCore *
>(handle->data);
120 assert(io !=
nullptr);
131 assert(handle !=
nullptr);
133 auto *player =
static_cast<Player *
>(handle->data);
134 assert(player !=
nullptr);
136 if (signum != SIGINT)
return;
138 Debug() <<
"Caught SIGINT, closing..." << std::endl;
148 assert(handle !=
nullptr);
151 Debug() <<
"UvShutdownCallback: got status:" << status
155 auto *conn =
static_cast<Connection *
>(handle->data);
156 assert(conn !=
nullptr);
174 void IoCore::Run(
const std::string &host,
const std::string &port)
176 this->
loop = uv_default_loop();
183 uv_run(this->
loop, UV_RUN_DEFAULT);
187 uv_loop_close(this->
loop);
192 assert(server !=
nullptr);
193 assert(this->
loop !=
nullptr);
195 auto client =
new uv_tcp_t();
196 uv_tcp_init(this->
loop, client);
199 if (uv_accept(server, (uv_stream_t *)client)) {
205 auto conn = std::make_shared<Connection>(*
this, client, this->
player, id);
206 client->data =
static_cast<void *
>(conn.get());
207 this->
pool[
id - 1] = std::move(conn);
211 .AddArg(std::to_string(
id))
215 .AddArg(
"player/file"));
238 assert(id <= this->
pool.size());
252 bool full = this->
pool.size() == (SIZE_MAX - 1);
255 this->
pool.emplace_back(
nullptr);
262 assert(0 < slot && slot <= this->
pool.size());
266 if (this->
pool.at(slot - 1)) {
267 this->
pool[slot - 1] =
nullptr;
271 assert(!this->
pool.at(slot - 1));
282 Debug() <<
"Shutting down..." << std::endl;
293 uv_close(reinterpret_cast<uv_handle_t *>(&this->
server),
nullptr);
296 for (
const auto conn : this->
pool) {
297 if (conn) conn->Shutdown();
301 uv_signal_stop(&this->
sigint);
302 uv_close(reinterpret_cast<uv_handle_t *>(&this->
sigint),
nullptr);
307 if (this->
pool.empty())
return;
318 Debug() <<
"broadcast:" << response.
Pack() << std::endl;
322 for (
const auto c : this->
pool) {
323 if (c) c->Respond(response);
329 assert(0 <
id && id <= this->
pool.size());
331 Debug() <<
"unicast @" << std::to_string(
id) <<
":" << response.
Pack()
334 auto c = this->
pool.at(
id - 1);
335 if (c) c->Respond(response);
340 assert(this->
loop !=
nullptr);
343 this->
updater.data =
static_cast<void *
>(
this);
351 assert(this->
loop !=
nullptr);
356 this->
server.data =
static_cast<void *
>(
this);
357 assert(this->
server.data !=
nullptr);
359 struct sockaddr_in bind_addr;
360 uv_ip4_addr(address.c_str(), std::stoi(port), &bind_addr);
361 uv_tcp_bind(&this->
server, (
const sockaddr *)&bind_addr, 0);
365 throw NetError(
"Could not listen on " + address +
":" + port +
366 " (" + std::string(uv_err_name(r)) +
")");
369 Debug() <<
"Listening at" << address <<
"on" << port << std::endl;
374 int r = uv_signal_init(this->
loop, &this->
sigint);
377 std::string(uv_err_name(r)));
384 assert(this->
sigint.data !=
nullptr);
393 : parent(parent), tcp(tcp), tokeniser(), player(player), id(id)
395 Debug() <<
"Opening connection from" <<
Name() << std::endl;
400 Debug() <<
"Closing connection from" <<
Name() << std::endl;
408 auto string = response.
Pack();
409 string.push_back(
'\n');
411 unsigned int l =
string.length();
415 auto buf = uv_buf_init(
new char[l], l);
416 assert(buf.base !=
nullptr);
417 string.copy(buf.base, l);
422 auto req =
new uv_write_t;
423 req->data =
static_cast<void *
>(buf.base);
425 uv_write((uv_write_t *)req, (uv_stream_t *)this->
tcp, &buf, 1,
436 struct sockaddr_storage s;
437 auto sp = (
struct sockaddr *)&s;
440 socklen_t namelen =
sizeof(s);
442 int pe = uv_tcp_getpeername(this->
tcp, sp, (
int *)&namelen);
446 if (pe)
return "<error@peer: " + std::string(uv_strerror(pe)) +
">";
449 char host[NI_MAXHOST];
450 char serv[NI_MAXSERV];
455 int ne = getnameinfo(sp, namelen, host,
sizeof(host), serv,
456 sizeof(serv), NI_NUMERICSERV);
458 if (ne)
return "<error@name: " + std::string(gai_strerror(ne)) +
">";
460 auto id = std::to_string(this->
id);
461 return id + std::string(
"!") + host + std::string(
":") + serv;
466 assert(buf !=
nullptr);
471 if (nread == UV_EOF) {
478 Debug() <<
"Error on" <<
Name() <<
"-" << uv_err_name(nread)
485 if (buf->base ==
nullptr)
return;
488 auto cmds = this->
tokeniser.
Feed(std::string(buf->base, nread));
489 for (
auto cmd : cmds) {
490 if (cmd.empty())
continue;
508 auto nargs = cmd.size() - 2;
513 if (
"end" == word)
return this->
player.
End(tag);
514 if (
"eject" == word)
return this->
player.
Eject(tag);
515 if (
"dump" == word)
return this->
player.
Dump(
id, tag);
516 }
else if (nargs == 1) {
517 if (
"fload" == word)
return this->
player.
Load(tag, cmd[2]);
518 if (
"pos" == word)
return this->
player.
Pos(tag, cmd[2]);
526 auto req =
new uv_shutdown_t;
527 assert(req !=
nullptr);
531 uv_shutdown(req, reinterpret_cast<uv_stream_t *>(this->
tcp),
Declarations of the playd Error exception set.
void UvShutdownCallback(uv_shutdown_t *handle, int status)
The callback fired when a client is shut down.
const std::string MSG_IO_CANNOT_ALLOC
Message shown when allocating an IO object fails.
const std::string MSG_CMD_SHORT
Message shown when the CommandHandler receives an under-length command.
size_t NextConnectionID()
Acquires the next available connection ID.
bool Update()
Instructs the Player to perform a cycle of work.
void UvWriteCallback(uv_write_t *req, int status)
The callback fired when a response has been sent to a client.
void Read(ssize_t nread, const uv_buf_t *buf)
Processes a data read on this connection.
Player & player
The Player to which finished commands should be sent.
std::string Pack() const
Packs the Response, converting it to a BAPS3 protocol message.
void UvAlloc(uv_handle_t *, size_t suggested_size, uv_buf_t *buf)
The function used to allocate and initialise buffers for client reading.
Response Load(const std::string &tag, const std::string &path)
Loads a file.
void UvSigintCallback(uv_signal_t *handle, int signum)
The callback fired when SIGINT occurs.
void UpdatePlayer()
Performs a player update cycle.
Constant human-readable messages used within playd.
std::vector< size_t > free_list
A list of free 1-indexed slots inside pool.
void UvUpdateTimerCallback(uv_timer_t *handle)
The callback fired when the update timer fires.
Declaration of the I/O classes used in playd.
IoCore & parent
The pool on which this connection is running.
void Broadcast(const Response &response) const
Sends the given response to all connections.
void InitSignals()
Initialises playd's signal handling.
~Connection()
Destructs a Connection.
static const std::string NOREQUEST
The tag for unsolicited messages (not from responses).
static Response Success(const std::string &tag)
Shortcut for constructing a final response to a successful request.
uv_signal_t sigint
The libuv handle for the Ctrl-C signal.
Player & player
The player.
Response Eject(const std::string &tag)
Ejects the current loaded song, if any.
std::string Name()
Retrieves a name for this connection.
void Unicast(size_t id, const Response &response) const
Sends the given response to the identified connection.
void Respond(size_t id, const Response &response) const override
Outputs a response.
void Depool()
Removes this connection from its connection pool.
void Accept(uv_stream_t *server)
Accepts a new connection.
void UvReadCallback(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
The callback fired when some bytes are read from a client connection.
void InitAcceptor(const std::string &address, const std::string &port)
Initialises a TCP acceptor on the given address and port.
void Run(const std::string &host, const std::string &port)
Runs the reactor.
void UvListenCallback(uv_stream_t *server, int status)
The callback fired when a new client connection is acquired by the listener.
uv_tcp_t server
The libuv handle for the TCP server.
const std::string MSG_CMD_INVALID
Message shown when the CommandHandler receives an invalid command.
Response Pos(const std::string &tag, const std::string &pos_str)
Seeks to a given position in the current file.
std::vector< std::vector< std::string > > Feed(const std::string &raw)
Feeds a string into a Tokeniser.
void InitUpdateTimer()
Sets up a periodic timer to run the playd update loop.
void UvCloseCallback(uv_handle_t *handle)
The callback fired when a client connection closes.
const std::string MSG_TOO_MANY_CONNS
Message shown when too many simultaneous connections are launched.
Declaration of the Player class, and associated types.
Response Dump(size_t id, const std::string &tag) const
Dumps the current player state to the given ID.
std::vector< std::shared_ptr< Connection > > pool
The set of connections inside this IoCore.
Response RunCommand(const std::vector< std::string > &msg)
Handles a tokenised command line.
void ExpandPool()
Adds a new connection slot to the connection pool.
const std::string MSG_OHAI_BIFROST
The protocol name and version.
Declaration of classes pertaining to responses to the client.
Response SetPlaying(const std::string &tag, bool playing)
Tells the audio file to start or stop playing.
Class for telling the human what playd is doing.
An Error signifying that playd has hit an internal snag.
Connection(IoCore &parent, uv_tcp_t *tcp, Player &player, size_t id)
Constructs a Connection.
Response End(const std::string &tag)
Ends a file, stopping and rewinding.
uv_tcp_t * tcp
The libuv handle for the TCP connection.
void Shutdown()
Shuts down the IoCore by terminating all IO loop tasks.
IoCore(Player &player)
Constructs an IoCore.
void Shutdown()
Gracefully shuts this connection down.
Tokeniser tokeniser
The Tokeniser to which data read on this connection should be sent.
A TCP connection from a client.
uv_loop_t * loop
The loop this IoCore is using.
A Player contains a loaded audio file and a command API for manipulating it.
void Respond(const Response &response)
Emits a Response via this Connection.
void Remove(size_t id)
Removes a connection.
const std::string MSG_OHAI_PLAYD
The playd name and version.
The IO core, which services input, routes responses, and executes the Player update routine periodica...
static const uint16_t PLAYER_UPDATE_PERIOD
The period between player updates.
static Response Invalid(const std::string &tag, const std::string &msg)
Shortcut for constructing a final response to a invalid request.
uv_timer_t updater
The libuv handle for the update timer.