gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] r26706 - in libmicrohttpd: . src/examples


From: gnunet
Subject: [GNUnet-SVN] r26706 - in libmicrohttpd: . src/examples
Date: Sun, 31 Mar 2013 23:18:03 +0200

Author: grothoff
Date: 2013-03-31 23:18:03 +0200 (Sun, 31 Mar 2013)
New Revision: 26706

Modified:
   libmicrohttpd/ChangeLog
   libmicrohttpd/src/examples/demo.c
Log:
finished with demonstrator

Modified: libmicrohttpd/ChangeLog
===================================================================
--- libmicrohttpd/ChangeLog     2013-03-31 20:57:10 UTC (rev 26705)
+++ libmicrohttpd/ChangeLog     2013-03-31 21:18:03 UTC (rev 26706)
@@ -1,3 +1,6 @@
+Sun Mar 31 23:17:13 CEST 2013
+       Added MHD demonstration code 'src/examples/demo.c'. -CG
+
 Sun Mar 31 20:27:48 CEST 2013
        Adding new API call 'MHD_run_from_select' to allow programs
        running in 'external select mode' to reduce the number of

Modified: libmicrohttpd/src/examples/demo.c
===================================================================
--- libmicrohttpd/src/examples/demo.c   2013-03-31 20:57:10 UTC (rev 26705)
+++ libmicrohttpd/src/examples/demo.c   2013-03-31 21:18:03 UTC (rev 26706)
@@ -19,11 +19,14 @@
 
 /**
  * @file demo.c
- * @brief complex demonstration site: upload, index, download
+ * @brief complex demonstration site: create directory index, offer
+ *        upload via form and HTTP POST, download with mime type detection
+ *        and error reporting (403, etc.) --- and all of this with 
+ *        high-performance settings (large buffers, thread pool).
+ *        If you want to benchmark MHD, this code should be used to
+ *        run tests against.  Note that the number of threads may need
+ *        to be adjusted depending on the number of available cores.
  * @author Christian Grothoff
- *
- * TODO:
- * - should have a slightly more ambitious upload form & file listing 
(structure!)
  */
 #include "platform.h"
 #include <microhttpd.h>
@@ -34,13 +37,21 @@
 #include <dirent.h>
 #include <magic.h>
 
+
 /**
+ * Number of threads to run in the thread pool.  Should (roughly) match
+ * the number of cores on your system.
+ */
+#define NUMBER_OF_THREADS 8
+
+/**
  * How many bytes of a file do we give to libmagic to determine the mime type?
  * 16k might be a bit excessive, but ought not hurt performance much anyway,
  * and should definitively be on the safe side.
  */
 #define MAGIC_HEADER_SIZE (16 * 1024)
 
+
 /**
  * Page returned for file-not-found.
  */
@@ -63,10 +74,26 @@
  * 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\"/>"\
+   "<h1>Upload</h1>\n"\
+   "<form method=\"POST\" enctype=\"multipart/form-data\" action=\"/\">\n"\
+   "<dl><dt>Content type:</dt><dd>"\
+   "<input type=\"radio\" name=\"category\" value=\"books\">Book</input>"\
+   "<input type=\"radio\" name=\"category\" value=\"images\">Image</input>"\
+   "<input type=\"radio\" name=\"category\" value=\"music\">Music</input>"\
+   "<input type=\"radio\" name=\"category\" 
value=\"software\">Software</input>"\
+   "<input type=\"radio\" name=\"category\" value=\"videos\">Videos</input>\n"\
+   "<input type=\"radio\" name=\"category\" value=\"other\" 
checked>Other</input></dd>"\
+   "<dt>Language:</dt><dd>"\
+   "<input type=\"radio\" name=\"language\" value=\"no-lang\" 
checked>none</input>"\
+   "<input type=\"radio\" name=\"language\" value=\"en\">English</input>"\
+   "<input type=\"radio\" name=\"language\" value=\"de\">German</input>"\
+   "<input type=\"radio\" name=\"language\" value=\"fr\">French</input>"\
+   "<input type=\"radio\" name=\"language\" 
value=\"es\">Spanish</input></dd>\n"\
+   "<dt>File:</dt><dd>"\
+   "<input type=\"file\" name=\"upload\"/></dd></dl>"\
+   "<input type=\"submit\" value=\"Send!\"/>\n"\
    "</form>\n"\
+   "<h1>Download</h1>\n"\
    "<ol>\n"
 
 /**
@@ -75,8 +102,55 @@
 #define INDEX_PAGE_FOOTER "</ol>\n</body>\n</html>"
 
 
+/**
+ * NULL-terminated array of supported upload categories.  Should match HTML
+ * in the form.
+ */
+static const char * const categories[] =
+  {
+    "books",
+    "images",
+    "music",
+    "software",
+    "videos",
+    "other",
+    NULL,
+  };
 
+
 /**
+ * Specification of a supported language.
+ */
+struct Language
+{
+  /**
+   * Directory name for the language.
+   */
+  const char *dirname;
+
+  /**
+   * Long name for humans.
+   */
+  const char *longname;
+
+};
+
+/**
+ * NULL-terminated array of supported upload categories.  Should match HTML
+ * in the form.
+ */
+static const struct Language languages[] =
+  {
+    { "no-lang", "No language specified" },
+    { "en", "English" },
+    { "de", "German" },
+    { "fr", "French" },
+    { "es", "Spanish" },
+    { NULL, NULL },
+  };
+
+
+/**
  * Response returned if the requested file does not exist (or is not 
accessible).
  */
 static struct MHD_Response *file_not_found_response;
@@ -223,6 +297,12 @@
   static size_t initial_allocation = 32 * 1024; /* initial size for response 
buffer */
   struct MHD_Response *response;
   struct ResponseDataContext rdc;
+  unsigned int language_idx;
+  unsigned int category_idx;
+  const struct Language *language;
+  const char *category;
+  char dir_name[128];
+  struct stat sbuf;
 
   rdc.buf_len = initial_allocation; 
   if (NULL == (rdc.buf = malloc (rdc.buf_len)))
@@ -233,12 +313,42 @@
   rdc.off = snprintf (rdc.buf, rdc.buf_len,
                      "%s",
                      INDEX_PAGE_HEADER);
+  for (language_idx = 0; NULL != languages[language_idx].dirname; 
language_idx++)
+    {
+      language = &languages[language_idx];
 
-  if (MHD_NO == list_directory (&rdc, "."))
-    {
-      free (rdc.buf);
-      update_cached_response (NULL);
-      return;
+      if (0 != stat (language->dirname, &sbuf))
+       continue; /* empty */
+      /* we ensured always +1k room, filenames are ~256 bytes,
+        so there is always still enough space for the header
+        without need for an additional reallocation check. */
+      rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
+                          "<h2>%s</h2>\n",
+                          language->longname);
+      for (category_idx = 0; NULL != categories[category_idx]; category_idx++)
+       {
+         category = categories[category_idx];
+         snprintf (dir_name, sizeof (dir_name),
+                   "%s/%s",
+                   language->dirname,
+                   category);
+         if (0 != stat (dir_name, &sbuf))
+           continue; /* empty */
+
+         /* we ensured always +1k room, filenames are ~256 bytes,
+            so there is always still enough space for the header
+            without need for an additional reallocation check. */
+         rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
+                              "<h3>%s</h3>\n",
+                              category);
+                 
+         if (MHD_NO == list_directory (&rdc, dir_name))
+           {
+             free (rdc.buf);
+             update_cached_response (NULL);
+             return;
+           }
+       }
     }
   /* we ensured always +1k room, filenames are ~256 bytes,
      so there is always still enough space for the footer 
@@ -271,6 +381,16 @@
   char *filename;
 
   /**
+   * Language for the upload.
+   */
+  char *language;
+
+  /**
+   * Category for the upload.
+   */
+  char *category;
+
+  /**
    * Post processor we're using to process the upload.
    */
   struct MHD_PostProcessor *pp;
@@ -288,6 +408,40 @@
 
 
 /**
+ * Append the 'size' bytes from 'data' to '*ret', adding
+ * 0-termination.  If '*ret' is NULL, allocate an empty string first.
+ *
+ * @param ret string to update, NULL or 0-terminated
+ * @param data data to append
+ * @param size number of bytes in 'data'
+ * @return MHD_NO on allocation failure, MHD_YES on success
+ */
+static int
+do_append (char **ret,
+          const char *data,
+          size_t size)
+{
+  char *buf;
+  size_t old_len;
+  
+  if (NULL == *ret)
+    old_len = 0;
+  else
+    old_len = strlen (*ret);
+  buf = malloc (old_len + size + 1);
+  if (NULL == buf)
+    return MHD_NO;
+  memcpy (buf, *ret, old_len);
+  if (NULL != *ret)
+    free (*ret);
+  memcpy (&buf[old_len], data, size);
+  buf[old_len + size] = '\0';
+  *ret = buf;
+  return MHD_YES;
+}
+
+
+/**
  * Iterator over key-value pairs where the value
  * maybe made available in increments and/or may
  * not be zero-terminated.  Used for processing
@@ -319,13 +473,35 @@
 {
   struct UploadContext *uc = cls;
 
+  if (0 == strcmp (key, "category"))
+    return do_append (&uc->category, data, size);
+  if (0 == strcmp (key, "language"))
+    return do_append (&uc->language, data, size);
+  if (0 != strcmp (key, "upload"))
+    {
+      fprintf (stderr, 
+              "Ignoring unexpected form value `%s'\n",
+              key);
+      return MHD_YES; /* ignore */  
+    }
   if (NULL == filename)
     {
       fprintf (stderr, "No filename, aborting upload\n");
       return MHD_NO; /* no filename, error */
     }
+  if ( (NULL == uc->category) ||
+       (NULL == uc->language) )
+    {
+      fprintf (stderr, 
+              "Missing form data for upload `%s'\n",
+              filename);
+      uc->response = request_refused_response;
+      return MHD_NO;
+    }
   if (-1 == uc->fd)
     {
+      char fn[PATH_MAX];
+
       if ( (NULL != strstr (filename, "..")) ||
           (NULL != strchr (filename, '/')) ||
           (NULL != strchr (filename, '\\')) )
@@ -333,7 +509,21 @@
          uc->response = request_refused_response;
          return MHD_NO;
        }
-      uc->fd = open (filename, 
+      /* create directories -- if they don't exist already */
+      (void) mkdir (uc->language, S_IRWXU);
+      snprintf (fn, sizeof (fn),
+               "%s/%s",
+               uc->language,
+               uc->category);      
+      (void) mkdir (fn, S_IRWXU);
+
+      /* open file */
+      snprintf (fn, sizeof (fn),
+               "%s/%s/%s",
+               uc->language,
+               uc->category,
+               filename);      
+      uc->fd = open (fn, 
                     O_CREAT | O_EXCL 
 #if O_LARGEFILE
                     | O_LARGEFILE
@@ -344,20 +534,20 @@
        {
          fprintf (stderr, 
                   "Error opening file `%s' for upload: %s\n",
-                  filename,
+                  fn,
                   strerror (errno));
          uc->response = request_refused_response;
          return MHD_NO;
        }      
+      uc->filename = strdup (fn);
     }
-  uc->filename = strdup (filename);
   if ( (0 != size) &&
        (size != write (uc->fd, data, size)) )    
     {
       /* write failed; likely: disk full */
       fprintf (stderr, 
               "Error writing to file `%s': %s\n",
-              filename,
+              uc->filename,
               strerror (errno));
       uc->response = internal_error_response;
       close (uc->fd);
@@ -525,12 +715,11 @@
        {
          if (NULL == (uc = malloc (sizeof (struct UploadContext))))
            return MHD_NO; /* out of memory, close connection */
-         uc->response = NULL;
-         uc->filename = NULL;
+         memset (uc, 0, sizeof (struct UploadContext));
           uc->fd = -1;
          uc->connection = connection;
          uc->pp = MHD_create_post_processor (connection,
-                                             32 * 1024 /* buffer size */,
+                                             64 * 1024 /* buffer size */,
                                              &process_upload_data, uc);
          if (NULL == uc->pp)
            {
@@ -543,7 +732,7 @@
        }     
       if (0 != *upload_data_size)
        {
-         if (NULL != uc->response)
+         if (NULL == uc->response)
            (void) MHD_post_process (uc->pp, 
                                     upload_data,
                                     *upload_data_size);
@@ -620,12 +809,14 @@
                                                             
MHD_RESPMEM_PERSISTENT);
   mark_as_html (internal_error_response);
   update_directory ();
-  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY,
                         port,
                         NULL, NULL, 
                        &generate_page, NULL, 
-                       MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (1024 * 
1024),
-                       MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) 8,
+                       MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (256 * 
1024),
+                       MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) (64),
+                       MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) (120 /* 
seconds */),
+                       MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) 
NUMBER_OF_THREADS,
                        MHD_OPTION_NOTIFY_COMPLETED, 
&response_completed_callback, NULL,
                        MHD_OPTION_END);
   if (NULL == d)




reply via email to

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