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