blob: d96193ab345a370cb9b40ed66d612cd2becb7cf9 [file] [log] [blame] [raw]
/*
This is an example Websocket client.
Data is read from STDIN (which is most of the code) and sent to the Websocket.
Each line of text enterd through STDIN is sent to the Websocket, excluding the
EOL (end of line) character(s).
To try it out, after compiling (assuming `tmp/demo` is the compiled file):
$ tmp/demo echo.websocket.org
*/
#include "http.h"
#include <signal.h>
/* *****************************************************************************
Globals
***************************************************************************** */
FIOBJ input_channel = FIOBJ_INVALID;
/* *****************************************************************************
REPL
***************************************************************************** */
FIOBJ repl_buffer;
/** we read data from stdin and test for EOL markers. */
void repl_on_data(intptr_t uuid, protocol_s *protocol) {
const size_t capa = fiobj_str_capa(repl_buffer);
fio_cstr_s buffer = fiobj_obj2cstr(repl_buffer);
if (buffer.len == capa) {
fprintf(stderr, "FATAL ERROR: input too long\n");
kill(0, SIGINT);
return;
}
ssize_t n = sock_read(uuid, buffer.data + buffer.len, capa - buffer.len);
if (n <= 0)
return;
buffer.len += n;
{
/* eliminate backspace? */
char *p;
while ((p = memchr(buffer.data, '\b', buffer.len))) {
if (buffer.len - (p - buffer.data)) {
memmove(p, p + 1, buffer.len - (p - buffer.data));
}
--buffer.len;
}
}
fiobj_str_resize(repl_buffer, buffer.len);
char *eol = memchr(buffer.data, '\n', buffer.len);
if (eol == NULL)
return;
if (eol == buffer.data) {
memmove(buffer.data, buffer.data + 1, buffer.len - 1);
fiobj_str_resize(repl_buffer, buffer.len - 1);
return;
} else if (eol == buffer.data + 1 && buffer.data[0] == '\r') {
memmove(buffer.data, buffer.data + 2, buffer.len - 2);
fiobj_str_resize(repl_buffer, buffer.len - 2);
return;
}
if (eol[-1] == '\r')
--eol;
size_t message_length = eol - buffer.data;
FIOBJ message = fiobj_str_new(buffer.data, message_length);
pubsub_publish(.channel = input_channel, .message = message);
fprintf(stderr, "* Sent %zu bytes\n", fiobj_obj2cstr(message).len);
fiobj_free(message);
if (eol[0] == '\r') {
++message_length;
++eol;
}
++message_length;
memmove(buffer.data, eol + 1, buffer.len - message_length);
fiobj_str_resize(repl_buffer, buffer.len - message_length);
sock_flush_all();
(void)protocol;
}
/** called when a connection's timeout was reached */
void repl_ping(intptr_t uuid, protocol_s *protocol) {
sock_touch(uuid);
(void)protocol;
}
static protocol_s REPL = {
.on_data = repl_on_data, .ping = repl_ping,
};
/* *****************************************************************************
Websocket Client
***************************************************************************** */
static void on_websocket_message(ws_s *ws, char *data, size_t size,
uint8_t is_text) {
fprintf(stderr, "%.*s\n", (int)size, data);
(void)(ws);
(void)(is_text);
}
static void on_open(ws_s *ws) {
fprintf(stderr, "Opened Websocket Connection\n");
websocket_subscribe(ws, .channel = input_channel);
}
static void on_shutdown(ws_s *ws) {
websocket_write(ws, "Going Away...", 13, 1);
fprintf(stderr, "Client app closing...\n");
}
static void on_close(intptr_t uuid, void *udata) {
if (!uuid)
fprintf(stderr, "Connection failed...\n");
else
fprintf(stderr, "We're done...\n");
kill(0, SIGINT);
(void)(uuid);
(void)(udata);
}
/* *****************************************************************************
Main
***************************************************************************** */
int main(int argc, char const *argv[]) {
repl_buffer = fiobj_str_buf(4096);
FIOBJ address = fiobj_str_new("ws://", 5);
input_channel = fiobj_str_new("input_channel", 13);
if (argc >= 2) {
fiobj_str_write(address, argv[1], strlen(argv[1]));
if (argc >= 3) {
fiobj_str_write(address, ":", 1);
fiobj_str_write(address, argv[2], strlen(argv[2]));
}
} else {
fiobj_str_write(address, "localhost:3000/", 15);
}
if (websocket_connect(fiobj_obj2cstr(address).data, .on_close = on_close,
.on_open = on_open, .on_message = on_websocket_message,
.on_shutdown = on_shutdown, ) < 0) {
fprintf(stderr, "Failed to connect to Websocket server\n");
exit(-1);
}
int stdio_fd = dup(fileno(stdin));
sock_set_non_block(stdio_fd);
facil_attach(sock_open(stdio_fd), &REPL);
facil_run(.threads = 1, .processes = 1);
fiobj_free(address);
fiobj_free(input_channel);
fiobj_free(repl_buffer);
}