#include #include 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" "%d %s" "%s", error_code, reason, error_code, reason, reason); g_output_stream_write_all (out, res, strlen (res), NULL, NULL, NULL); g_free (res); } static gboolean handler (GThreadedSocketService *service, GSocketConnection *connection, GSocketListener *listener, gpointer user_data) { GOutputStream *out; GInputStream *in; GFileInputStream *file_in; GDataInputStream *data; char *line, *escaped, *tmp, *query, *unescaped, *path, *version; GFile *f; GError *error; GFileInfo *info; GString *s; in = g_io_stream_get_input_stream (G_IO_STREAM (connection)); out = g_io_stream_get_output_stream (G_IO_STREAM (connection)); 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) { send_error (out, 400, "Bad Request"); goto out; } *tmp = 0; version = tmp + 1; if (!g_str_has_prefix (version, "HTTP/1.")) { send_error(out, 505, "HTTP Version Not Supported"); goto out; } query = strchr (escaped, '?'); if (query != NULL) *query++ = 0; unescaped = g_uri_unescape_string (escaped, NULL); path = g_build_filename (root, unescaped, NULL); g_free (unescaped); 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); goto out; } s = g_string_new ("HTTP/1.0 200 OK\r\n"); info = g_file_input_stream_query_info (file_in, G_FILE_ATTRIBUTE_STANDARD_SIZE "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, NULL, NULL); if (info) { const char *content_type; char *mime_type; if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE)) g_string_append_printf (s, "Content-Length: %"G_GINT64_FORMAT"\r\n", g_file_info_get_size (info)); content_type = g_file_info_get_content_type (info); if (content_type) { mime_type = g_content_type_get_mime_type (content_type); if (mime_type) { g_string_append_printf (s, "Content-Type: %s\r\n", mime_type); g_free (mime_type); } } } 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_input_stream_close (G_INPUT_STREAM (file_in), NULL, NULL); g_object_unref (file_in); out: g_object_unref (data); return TRUE; } int main (int argc, char *argv[]) { GSocketService *service; GOptionContext *context; GError *error = NULL; context = g_option_context_new (" - 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 (); }