1#include <gio/gio.h>
2#include <string.h>
3
4static int port = 8080;
5static char *root = NULL;
6static GOptionEntry cmd_entries[] = {
7 {"port", 'p', 0, G_OPTION_ARG_INT, &port,
8 "Local port to bind to", NULL},
9 {NULL}
10};
11
12static void
13send_error (GOutputStream *out,
14 int error_code,
15 const char *reason)
16{
17 char *res;
18
19 res = g_strdup_printf (format: "HTTP/1.0 %d %s\r\n\r\n"
20 "<html><head><title>%d %s</title></head>"
21 "<body>%s</body></html>",
22 error_code, reason,
23 error_code, reason,
24 reason);
25 g_output_stream_write_all (stream: out, buffer: res, count: strlen (s: res), NULL, NULL, NULL);
26 g_free (mem: res);
27}
28
29static gboolean
30handler (GThreadedSocketService *service,
31 GSocketConnection *connection,
32 GSocketListener *listener,
33 gpointer user_data)
34{
35 GOutputStream *out;
36 GInputStream *in;
37 GFileInputStream *file_in;
38 GDataInputStream *data;
39 char *line, *escaped, *tmp, *query, *unescaped, *path, *version;
40 GFile *f;
41 GError *error;
42 GFileInfo *info;
43 GString *s;
44
45 in = g_io_stream_get_input_stream (G_IO_STREAM (connection));
46 out = g_io_stream_get_output_stream (G_IO_STREAM (connection));
47
48 data = g_data_input_stream_new (base_stream: in);
49 /* Be tolerant of input */
50 g_data_input_stream_set_newline_type (stream: data, type: G_DATA_STREAM_NEWLINE_TYPE_ANY);
51
52 line = g_data_input_stream_read_line (stream: data, NULL, NULL, NULL);
53
54 if (line == NULL)
55 {
56 send_error (out, error_code: 400, reason: "Invalid request");
57 goto out;
58 }
59
60 if (!g_str_has_prefix (str: line, prefix: "GET "))
61 {
62 send_error (out, error_code: 501, reason: "Only GET implemented");
63 goto out;
64 }
65
66 escaped = line + 4; /* Skip "GET " */
67
68 version = NULL;
69 tmp = strchr (s: escaped, c: ' ');
70 if (tmp == NULL)
71 {
72 send_error (out, error_code: 400, reason: "Bad Request");
73 goto out;
74 }
75 *tmp = 0;
76
77 version = tmp + 1;
78 if (!g_str_has_prefix (str: version, prefix: "HTTP/1."))
79 {
80 send_error(out, error_code: 505, reason: "HTTP Version Not Supported");
81 goto out;
82 }
83
84 query = strchr (s: escaped, c: '?');
85 if (query != NULL)
86 *query++ = 0;
87
88 unescaped = g_uri_unescape_string (escaped_string: escaped, NULL);
89 path = g_build_filename (first_element: root, unescaped, NULL);
90 g_free (mem: unescaped);
91 f = g_file_new_for_path (path);
92 g_free (mem: path);
93
94 error = NULL;
95 file_in = g_file_read (file: f, NULL, error: &error);
96 if (file_in == NULL)
97 {
98 send_error (out, error_code: 404, reason: error->message);
99 g_error_free (error);
100 goto out;
101 }
102
103 s = g_string_new (init: "HTTP/1.0 200 OK\r\n");
104 info = g_file_input_stream_query_info (stream: file_in,
105 G_FILE_ATTRIBUTE_STANDARD_SIZE ","
106 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
107 NULL, NULL);
108 if (info)
109 {
110 const char *content_type;
111 char *mime_type;
112
113 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE))
114 g_string_append_printf (string: s, format: "Content-Length: %"G_GINT64_FORMAT"\r\n",
115 g_file_info_get_size (info));
116 content_type = g_file_info_get_content_type (info);
117 if (content_type)
118 {
119 mime_type = g_content_type_get_mime_type (type: content_type);
120 if (mime_type)
121 {
122 g_string_append_printf (string: s, format: "Content-Type: %s\r\n",
123 mime_type);
124 g_free (mem: mime_type);
125 }
126 }
127 }
128 g_string_append (string: s, val: "\r\n");
129
130 if (g_output_stream_write_all (stream: out,
131 buffer: s->str, count: s->len,
132 NULL, NULL, NULL))
133 {
134 g_output_stream_splice (stream: out,
135 G_INPUT_STREAM (file_in),
136 flags: 0, NULL, NULL);
137 }
138 g_string_free (string: s, TRUE);
139
140 g_input_stream_close (G_INPUT_STREAM (file_in), NULL, NULL);
141 g_object_unref (object: file_in);
142
143 out:
144 g_object_unref (object: data);
145
146 return TRUE;
147}
148
149int
150main (int argc, char *argv[])
151{
152 GSocketService *service;
153 GOptionContext *context;
154 GError *error = NULL;
155
156 context = g_option_context_new (parameter_string: "<http root dir> - Simple HTTP server");
157 g_option_context_add_main_entries (context, entries: cmd_entries, NULL);
158 if (!g_option_context_parse (context, argc: &argc, argv: &argv, error: &error))
159 {
160 g_printerr (format: "%s: %s\n", argv[0], error->message);
161 return 1;
162 }
163
164 if (argc != 2)
165 {
166 g_printerr (format: "Root directory not specified\n");
167 return 1;
168 }
169
170 root = g_strdup (str: argv[1]);
171
172 service = g_threaded_socket_service_new (max_threads: 10);
173 if (!g_socket_listener_add_inet_port (G_SOCKET_LISTENER (service),
174 port,
175 NULL,
176 error: &error))
177 {
178 g_printerr (format: "%s: %s\n", argv[0], error->message);
179 return 1;
180 }
181
182 g_print (format: "Http server listening on port %d\n", port);
183
184 g_signal_connect (service, "run", G_CALLBACK (handler), NULL);
185
186 g_main_loop_run (loop: g_main_loop_new (NULL, FALSE));
187 g_assert_not_reached ();
188}
189

source code of gtk/subprojects/glib/gio/tests/httpd.c