Git init
[profile/ivi/libsoup2.4.git] / tests / simple-httpd.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2001-2003, Ximian, Inc.
4  */
5
6 #include "config.h"
7
8 #include <ctype.h>
9 #include <dirent.h>
10 #include <fcntl.h>
11 #include <errno.h>
12 #include <signal.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18
19 #ifdef HAVE_MMAP
20 #include <sys/mman.h>
21 #endif
22
23 #include <libsoup/soup.h>
24
25 #ifdef HAVE_MMAP
26 struct mapping {
27         void   *start;
28         size_t  length;
29 };
30
31 static void
32 free_mapping (gpointer data)
33 {
34         struct mapping *mapping = data;
35         munmap (mapping->start, mapping->length);
36         g_slice_free (struct mapping, mapping);
37 }
38 #endif
39
40 static int
41 compare_strings (gconstpointer a, gconstpointer b)
42 {
43         const char **sa = (const char **)a;
44         const char **sb = (const char **)b;
45
46         return strcmp (*sa, *sb);
47 }
48
49 static GString *
50 get_directory_listing (const char *path)
51 {
52         GPtrArray *entries;
53         GString *listing;
54         char *escaped;
55         DIR *dir;
56         struct dirent *dent;
57         int i;
58
59         entries = g_ptr_array_new ();
60         dir = opendir (path);
61         if (dir) {
62                 while ((dent = readdir (dir))) {
63                         if (!strcmp (dent->d_name, ".") ||
64                             (!strcmp (dent->d_name, "..") &&
65                              !strcmp (path, "./")))
66                                 continue;
67                         escaped = g_markup_escape_text (dent->d_name, -1);
68                         g_ptr_array_add (entries, escaped);
69                 }
70                 closedir (dir);
71         }
72
73         g_ptr_array_sort (entries, (GCompareFunc)compare_strings);
74
75         listing = g_string_new ("<html>\r\n");
76         escaped = g_markup_escape_text (strchr (path, '/'), -1);
77         g_string_append_printf (listing, "<head><title>Index of %s</title></head>\r\n", escaped);
78         g_string_append_printf (listing, "<body><h1>Index of %s</h1>\r\n<p>\r\n", escaped);
79         g_free (escaped);
80         for (i = 0; i < entries->len; i++) {
81                 g_string_append_printf (listing, "<a href=\"%s\">%s</a><br>\r\n",
82                                         (char *)entries->pdata[i], 
83                                         (char *)entries->pdata[i]);
84                 g_free (entries->pdata[i]);
85         }
86         g_string_append (listing, "</body>\r\n</html>\r\n");
87
88         g_ptr_array_free (entries, TRUE);
89         return listing;
90 }
91
92 static void
93 do_get (SoupServer *server, SoupMessage *msg, const char *path)
94 {
95         char *slash;
96         struct stat st;
97         int fd;
98
99         if (stat (path, &st) == -1) {
100                 if (errno == EPERM)
101                         soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
102                 else if (errno == ENOENT)
103                         soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
104                 else
105                         soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
106                 return;
107         }
108
109         if (S_ISDIR (st.st_mode)) {
110                 GString *listing;
111                 char *index_path;
112
113                 slash = strrchr (path, '/');
114                 if (!slash || slash[1]) {
115                         char *uri, *redir_uri;
116
117                         uri = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
118                         redir_uri = g_strdup_printf ("%s/", uri);
119                         soup_message_headers_append (msg->response_headers,
120                                                      "Location", redir_uri);
121                         soup_message_set_status (msg, SOUP_STATUS_MOVED_PERMANENTLY);
122                         g_free (redir_uri);
123                         g_free (uri);
124                         return;
125                 }
126
127                 index_path = g_strdup_printf ("%s/index.html", path);
128                 if (stat (index_path, &st) != -1) {
129                         do_get (server, msg, index_path);
130                         g_free (index_path);
131                         return;
132                 }
133
134                 listing = get_directory_listing (path);
135                 soup_message_set_response (msg, "text/html",
136                                            SOUP_MEMORY_TAKE,
137                                            listing->str, listing->len);
138                 g_string_free (listing, FALSE);
139                 return;
140         }
141
142         fd = open (path, O_RDONLY);
143         if (fd == -1) {
144                 soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
145                 return;
146         }
147
148         if (msg->method == SOUP_METHOD_GET) {
149 #ifdef HAVE_MMAP
150                 struct mapping *mapping = g_slice_new (struct mapping);
151                 SoupBuffer *buffer;
152
153                 mapping->start = mmap (NULL, st.st_size, PROT_READ,
154                                        MAP_PRIVATE, fd, 0);
155                 mapping->length = st.st_size;
156                 buffer = soup_buffer_new_with_owner (mapping->start,
157                                                      mapping->length,
158                                                      mapping, free_mapping);
159                 soup_message_body_append_buffer (msg->response_body, buffer);
160                 soup_buffer_free (buffer);
161 #else
162                 char *buf;
163
164                 buf = g_malloc (st.st_size);
165                 read (fd, buf, st.st_size);
166                 close (fd);
167                 soup_message_body_append (msg->response_body, SOUP_MEMORY_TAKE,
168                                           buf, st.st_size);
169 #endif
170         } else /* msg->method == SOUP_METHOD_HEAD */ {
171                 char *length;
172
173                 /* We could just use the same code for both GET and
174                  * HEAD. But we'll optimize and avoid the extra
175                  * malloc.
176                  */
177                 length = g_strdup_printf ("%lu", (gulong)st.st_size);
178                 soup_message_headers_append (msg->response_headers,
179                                              "Content-Length", length);
180                 g_free (length);
181         }
182
183         soup_message_set_status (msg, SOUP_STATUS_OK);
184 }
185
186 static void
187 do_put (SoupServer *server, SoupMessage *msg, const char *path)
188 {
189         struct stat st;
190         FILE *f;
191         gboolean created = TRUE;
192
193         if (stat (path, &st) != -1) {
194                 const char *match = soup_message_headers_get_one (msg->request_headers, "If-None-Match");
195                 if (match && !strcmp (match, "*")) {
196                         soup_message_set_status (msg, SOUP_STATUS_CONFLICT);
197                         return;
198                 }
199
200                 if (!S_ISREG (st.st_mode)) {
201                         soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
202                         return;
203                 }
204
205                 created = FALSE;
206         }
207
208         f = fopen (path, "w");
209         if (!f) {
210                 soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
211                 return;
212         }
213
214         fwrite (msg->request_body->data, 1, msg->request_body->length, f);
215         fclose (f);
216
217         soup_message_set_status (msg, created ? SOUP_STATUS_CREATED : SOUP_STATUS_OK);
218 }
219
220 static void
221 server_callback (SoupServer *server, SoupMessage *msg,
222                  const char *path, GHashTable *query,
223                  SoupClientContext *context, gpointer data)
224 {
225         char *file_path;
226         SoupMessageHeadersIter iter;
227         const char *name, *value;
228
229         printf ("%s %s HTTP/1.%d\n", msg->method, path,
230                 soup_message_get_http_version (msg));
231         soup_message_headers_iter_init (&iter, msg->request_headers);
232         while (soup_message_headers_iter_next (&iter, &name, &value))
233                 printf ("%s: %s\n", name, value);
234         if (msg->request_body->length)
235                 printf ("%s\n", msg->request_body->data);
236
237         file_path = g_strdup_printf (".%s", path);
238
239         if (msg->method == SOUP_METHOD_GET || msg->method == SOUP_METHOD_HEAD)
240                 do_get (server, msg, file_path);
241         else if (msg->method == SOUP_METHOD_PUT)
242                 do_put (server, msg, file_path);
243         else
244                 soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
245
246         g_free (file_path);
247         printf ("  -> %d %s\n\n", msg->status_code, msg->reason_phrase);
248 }
249
250 static void
251 quit (int sig)
252 {
253         /* Exit cleanly on ^C in case we're valgrinding. */
254         exit (0);
255 }
256
257 int
258 main (int argc, char **argv)
259 {
260         GMainLoop *loop;
261         SoupServer *server, *ssl_server;
262         int opt;
263         int port = SOUP_ADDRESS_ANY_PORT;
264         int ssl_port = SOUP_ADDRESS_ANY_PORT;
265         const char *ssl_cert_file = NULL, *ssl_key_file = NULL;
266
267         g_thread_init (NULL);
268         g_type_init ();
269         signal (SIGINT, quit);
270
271         while ((opt = getopt (argc, argv, "p:k:c:s:")) != -1) {
272                 switch (opt) {
273                 case 'p':
274                         port = atoi (optarg);
275                         break;
276                 case 'k':
277                         ssl_key_file = optarg;
278                         break;
279                 case 'c':
280                         ssl_cert_file = optarg;
281                         break;
282                 case 's':
283                         ssl_port = atoi (optarg);
284                         break;
285                 default:
286                         fprintf (stderr, "Usage: %s [-p port] [-c ssl-cert-file -k ssl-key-file [-s ssl-port]]\n",
287                                  argv[0]);
288                         exit (1);
289                 }
290         }
291
292         server = soup_server_new (SOUP_SERVER_PORT, port,
293                                   SOUP_SERVER_SERVER_HEADER, "simple-httpd ",
294                                   NULL);
295         if (!server) {
296                 fprintf (stderr, "Unable to bind to server port %d\n", port);
297                 exit (1);
298         }
299         soup_server_add_handler (server, NULL,
300                                  server_callback, NULL, NULL);
301         printf ("\nStarting Server on port %d\n",
302                 soup_server_get_port (server));
303         soup_server_run_async (server);
304
305         if (ssl_cert_file && ssl_key_file) {
306                 ssl_server = soup_server_new (
307                         SOUP_SERVER_PORT, ssl_port,
308                         SOUP_SERVER_SSL_CERT_FILE, ssl_cert_file,
309                         SOUP_SERVER_SSL_KEY_FILE, ssl_key_file,
310                         NULL);
311
312                 if (!ssl_server) {
313                         fprintf (stderr, "Unable to bind to SSL server port %d\n", ssl_port);
314                         exit (1);
315                 }
316                 soup_server_add_handler (ssl_server, NULL,
317                                          server_callback, NULL, NULL);
318                 printf ("Starting SSL Server on port %d\n", 
319                         soup_server_get_port (ssl_server));
320                 soup_server_run_async (ssl_server);
321         }
322
323         printf ("\nWaiting for requests...\n");
324
325         loop = g_main_loop_new (NULL, TRUE);
326         g_main_loop_run (loop);
327
328         return 0;
329 }