gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r26686 - libmicrohttpd/src/examples


From: gnunet
Subject: [GNUnet-SVN] r26686 - libmicrohttpd/src/examples
Date: Sun, 31 Mar 2013 00:45:39 +0100

Author: grothoff
Date: 2013-03-31 00:45:39 +0100 (Sun, 31 Mar 2013)
New Revision: 26686

Added:
   libmicrohttpd/src/examples/demo.c
Modified:
   libmicrohttpd/src/examples/Makefile.am
Log:
-starting more complex demo

Modified: libmicrohttpd/src/examples/Makefile.am
===================================================================
--- libmicrohttpd/src/examples/Makefile.am      2013-03-30 21:23:42 UTC (rev 
26685)
+++ libmicrohttpd/src/examples/Makefile.am      2013-03-30 23:45:39 UTC (rev 
26686)
@@ -29,6 +29,7 @@
 endif
 if HAVE_POSTPROCESSOR
 noinst_PROGRAMS += \
+  demo \
   post_example 
 endif
 
@@ -51,6 +52,11 @@
 minimal_example_LDADD = \
  $(top_builddir)/src/daemon/libmicrohttpd.la 
 
+demo_SOURCES = \
+ demo.c 
+demo_LDADD = \
+ $(top_builddir)/src/daemon/libmicrohttpd.la 
+
 dual_stack_example_SOURCES = \
  dual_stack_example.c 
 dual_stack_example_LDADD = \

Added: libmicrohttpd/src/examples/demo.c
===================================================================
--- libmicrohttpd/src/examples/demo.c                           (rev 0)
+++ libmicrohttpd/src/examples/demo.c   2013-03-30 23:45:39 UTC (rev 26686)
@@ -0,0 +1,474 @@
+/*
+     This file is part of libmicrohttpd
+     (C) 2013 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 
 USA
+*/
+
+/**
+ * @file demo.c
+ * @brief complex demonstration site: upload, index, download
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <microhttpd.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+
+/**
+ * Page returned for file-not-found.
+ */
+#define FILE_NOT_FOUND_PAGE "<html><head><title>File not 
found</title></head><body>File not found</body></html>"
+
+
+/**
+ * Page returned for internal errors.
+ */
+#define INTERNAL_ERROR_PAGE "<html><head><title>Internal 
error</title></head><body>Internal error</body></html>"
+
+
+/**
+ * Head of index page.
+ */
+#define INDEX_PAGE_HEADER 
"<html>\n<head><title>Welcome</title></head>\n<body>\n"\
+   "<form method=\"POST\" enctype=\"multipart/form-data\" action=\"/\">"\
+   "Upload: <input type=\"file\" name=\"upload\"/>"\
+   "<input type=\"submit\" value=\"Send\"/>"\
+   "</form>\n"\
+   "<ol>\n"
+
+/**
+ * Footer of index page.
+ */
+#define INDEX_PAGE_FOOTER "</ol>\n</body>\n</html>"
+
+
+/**
+ * Response returned if the requested file does not exist (or is not 
accessible).
+ */
+static struct MHD_Response *file_not_found_response;
+
+/**
+ * Response returned for internal errors.
+ */
+static struct MHD_Response *internal_error_response;
+
+/**
+ * Response returned for '/' (GET) to list the contents of the directory and 
allow upload.
+ */
+static struct MHD_Response *cached_directory_response;
+
+/**
+ * Mutex used when we update the cached directory response object.
+ */
+static pthread_mutex_t mutex;
+
+
+/**
+ * Replace the existing 'cached_directory_response' with the
+ * given response.
+ *
+ * @param response new directory response
+ */
+static void
+update_cached_response (struct MHD_Response *response)
+{
+  (void) pthread_mutex_lock (&mutex);
+  if (NULL != cached_directory_response)
+    MHD_destroy_response (cached_directory_response);
+  cached_directory_response = response;
+  (void) pthread_mutex_unlock (&mutex);
+}
+
+
+/**
+ * Re-scan our local directory and re-build the index.
+ */
+static void
+update_directory ()
+{
+  static size_t initial_allocation = 32 * 1024; /* initial size for response 
buffer */
+  DIR *dir;
+  struct dirent *de;
+  struct MHD_Response *response;
+  char *buf;
+  size_t buf_len;
+  size_t off;
+
+  dir = opendir (".");
+  if (NULL == dir)
+    goto err;
+  buf_len = initial_allocation; 
+  buf = malloc (buf_len);
+  if (NULL == buf)
+    {
+      closedir (dir);
+      goto err;
+    }
+  off = snprintf (buf, buf_len,
+                 "%s",
+                 INDEX_PAGE_HEADER);
+  while (NULL != (de = readdir (dir)))
+    {
+      if ('.' == de->d_name[0])
+       continue;
+      if (off + 1024 > buf_len)
+       {
+         void *r;
+
+         if ( (2 * buf_len + 1024) < buf_len)
+           break; /* more than SIZE_T _index_ size? Too big for us */
+         buf_len = 2 * buf_len + 1024;
+         if (NULL == (r = realloc (buf, buf_len)))
+           break; /* out of memory */
+         buf = r;
+       }
+      off += snprintf (&buf[off], buf_len - off,
+                      "<li><a href=\"/%s\">%s</a></li>\n",
+                      de->d_name,
+                      de->d_name);
+    }
+  /* we ensured always +1k room, filenames are ~256 bytes,
+     so there is always still enough space for the footer 
+     without need for a final reallocation check. */
+  off += snprintf (&buf[off], buf_len - off,
+                  "%s",
+                  INDEX_PAGE_FOOTER);
+  closedir (dir);
+  initial_allocation = buf_len; /* remember for next time */
+  response = MHD_create_response_from_buffer (off,
+                                             buf,
+                                             MHD_RESPMEM_MUST_FREE);
+  update_cached_response (response);
+  return;
+ err:
+  /* failed to list directory, use error page */
+  update_cached_response (NULL);
+}
+
+
+/**
+ * Context we keep for an upload.
+ */
+struct UploadContext
+{
+  /**
+   * Handle where we write the uploaded file to.
+   */
+  int fd;
+
+  /**
+   * Post processor we're using to process the upload.
+   */
+  struct MHD_PostProcessor *pp;
+
+  /**
+   * Handle to connection that we're processing the upload for.
+   */
+  struct MHD_Connection *connection;
+};
+
+
+/**
+ * Iterator over key-value pairs where the value
+ * maybe made available in increments and/or may
+ * not be zero-terminated.  Used for processing
+ * POST data.
+ *
+ * @param cls user-specified closure
+ * @param kind type of the value, always MHD_POSTDATA_KIND when called from MHD
+ * @param key 0-terminated key for the value
+ * @param filename name of the uploaded file, NULL if not known
+ * @param content_type mime-type of the data, NULL if not known
+ * @param transfer_encoding encoding of the data, NULL if not known
+ * @param data pointer to size bytes of data at the
+ *              specified offset
+ * @param off offset of data in the overall value
+ * @param size number of bytes in data available
+ * @return MHD_YES to continue iterating,
+ *         MHD_NO to abort the iteration
+ */
+static int
+process_upload_data (void *cls,
+                    enum MHD_ValueKind kind,
+                    const char *key,
+                    const char *filename,
+                    const char *content_type,
+                    const char *transfer_encoding,
+                    const char *data, 
+                    uint64_t off, 
+                    size_t size)
+{
+  struct UploadContext *uc = cls;
+
+  if (NULL == filename)
+    {
+      fprintf (stderr, "No filename, aborting upload\n");
+      return MHD_NO; /* no filename, error */
+    }
+  fprintf (stderr, "Got %u bytes of upload data for %s\n",
+          (unsigned int) size, filename);
+  if (-1 == uc->fd)
+    {
+      uc->fd = open (filename, 
+                    O_CREAT | O_EXCL 
+#if O_LARGEFILE
+                    | O_LARGEFILE
+#endif
+#if O_NONBLOCK
+                    | O_NONBLOCK
+#endif
+                    | O_WRONLY,
+                    S_IRUSR | S_IWUSR);
+      if (-1 == uc->fd)
+       {
+         // FIXME: generate error page NICELY
+         fprintf (stderr, "Error opening file to write!\n");
+         return MHD_NO;
+       }      
+    }
+  if ( (0 != size) &&
+       (size != write (uc->fd, data, size)) )    
+    {
+      // FIXME: generate error page NICELY
+      fprintf (stderr, "Error writing to disk!\n");
+      return MHD_NO; 
+    }
+  return MHD_YES;
+}
+
+
+/**
+ * Function called whenever a request was completed.
+ * Used to clean up 'struct UploadContext' objects.
+ *
+ * @param cls client-defined closure, NULL
+ * @param connection connection handle
+ * @param con_cls value as set by the last call to
+ *        the MHD_AccessHandlerCallback, points to NULL if this was
+ *            not an upload
+ * @param toe reason for request termination
+ */
+static void
+response_completed_callback (void *cls,
+                            struct MHD_Connection *connection,
+                            void **con_cls,
+                            enum MHD_RequestTerminationCode toe)
+{
+  struct UploadContext *uc = *con_cls;
+
+  if (NULL == uc)
+    return; /* this request wasn't an upload request */
+  if (NULL != uc->pp)
+    {
+      MHD_destroy_post_processor (uc->pp);
+      uc->pp = NULL;
+    }
+  if (-1 != uc->fd)
+  {
+    close (uc->fd);
+    fprintf (stderr, "Possible upload failure, need to remove file?!\n");
+    /* FIXME: unlink here on error!? */
+  }
+  free (uc);
+}
+
+
+/**
+ * Return the current directory listing.
+ * 
+ * @param connection connection to return the directory for
+ * @return MHD_YES on success, MHD_NO on error
+ */
+static int
+list_directory (struct MHD_Connection *connection)
+{
+  int ret;
+
+  (void) pthread_mutex_lock (&mutex);
+  if (NULL == cached_directory_response)
+    ret = MHD_queue_response (connection, 
+                             MHD_HTTP_INTERNAL_SERVER_ERROR, 
+                             internal_error_response);
+  else
+    ret = MHD_queue_response (connection, 
+                             MHD_HTTP_OK, 
+                             cached_directory_response);
+  (void) pthread_mutex_unlock (&mutex);
+  return ret;
+}
+
+
+
+/**
+ * Main callback from MHD, used to generate the page.
+ *
+ * @param cls NULL
+ * @param connection connection handle
+ * @param url requested URL
+ * @param method GET, PUT, POST, etc.
+ * @param version HTTP version
+ * @param upload_data data from upload (PUT/POST)
+ * @param upload_data_size number of bytes in "upload_data"
+ * @param ptr our context 
+ * @return MHD_YES on success, MHD_NO to drop connection
+ */
+static int
+generate_page (void *cls,
+              struct MHD_Connection *connection,
+              const char *url,
+              const char *method,
+              const char *version,
+              const char *upload_data,
+              size_t *upload_data_size, void **ptr)
+{  
+  struct MHD_Response *response;
+  int ret;
+  int fd;
+  struct stat buf;  
+
+  if (0 != strcmp (url, "/"))
+    {
+      /* should be file download */
+      if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
+       return MHD_NO;  /* unexpected method (we're not polite...) */
+      if ( (0 == stat (&url[1], &buf)) &&
+          (NULL == strstr (&url[1], "..")) &&
+          ('/' != url[1]))        
+       fd = open (&url[1], O_RDONLY);
+      else
+       fd = -1;
+      if (-1 == fd)
+       return MHD_queue_response (connection, 
+                                  MHD_HTTP_NOT_FOUND, 
+                                  file_not_found_response);
+      if (NULL == (response = MHD_create_response_from_fd (buf.st_size, 
+                                                          fd)))
+       {
+         /* internal error (i.e. out of memory) */
+         (void) close (fd);
+         return MHD_NO; 
+       }
+      ret = MHD_queue_response (connection, 
+                               MHD_HTTP_OK, 
+                               response);
+      MHD_destroy_response (response);
+      return ret;
+    }
+
+  if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
+    {
+      /* upload! */
+      struct UploadContext *uc = *ptr;
+
+      if (NULL == uc)
+       {
+         if (NULL == (uc = malloc (sizeof (struct UploadContext))))
+           return MHD_NO; /* out of memory, close connection */
+          uc->fd = -1;
+         uc->connection = connection;
+         uc->pp = MHD_create_post_processor (connection,
+                                             32 * 1024 /* buffer size */,
+                                             &process_upload_data, uc);
+         if (NULL == uc->pp)
+           {
+             /* out of memory, close connection */
+             free (uc);
+             return MHD_NO;
+           }
+         *ptr = uc;
+         return MHD_YES;
+       }
+      if (0 != *upload_data_size)
+       {
+         ret = MHD_post_process (uc->pp, 
+                                 upload_data,
+                                 *upload_data_size);
+         *upload_data_size = 0;
+         return ret;
+       }
+      /* end of upload, finish it! */
+      MHD_destroy_post_processor (uc->pp);
+      uc->pp = NULL;
+      if (-1 != uc->fd)
+       {
+         close (uc->fd);
+         uc->fd = -1;
+       }
+      update_directory ();
+      return list_directory (connection);
+    }
+  if (0 == strcmp (method, MHD_HTTP_METHOD_GET))
+    return list_directory (connection);
+
+  /* unexpected request, refuse */
+  fprintf (stderr, "Unexpected request, refusing\n");
+  return MHD_NO;
+}
+
+
+/**
+ * Entry point to demo.  Note: this HTTP server will make all
+ * files in the current directory and its subdirectories available
+ * to anyone.
+ *
+ * @param argc number of arguments in argv
+ * @param argv first and only argument should be the port number
+ * @return 0 on success
+ */
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+  unsigned int port;
+
+  if ( (argc != 2) ||
+       (1 != sscanf (argv[1], "%u", &port)) ||
+       (UINT16_MAX < port) ) 
+    {
+      fprintf (stderr,
+              "%s PORT\n", argv[0]);
+      return 1;
+    }
+  (void) pthread_mutex_init (&mutex, NULL);
+  file_not_found_response = MHD_create_response_from_buffer (strlen 
(FILE_NOT_FOUND_PAGE),
+                                                            (void *) 
FILE_NOT_FOUND_PAGE,
+                                                            
MHD_RESPMEM_PERSISTENT);
+  internal_error_response = MHD_create_response_from_buffer (strlen 
(INTERNAL_ERROR_PAGE),
+                                                            (void *) 
INTERNAL_ERROR_PAGE,
+                                                            
MHD_RESPMEM_PERSISTENT);
+  update_directory ();
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        port,
+                        NULL, NULL, 
+                       &generate_page, NULL, 
+                       MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) 8,
+                       MHD_OPTION_NOTIFY_COMPLETED, 
&response_completed_callback, NULL,
+                       MHD_OPTION_END);
+  if (NULL == d)
+    return 1;
+  (void) getc (stdin);
+  MHD_stop_daemon (d);
+  MHD_destroy_response (file_not_found_response);
+  MHD_destroy_response (internal_error_response);
+  update_cached_response (NULL);
+  (void) pthread_mutex_destroy (&mutex);
+  return 0;
+}
+
+/* end of demo.c */




reply via email to

[Prev in Thread] Current Thread [Next in Thread]