diff options
Diffstat (limited to 'daemon.c')
-rw-r--r-- | daemon.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/daemon.c b/daemon.c new file mode 100644 index 0000000..a2c5b96 --- /dev/null +++ b/daemon.c @@ -0,0 +1,236 @@ +/* Build with + gcc `pkg-config --cflags --libs gio-2.0` daemon.c -o daemon +*/ + +#include <gio/gio.h> +#include <string.h> + +static int port = 8080; +static char *root = NULL; +static GOptionEntry cmd_entries[] = { + {"port", 'p', 0, G_OPTION_ARG_INT, &port, + "Local port to bind to", NULL}, + {NULL} +}; + +static void +send_error (GOutputStream *out, + int error_code, + const char *reason) +{ + char *res; + + res = g_strdup_printf ("HTTP/1.0 %d %s\r\n\r\n" + "<html><head><title>%d %s</title></head>" + "<body>%s</body></html>", + error_code, reason, + error_code, reason, + reason); + g_output_stream_write_all (out, res, strlen (res), NULL, NULL, NULL); + g_free (res); +} + +static gboolean +handle_read_file(const char *filename, GOutputStream *out) +{ + GFileInputStream *file_in; + char *path; + GFile *f; + GError *error; + GString *s; + + path = g_build_filename (root, filename, NULL); + f = g_file_new_for_path (path); + g_free (path); + + error = NULL; + file_in = g_file_read (f, NULL, &error); + if (file_in == NULL) + { + send_error (out, 404, error->message); + g_error_free (error); + g_object_unref (f); + return FALSE; + } + + s = g_string_new ("HTTP/1.0 200 OK\r\n"); + g_string_append_printf (s, "Content-Type: %s\r\n", + "text/html"); + g_string_append (s, "\r\n"); + + if (g_output_stream_write_all (out, + s->str, s->len, + NULL, NULL, NULL)) + { + g_output_stream_splice (out, + G_INPUT_STREAM (file_in), + 0, NULL, NULL); + } + g_string_free (s, TRUE); + g_object_unref (file_in); + + return TRUE; +} + +static gboolean +send_draw_ops(GOutputStream *out) +{ + GZlibCompressor *compressor; + GOutputStream *cout; + char *boundary = + "--x\r\n" + "\r\n"; + char *header; + char *data; + int i; + + header = + "HTTP/1.1 200 OK\r\n" + "Content-type: multipart/x-mixed-replace;boundary=x\r\n" + "Content-Encoding: gzip\r\n" + "\r\n"; + g_output_stream_write_all (out, + header, strlen(header), + NULL, NULL, NULL); + + compressor = g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP, -1); + cout = g_converter_output_stream_new (out, G_CONVERTER (compressor)); + + g_output_stream_write_all (cout, + boundary, strlen (boundary), + NULL, NULL, NULL); + + i = 0; + + while (1) + { + data = g_strdup_printf ("test %d", i++); + + g_output_stream_write_all (cout, + data, strlen(data), + NULL, NULL, NULL); + g_free (data); + g_output_stream_write_all (cout, + boundary, strlen (boundary), + NULL, NULL, NULL); + g_output_stream_flush (cout, NULL, NULL); + + g_usleep(1 * 1000 * 1000); + } + + return TRUE; +} + +static gboolean +handler (GThreadedSocketService *service, + GSocketConnection *connection, + GSocketListener *listener, + gpointer user_data) +{ + GOutputStream *out; + GInputStream *in; + GDataInputStream *data; + char *line, *escaped, *tmp, *query, *unescaped, *version; + gboolean res; + + in = g_io_stream_get_input_stream (G_IO_STREAM (connection)); + out = g_io_stream_get_output_stream (G_IO_STREAM (connection)); + + res = FALSE; + data = g_data_input_stream_new (in); + + /* Be tolerant of input */ + g_data_input_stream_set_newline_type (data, G_DATA_STREAM_NEWLINE_TYPE_ANY); + + line = g_data_input_stream_read_line (data, NULL, NULL, NULL); + + if (line == NULL) + { + send_error (out, 400, "Invalid request"); + goto out; + } + + if (!g_str_has_prefix (line, "GET ")) + { + send_error (out, 501, "Only GET implemented"); + goto out; + } + + escaped = line + 4; /* Skip "GET " */ + + version = NULL; + tmp = strchr (escaped, ' '); + if (tmp != NULL) + { + *tmp = 0; + version = tmp + 1; + } + + query = strchr (escaped, '?'); + if (query != NULL) + *query++ = 0; + + unescaped = g_uri_unescape_string (escaped, NULL); + + if (strcmp(unescaped, "/client.html") == 0) + res = handle_read_file("client.html", out); + else if (strcmp(unescaped, "/ops") == 0) + { + res = send_draw_ops(out); + } + else + { + send_error (out, 404, "No such file"); + } + g_free (unescaped); + g_free (line); + + out: + g_object_unref (data); + + return res; +} + +int +main (int argc, char *argv[]) +{ + GSocketService *service; + GOptionContext *context; + GError *error = NULL; + + g_type_init (); + g_thread_init (NULL); + + context = g_option_context_new ("<http root dir> - Simple HTTP server"); + g_option_context_add_main_entries (context, cmd_entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s: %s\n", argv[0], error->message); + return 1; + } + + if (argc != 2) + { + g_printerr ("Root directory not specified\n"); + return 1; + } + + root = g_strdup (argv[1]); + + service = g_threaded_socket_service_new (10); + if (!g_socket_listener_add_inet_port (G_SOCKET_LISTENER (service), + port, + NULL, + &error)) + { + g_printerr ("%s: %s\n", argv[0], error->message); + return 1; + } + + g_print ("Http server listening on port %d\n", port); + + g_signal_connect (service, "run", G_CALLBACK (handler), NULL); + + g_main_loop_run (g_main_loop_new (NULL, FALSE)); + g_assert_not_reached (); +} |