libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

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 }