fileserver_example_dirs.c (6624B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007 Christian Grothoff (and other contributing authors) 4 Copyright (C) 2016-2022 Evgeny Grin (Karlson2k) 5 6 This library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 This library is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with this library; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21 /** 22 * @file fileserver_example_dirs.c 23 * @brief example for how to use libmicrohttpd to serve files (with directory support) 24 * @author Christian Grothoff 25 * @author Karlson2k (Evgeny Grin) 26 */ 27 28 #include "platform.h" 29 #include <dirent.h> 30 #include <microhttpd.h> 31 #ifdef HAVE_UNISTD_H 32 #include <unistd.h> 33 #endif 34 #include <errno.h> 35 36 37 static ssize_t 38 file_reader (void *cls, uint64_t pos, char *buf, size_t max) 39 { 40 FILE *file = (FILE *) cls; 41 size_t bytes_read; 42 43 /* 'fseek' may not support files larger 2GiB, depending on platform. 44 * For production code, make sure that 'pos' has valid values, supported by 45 * 'fseek', or use 'fseeko' or similar function. */ 46 if (0 != fseek (file, (long) pos, SEEK_SET)) 47 return MHD_CONTENT_READER_END_WITH_ERROR; 48 bytes_read = fread (buf, 1, max, file); 49 if (0 == bytes_read) 50 return (0 != ferror (file)) ? MHD_CONTENT_READER_END_WITH_ERROR : 51 MHD_CONTENT_READER_END_OF_STREAM; 52 return (ssize_t) bytes_read; 53 } 54 55 56 static void 57 file_free_callback (void *cls) 58 { 59 FILE *file = cls; 60 fclose (file); 61 } 62 63 64 static void 65 dir_free_callback (void *cls) 66 { 67 DIR *dir = cls; 68 if (dir != NULL) 69 closedir (dir); 70 } 71 72 73 static ssize_t 74 dir_reader (void *cls, uint64_t pos, char *buf, size_t max) 75 { 76 DIR *dir = cls; 77 struct dirent *e; 78 int res; 79 80 if (max < 512) 81 return 0; 82 (void) pos; /* 'pos' is ignored as function return next one single entry per call. */ 83 do 84 { 85 e = readdir (dir); 86 if (e == NULL) 87 return MHD_CONTENT_READER_END_OF_STREAM; 88 } while (e->d_name[0] == '.'); 89 res = snprintf (buf, max, 90 "<a href=\"/%s\">%s</a><br>", 91 e->d_name, 92 e->d_name); 93 if (0 >= res) 94 return MHD_CONTENT_READER_END_WITH_ERROR; 95 if (max < (size_t) res) 96 return MHD_CONTENT_READER_END_WITH_ERROR; 97 return (ssize_t) res; 98 } 99 100 101 static enum MHD_Result 102 ahc_echo (void *cls, 103 struct MHD_Connection *connection, 104 const char *url, 105 const char *method, 106 const char *version, 107 const char *upload_data, 108 size_t *upload_data_size, void **req_cls) 109 { 110 static int aptr; 111 struct MHD_Response *response; 112 enum MHD_Result ret; 113 FILE *file; 114 int fd; 115 DIR *dir; 116 struct stat buf; 117 char emsg[1024]; 118 (void) cls; /* Unused. Silent compiler warning. */ 119 (void) version; /* Unused. Silent compiler warning. */ 120 (void) upload_data; /* Unused. Silent compiler warning. */ 121 (void) upload_data_size; /* Unused. Silent compiler warning. */ 122 123 if (0 != strcmp (method, MHD_HTTP_METHOD_GET)) 124 return MHD_NO; /* unexpected method */ 125 if (&aptr != *req_cls) 126 { 127 /* do never respond on first call */ 128 *req_cls = &aptr; 129 return MHD_YES; 130 } 131 *req_cls = NULL; /* reset when done */ 132 133 file = fopen (&url[1], "rb"); 134 if (NULL != file) 135 { 136 fd = fileno (file); 137 if (-1 == fd) 138 { 139 (void) fclose (file); 140 return MHD_NO; /* internal error */ 141 } 142 if ( (0 != fstat (fd, &buf)) || 143 (! S_ISREG (buf.st_mode)) ) 144 { 145 /* not a regular file, refuse to serve */ 146 fclose (file); 147 file = NULL; 148 } 149 } 150 151 if (NULL == file) 152 { 153 dir = opendir ("."); 154 if (NULL == dir) 155 { 156 /* most likely cause: more concurrent requests than 157 available file descriptors / 2 */ 158 snprintf (emsg, 159 sizeof (emsg), 160 "Failed to open directory `.': %s\n", 161 strerror (errno)); 162 response = MHD_create_response_from_buffer (strlen (emsg), 163 emsg, 164 MHD_RESPMEM_MUST_COPY); 165 if (NULL == response) 166 return MHD_NO; 167 ret = MHD_queue_response (connection, 168 MHD_HTTP_SERVICE_UNAVAILABLE, 169 response); 170 MHD_destroy_response (response); 171 } 172 else 173 { 174 response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 175 32 * 1024, 176 &dir_reader, 177 dir, 178 &dir_free_callback); 179 if (NULL == response) 180 { 181 closedir (dir); 182 return MHD_NO; 183 } 184 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 185 MHD_destroy_response (response); 186 } 187 } 188 else 189 { 190 response = MHD_create_response_from_callback ((size_t) buf.st_size, 191 32 * 1024, /* 32k page size */ 192 &file_reader, 193 file, 194 &file_free_callback); 195 if (NULL == response) 196 { 197 fclose (file); 198 return MHD_NO; 199 } 200 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 201 MHD_destroy_response (response); 202 } 203 return ret; 204 } 205 206 207 int 208 main (int argc, char *const *argv) 209 { 210 struct MHD_Daemon *d; 211 int port; 212 213 if (argc != 2) 214 { 215 printf ("%s PORT\n", argv[0]); 216 return 1; 217 } 218 port = atoi (argv[1]); 219 if ( (1 > port) || (port > 65535) ) 220 { 221 fprintf (stderr, 222 "Port must be a number between 1 and 65535.\n"); 223 return 1; 224 } 225 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION 226 | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 227 (uint16_t) port, 228 NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END); 229 if (NULL == d) 230 return 1; 231 (void) getc (stdin); 232 MHD_stop_daemon (d); 233 return 0; 234 }