1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4 #include <string.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include "../gskstreamlistenersocket.h"
8 #include "../http/gskhttpserver.h"
9 #include "../gskdebug.h"
10 #include "../gskinit.h"
11 #include "../gskmain.h"
12 #include "../gskmemory.h"
13 #include "../gskstreamfd.h"
14
15 typedef struct _PathInfo PathInfo;
16 struct _PathInfo
17 {
18 char *url_prefix;
19 char *local_path_prefix;
20 };
21
22 static GArray *path_info = NULL;
23
24 static gboolean
handle_request(GskHttpServer * server)25 handle_request (GskHttpServer *server)
26 {
27 GskHttpRequest *request;
28 GskStream *posted_data;
29 GError *error = NULL;
30 GskStream *stream = NULL;
31 GskHttpResponse *response = NULL;
32 guint i;
33 if (!gsk_http_server_get_request (server, &request, &posted_data))
34 return TRUE;
35 if (request->verb != GSK_HTTP_VERB_GET && request->verb != GSK_HTTP_VERB_HEAD)
36 {
37 stream = gsk_memory_source_static_string (
38 "<html><head><title>Server Error</title></head>\n"
39 "<body>\n"
40 " No handling for the given " "request type could be found...\n"
41 "</body></html>\n");
42 response = gsk_http_response_from_request (request, GSK_HTTP_STATUS_NOT_IMPLEMENTED, -1);
43 goto respond;
44 }
45
46 /* this is pretty slow for a real server.
47 * but this is test program, not a real server.
48 */
49 for (i = 0; i < path_info->len; i++)
50 {
51 PathInfo *pinfo = &g_array_index (path_info, PathInfo, i);
52 guint url_prefix_len = strlen (pinfo->url_prefix);
53 if (strncmp (pinfo->url_prefix, request->path, url_prefix_len) == 0)
54 break;
55 }
56
57 if (i < path_info->len)
58 {
59 PathInfo *pinfo = &g_array_index (path_info, PathInfo, i);
60 guint url_prefix_len = strlen (pinfo->url_prefix);
61 char *rel_path = request->path + url_prefix_len;
62 gboolean slash_start = (*rel_path == G_DIR_SEPARATOR);
63 char *filename = g_strdup_printf ("%s%c%s", pinfo->local_path_prefix,
64 G_DIR_SEPARATOR,
65 rel_path + (slash_start ? 1 : 0));
66 stream = gsk_stream_fd_new_read_file (filename, &error);
67 g_free (filename);
68 }
69 if (stream == NULL)
70 {
71 if (request->verb == GSK_HTTP_VERB_GET)
72 stream = gsk_memory_source_static_string (
73 "<html><head><title>File not found</title></head>\n"
74 "<body>\n"
75 " File could not be located. Please check url and retry.\n"
76 "</body></html>\n");
77 response = gsk_http_response_from_request (request, GSK_HTTP_STATUS_NOT_FOUND, -1);
78 goto respond;
79 }
80 {
81 int fd = GSK_STREAM_FD_GET_FD (stream);
82 struct stat sbuf;
83 if (fstat (fd, &sbuf) < 0)
84 {
85 g_object_unref (stream);
86 if (request->verb == GSK_HTTP_VERB_GET)
87 stream = gsk_memory_source_static_string (
88 "<html><head><title>Error accessing file</title></head>\n"
89 "<body>\n"
90 " File could not be stat'd. Please check url and retry.\n"
91 "</body></html>\n");
92 else
93 stream = NULL;
94 response = gsk_http_response_from_request (request, GSK_HTTP_STATUS_NOT_FOUND, -1);
95 goto respond;
96 }
97 response = gsk_http_response_from_request (request, GSK_HTTP_STATUS_OK, sbuf.st_size);
98 gsk_http_response_set_content_type (response, "text");
99 gsk_http_response_set_content_subtype (response, "plain");
100 }
101 if (request->verb == GSK_HTTP_VERB_HEAD)
102 {
103 g_object_unref (stream);
104 stream = NULL;
105 }
106
107 respond:
108 gsk_http_server_respond (server, request, response, stream);
109 g_object_unref (response);
110 if (stream)
111 g_object_unref (stream);
112 g_object_unref (request);
113 if (posted_data)
114 g_object_unref (posted_data);
115 return TRUE;
116 }
117
118 static gboolean
handle_on_accept(GskStream * stream,gpointer data,GError ** error)119 handle_on_accept (GskStream *stream,
120 gpointer data,
121 GError **error)
122 {
123 GError *e = NULL;
124 GskHttpServer *http_server;
125 g_assert (data == NULL);
126 http_server = gsk_http_server_new ();
127 gsk_http_server_trap (http_server,
128 handle_request,
129 NULL, /* default shutdown handling */
130 NULL, /* no user-data or destroy function */
131 NULL);
132 gsk_stream_attach (stream, GSK_STREAM (http_server), &e);
133 gsk_stream_attach (GSK_STREAM (http_server), stream, &e);
134 if (e != NULL)
135 g_error ("gsk_stream_attach: %s", e->message);
136 g_object_unref (stream);
137 g_object_unref (http_server);
138 return TRUE;
139 }
140
141
142 static void
handle_errors(GError * error,gpointer data)143 handle_errors (GError *error,
144 gpointer data)
145 {
146 g_error ("error accepting new socket: %s", error->message);
147 }
148
149 static void
usage(const char * err)150 usage (const char *err)
151 {
152 if (err)
153 g_warning ("usage error: %s", err);
154 g_print ("usage: %s [--location URL PATH]... PORT\n"
155 "\n"
156 "Run a static content http server on PORT with the urls under URL\n"
157 "mapped to directories under PATH.\n", g_get_prgname ());
158 exit (1);
159 }
160
main(int argc,char ** argv)161 int main (int argc, char **argv)
162 {
163 GskStreamListener *listener;
164 GskSocketAddress *bind_addr;
165 GError *error = NULL;
166 int i;
167 int port = 0;
168
169 gsk_init_without_threads (&argc, &argv);
170 path_info = g_array_new (FALSE, FALSE, sizeof (PathInfo));
171
172 for (i = 1; i < argc; i++)
173 {
174 if (strcmp (argv[i], "--location") == 0)
175 {
176 PathInfo info;
177 if (i + 2 >= argc)
178 usage ("--location requires two arguments url-prefix and local-location");
179 info.url_prefix = argv[++i];
180 info.local_path_prefix = argv[++i];
181 g_array_append_val (path_info, info);
182 }
183 else
184 {
185 if (port != 0)
186 usage ("only one port number is allowed");
187 if (!isdigit (argv[i][0]))
188 usage ("expected port number");
189 port = atoi (argv[i]);
190 if (port == 0)
191 usage ("port must be nonzero");
192 }
193 }
194
195 if (port == 0)
196 usage ("must specify PORT");
197
198 bind_addr = gsk_socket_address_ipv4_localhost (port);
199 listener = gsk_stream_listener_socket_new_bind (bind_addr, &error);
200 if (error != NULL)
201 g_error ("gsk_stream_listener_tcp_unix failed: %s", error->message);
202 g_assert (listener != NULL);
203
204 gsk_stream_listener_handle_accept (listener,
205 handle_on_accept,
206 handle_errors,
207 NULL, /* data */
208 NULL); /* destroy */
209
210 return gsk_main_run ();
211 }
212