| 
									
										
										
										
											2009-05-15 21:34:14 +02:00
										 |  |  | #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, | 
					
						
							| 
									
										
										
										
											2009-06-29 18:24:08 +02:00
										 |  |  | 	    const char *reason) | 
					
						
							| 
									
										
										
										
											2009-05-15 21:34:14 +02:00
										 |  |  | { | 
					
						
							|  |  |  |   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 | 
					
						
							|  |  |  | 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) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       *tmp = 0; | 
					
						
							|  |  |  |       version = tmp + 1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2011-04-08 15:44:25 -04:00
										 |  |  |   version = version; /* To avoid -Wunused-but-set-variable */ | 
					
						
							| 
									
										
										
										
											2009-05-15 21:34:14 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   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 ("<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]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-18 08:47:10 +02:00
										 |  |  |   service = g_threaded_socket_service_new (10); | 
					
						
							| 
									
										
										
										
											2009-05-15 21:34:14 +02:00
										 |  |  |   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 (); | 
					
						
							|  |  |  | } |