libmicrohttpd

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

fileserver_example_external_select.c (5913B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007, 2008 Christian Grothoff (and other contributing authors)
      4      Copyright (C) 2014-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  * @file fileserver_example_external_select.c
     22  * @brief minimal example for how to use libmicrohttpd to server files
     23  * @author Christian Grothoff
     24  * @author Karlson2k (Evgeny Grin)
     25  */
     26 
     27 #include "platform.h"
     28 #include <microhttpd.h>
     29 #include <sys/stat.h>
     30 #include <unistd.h>
     31 #include <errno.h>
     32 
     33 #define PAGE \
     34   "<html><head><title>File not found</title></head><body>File not found</body></html>"
     35 
     36 static ssize_t
     37 file_reader (void *cls, uint64_t pos, char *buf, size_t max)
     38 {
     39   FILE *file = (FILE *) cls;
     40   size_t bytes_read;
     41 
     42   /* 'fseek' may not support files larger 2GiB, depending on platform.
     43    * For production code, make sure that 'pos' has valid values, supported by
     44    * 'fseek', or use 'fseeko' or similar function. */
     45   if (0 != fseek (file, (long) pos, SEEK_SET))
     46     return MHD_CONTENT_READER_END_WITH_ERROR;
     47   bytes_read = fread (buf, 1, max, file);
     48   if (0 == bytes_read)
     49     return (0 != ferror (file)) ? MHD_CONTENT_READER_END_WITH_ERROR :
     50            MHD_CONTENT_READER_END_OF_STREAM;
     51   return (ssize_t) bytes_read;
     52 }
     53 
     54 
     55 static void
     56 free_callback (void *cls)
     57 {
     58   FILE *file = cls;
     59   fclose (file);
     60 }
     61 
     62 
     63 static enum MHD_Result
     64 ahc_echo (void *cls,
     65           struct MHD_Connection *connection,
     66           const char *url,
     67           const char *method,
     68           const char *version,
     69           const char *upload_data,
     70           size_t *upload_data_size, void **req_cls)
     71 {
     72   static int aptr;
     73   struct MHD_Response *response;
     74   enum MHD_Result ret;
     75   FILE *file;
     76   int fd;
     77   struct stat buf;
     78   (void) cls;               /* Unused. Silent compiler warning. */
     79   (void) version;           /* Unused. Silent compiler warning. */
     80   (void) upload_data;       /* Unused. Silent compiler warning. */
     81   (void) upload_data_size;  /* Unused. Silent compiler warning. */
     82 
     83   if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
     84     return MHD_NO;              /* unexpected method */
     85   if (&aptr != *req_cls)
     86   {
     87     /* do never respond on first call */
     88     *req_cls = &aptr;
     89     return MHD_YES;
     90   }
     91   *req_cls = NULL;                  /* reset when done */
     92 
     93   file = fopen (&url[1], "rb");
     94   if (NULL != file)
     95   {
     96     fd = fileno (file);
     97     if (-1 == fd)
     98     {
     99       (void) fclose (file);
    100       return MHD_NO;     /* internal error */
    101     }
    102     if ( (0 != fstat (fd, &buf)) ||
    103          (! S_ISREG (buf.st_mode)) )
    104     {
    105       /* not a regular file, refuse to serve */
    106       fclose (file);
    107       file = NULL;
    108     }
    109   }
    110 
    111   if (NULL == file)
    112   {
    113     response = MHD_create_response_from_buffer_static (strlen (PAGE),
    114                                                        PAGE);
    115     ret = MHD_queue_response (connection, MHD_HTTP_NOT_FOUND, response);
    116     MHD_destroy_response (response);
    117   }
    118   else
    119   {
    120     response = MHD_create_response_from_callback ((size_t) buf.st_size,
    121                                                   32 * 1024, /* 32k page size */
    122                                                   &file_reader,
    123                                                   file,
    124                                                   &free_callback);
    125     if (NULL == response)
    126     {
    127       fclose (file);
    128       return MHD_NO;
    129     }
    130     ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    131     MHD_destroy_response (response);
    132   }
    133   return ret;
    134 }
    135 
    136 
    137 int
    138 main (int argc, char *const *argv)
    139 {
    140   struct MHD_Daemon *d;
    141   time_t end;
    142   time_t t;
    143   struct timeval tv;
    144   fd_set rs;
    145   fd_set ws;
    146   fd_set es;
    147   MHD_socket max;
    148   uint64_t mhd_timeout;
    149   int port;
    150 
    151   if (argc != 3)
    152   {
    153     printf ("%s PORT SECONDS-TO-RUN\n", argv[0]);
    154     return 1;
    155   }
    156   port = atoi (argv[1]);
    157   if ( (1 > port) || (port > 65535) )
    158   {
    159     fprintf (stderr,
    160              "Port must be a number between 1 and 65535.\n");
    161     return 1;
    162   }
    163 
    164   d = MHD_start_daemon (MHD_USE_ERROR_LOG,
    165                         (uint16_t) port,
    166                         NULL, NULL, &ahc_echo, NULL,
    167                         MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
    168                         MHD_OPTION_END);
    169   if (d == NULL)
    170     return 1;
    171   end = time (NULL) + atoi (argv[2]);
    172   while ((t = time (NULL)) < end)
    173   {
    174 #if ! defined(_WIN32) || defined(__CYGWIN__)
    175     tv.tv_sec = end - t;
    176 #else  /* Native W32 */
    177     tv.tv_sec = (long) (end - t);
    178 #endif /* Native W32 */
    179     tv.tv_usec = 0;
    180     max = 0;
    181     FD_ZERO (&rs);
    182     FD_ZERO (&ws);
    183     FD_ZERO (&es);
    184     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
    185       break; /* fatal internal error */
    186     if (MHD_get_timeout64 (d, &mhd_timeout) == MHD_YES)
    187     {
    188       if (((uint64_t) tv.tv_sec) < mhd_timeout / 1000LL)
    189       {
    190 #if ! defined(_WIN32) || defined(__CYGWIN__)
    191         tv.tv_sec = (time_t) (mhd_timeout / 1000LL);
    192 #else  /* Native W32 */
    193         tv.tv_sec = (long) (mhd_timeout / 1000LL);
    194 #endif /* Native W32 */
    195         tv.tv_usec = ((long) (mhd_timeout % 1000)) * 1000;
    196       }
    197     }
    198     if (-1 == select ((int) max + 1, &rs, &ws, &es, &tv))
    199     {
    200       if (EINTR != errno)
    201         abort ();
    202     }
    203     MHD_run (d);
    204   }
    205   MHD_stop_daemon (d);
    206   return 0;
    207 }