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