Update to version 2.39.3
[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 *redir_uri;
116
117                         redir_uri = g_strdup_printf ("%s/", soup_message_get_uri (msg)->path);
118                         soup_message_set_redirect (msg, SOUP_STATUS_MOVED_PERMANENTLY,
119                                                    redir_uri);
120                         g_free (redir_uri);
121                         return;
122                 }
123
124                 index_path = g_strdup_printf ("%s/index.html", path);
125                 if (stat (index_path, &st) != -1) {
126                         do_get (server, msg, index_path);
127                         g_free (index_path);
128                         return;
129                 }
130                 g_free (index_path);
131
132                 listing = get_directory_listing (path);
133                 soup_message_set_response (msg, "text/html",
134                                            SOUP_MEMORY_TAKE,
135                                            listing->str, listing->len);
136                 g_string_free (listing, FALSE);
137                 return;
138         }
139
140         fd = open (path, O_RDONLY);
141         if (fd == -1) {
142                 soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
143                 return;
144         }
145
146         if (msg->method == SOUP_METHOD_GET) {
147 #ifdef HAVE_MMAP
148                 struct mapping *mapping = g_slice_new (struct mapping);
149                 SoupBuffer *buffer;
150
151                 mapping->start = mmap (NULL, st.st_size, PROT_READ,
152                                        MAP_PRIVATE, fd, 0);
153                 mapping->length = st.st_size;
154                 buffer = soup_buffer_new_with_owner (mapping->start,
155                                                      mapping->length,
156                                                      mapping, free_mapping);
157                 soup_message_body_append_buffer (msg->response_body, buffer);
158                 soup_buffer_free (buffer);
159 #else
160                 char *buf;
161
162                 buf = g_malloc (st.st_size);
163                 read (fd, buf, st.st_size);
164                 close (fd);
165                 soup_message_body_append (msg->response_body, SOUP_MEMORY_TAKE,
166                                           buf, st.st_size);
167 #endif
168         } else /* msg->method == SOUP_METHOD_HEAD */ {
169                 char *length;
170
171                 /* We could just use the same code for both GET and
172                  * HEAD. But we'll optimize and avoid the extra
173                  * malloc.
174                  */
175                 length = g_strdup_printf ("%lu", (gulong)st.st_size);
176                 soup_message_headers_append (msg->response_headers,
177                                              "Content-Length", length);
178                 g_free (length);
179         }
180
181         soup_message_set_status (msg, SOUP_STATUS_OK);
182 }
183
184 static void
185 do_put (SoupServer *server, SoupMessage *msg, const char *path)
186 {
187         struct stat st;
188         FILE *f;
189         gboolean created = TRUE;
190
191         if (stat (path, &st) != -1) {
192                 const char *match = soup_message_headers_get_one (msg->request_headers, "If-None-Match");
193                 if (match && !strcmp (match, "*")) {
194                         soup_message_set_status (msg, SOUP_STATUS_CONFLICT);
195                         return;
196                 }
197
198                 if (!S_ISREG (st.st_mode)) {
199                         soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
200                         return;
201                 }
202
203                 created = FALSE;
204         }
205
206         f = fopen (path, "w");
207         if (!f) {
208                 soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
209                 return;
210         }
211
212         fwrite (msg->request_body->data, 1, msg->request_body->length, f);
213         fclose (f);
214
215         soup_message_set_status (msg, created ? SOUP_STATUS_CREATED : SOUP_STATUS_OK);
216 }
217
218 static void
219 server_callback (SoupServer *server, SoupMessage *msg,
220                  const char *path, GHashTable *query,
221                  SoupClientContext *context, gpointer data)
222 {
223         char *file_path;
224         SoupMessageHeadersIter iter;
225         const char *name, *value;
226
227         printf ("%s %s HTTP/1.%d\n", msg->method, path,
228                 soup_message_get_http_version (msg));
229         soup_message_headers_iter_init (&iter, msg->request_headers);
230         while (soup_message_headers_iter_next (&iter, &name, &value))
231                 printf ("%s: %s\n", name, value);
232         if (msg->request_body->length)
233                 printf ("%s\n", msg->request_body->data);
234
235         file_path = g_strdup_printf (".%s", path);
236
237         if (msg->method == SOUP_METHOD_GET || msg->method == SOUP_METHOD_HEAD)
238                 do_get (server, msg, file_path);
239         else if (msg->method == SOUP_METHOD_PUT)
240                 do_put (server, msg, file_path);
241         else
242                 soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
243
244         g_free (file_path);
245         printf ("  -> %d %s\n\n", msg->status_code, msg->reason_phrase);
246 }
247
248 static void
249 quit (int sig)
250 {
251         /* Exit cleanly on ^C in case we're valgrinding. */
252         exit (0);
253 }
254
255 int
256 main (int argc, char **argv)
257 {
258         GMainLoop *loop;
259         SoupServer *server, *ssl_server;
260         int opt;
261         int port = SOUP_ADDRESS_ANY_PORT;
262         int ssl_port = SOUP_ADDRESS_ANY_PORT;
263         const char *ssl_cert_file = NULL, *ssl_key_file = NULL;
264
265         g_type_init ();
266         signal (SIGINT, quit);
267
268         while ((opt = getopt (argc, argv, "p:k:c:s:")) != -1) {
269                 switch (opt) {
270                 case 'p':
271                         port = atoi (optarg);
272                         break;
273                 case 'k':
274                         ssl_key_file = optarg;
275                         break;
276                 case 'c':
277                         ssl_cert_file = optarg;
278                         break;
279                 case 's':
280                         ssl_port = atoi (optarg);
281                         break;
282                 default:
283                         fprintf (stderr, "Usage: %s [-p port] [-c ssl-cert-file -k ssl-key-file [-s ssl-port]]\n",
284                                  argv[0]);
285                         exit (1);
286                 }
287         }
288
289         server = soup_server_new (SOUP_SERVER_PORT, port,
290                                   SOUP_SERVER_SERVER_HEADER, "simple-httpd ",
291                                   NULL);
292         if (!server) {
293                 fprintf (stderr, "Unable to bind to server port %d\n", port);
294                 exit (1);
295         }
296         soup_server_add_handler (server, NULL,
297                                  server_callback, NULL, NULL);
298         printf ("\nStarting Server on port %d\n",
299                 soup_server_get_port (server));
300         soup_server_run_async (server);
301
302         if (ssl_cert_file && ssl_key_file) {
303                 ssl_server = soup_server_new (
304                         SOUP_SERVER_PORT, ssl_port,
305                         SOUP_SERVER_SSL_CERT_FILE, ssl_cert_file,
306                         SOUP_SERVER_SSL_KEY_FILE, ssl_key_file,
307                         NULL);
308
309                 if (!ssl_server) {
310                         fprintf (stderr, "Unable to bind to SSL server port %d\n", ssl_port);
311                         exit (1);
312                 }
313                 soup_server_add_handler (ssl_server, NULL,
314                                          server_callback, NULL, NULL);
315                 printf ("Starting SSL Server on port %d\n", 
316                         soup_server_get_port (ssl_server));
317                 soup_server_run_async (ssl_server);
318         }
319
320         printf ("\nWaiting for requests...\n");
321
322         loop = g_main_loop_new (NULL, TRUE);
323         g_main_loop_run (loop);
324
325         return 0;
326 }