]> code.delx.au - gnu-emacs-elpa/commitdiff
more documentation and examples
authorEric Schulte <schulte.eric@gmail.com>
Thu, 26 Dec 2013 00:08:09 +0000 (17:08 -0700)
committerEric Schulte <schulte.eric@gmail.com>
Thu, 26 Dec 2013 02:56:10 +0000 (19:56 -0700)
NOTES
README
doc/emacs-web-server.texi
emacs-web-server-test.el
emacs-web-server.el
examples/0-hello-world.el
examples/1-hello-world-utf8.el
examples/2-hello-world-html.el
examples/3-file-server.el
examples/4-url-param-echo.el
examples/5-post-echo.el

diff --git a/NOTES b/NOTES
index 26e018ac924282d83c3f1d73654e5d6e13d53286..78030da394d152e191a1e6d6181e43b388a8d331 100644 (file)
--- a/NOTES
+++ b/NOTES
@@ -2,7 +2,7 @@
 
 * Notes
 * Tasks [7/9]
-** TODO Documentation [0/4]
+** STARTED Documentation [0/4]
 - [ ] introduction
 - [ ] handlers
 - [ ] request headers
diff --git a/README b/README
index 6f7ccd7541e852bc17e36f65a6f6d9497975ae00..78d4d6a7dc286e0b070a70f959622adf290b7da3 100644 (file)
--- a/README
+++ b/README
@@ -12,4 +12,5 @@ STATUS
 
 EXAMPLES
     See the examples/ directory in this repository.  The Emacs Web
-    Server is also used to run https://github.com/eschulte/el-sprunge.
+    Server is also used to run https://github.com/eschulte/el-sprunge
+    and https://github.com/eschulte/org-ehtml/tree/emacs-web-server.
index e5ac1c9426a8c9a4fb31f2a99ea8fab275823ad3..30d79cfe22485c9e9034c5fd48664aa4129362c1 100644 (file)
@@ -41,7 +41,7 @@ A copy of the license is included in the section entitled
 @end ifnottex
 
 @menu
-* Introduction::                Getting to know the Emacs Web Server
+* Introduction::                Overview of the Emacs Web Server
 * Handlers::                    Handlers respond to HTTP requests
 * Request::                     Getting information on HTTP requests
 * Usage Examples::              Examples demonstrating usage
@@ -65,30 +65,32 @@ Appendices
 @chapter Introduction
 @cindex introduction
 
-The Emacs Web Server is a Web server implemented in Emacs Lisp using
-Emacs network communication primitives.  HTTP requests are matched to
-handlers (@pxref{Handlers}) which are implemented as Emacs Lisp
-functions.  Handler functions receive a request object
-(@pxref{Request}) which holds information about the request and the
-HTTP connection process.  Handlers write their responses directly to
-the connection process.
+The Emacs Web Server is a Web server implemented entirely in Emacs
+Lisp.  HTTP requests are matched to handlers (@pxref{Handlers}) which
+are Emacs Lisp functions.  Handlers receive as their only argument a
+request object (@pxref{Request}) which holds information about the
+request and the process holding HTTP network connection.  Handlers
+write their responses directly to the network process.
 
 A number of examples (@pxref{Usage Examples}) demonstrate usage of the
-Emacs Web Server.  Finally, the functions defining the interface are
+Emacs Web Server.  All public functions of the Emacs Web Server are
 listed (@pxref{Function Index}).
 
 @node Handlers, Request, Handlers, Top
 @chapter Handlers
 @cindex handlers
 
-The Emacs Web Server is started with the @code{ews-start} function
-which takes a ``handlers'' association list which is composed of pairs
-of matchers and handler functions.
+The function @code{ews-start} takes takes two arguments
+@code{handlers} and @code{port}.  It starts a server listening on
+@code{port} responding to requests with @code{handlers}, an
+association list composed of pairs of matchers and handler functions.
 
-@emph{Matchers} may be either a simple regular expression or a
-function.  A simple matcher consists of an HTTP header and a regular
-expression.  When the regular expression matches the content of that
-header the simple matcher succeeds and the associated handler is
+@section Matchers
+
+Matchers may be a regular expression or a function.  Regular
+expression matchers consists of an HTTP header and a regular
+expression.  When the regular expression matches the content of the
+given header the matcher succeeds and the associated handler is
 called.  For example the following matches any @code{GET} request
 whose path starts with the substring ``foo''.
 
@@ -96,9 +98,9 @@ whose path starts with the substring ``foo''.
 (:GET . "^foo")
 @end example
 
-A complex matcher is a function which takes the request object
+A function matcher is a function which takes the request object
 (@pxref{Request}) and succeeds when the function returns a non-nil
-value.  For example the following matcher matches every request
+value.  For example the following matcher matches every request,
 
 @example
 (lambda (_) t)
@@ -108,17 +110,34 @@ and the following matches only requests in which the supplied
 ``number'' parameter is odd.
 
 @example
-(lambda (request) (oddp (cdr (assoc "number" request))))
+(lambda (request)
+  (oddp (string-to-number (cdr (assoc "number" request)))))
 @end example
 
+@section Handler
+
+Each handler is a function which takes a request object
+(@pxref{Request}) as its only argument.  The function may respond to
+the request by writing to the network process held in the
+@code{process} field of the request object.  For example, the
+@code{process-send-string} function may be used to write string data
+to a request as in the following.
+
+@example
+  (process-send-string (process request) "hello world")
+@end example
+
+When the handler function exits the connection is terminated unless
+the handler function returns the keyword @code{:keep-alive}.
+
 @node Request, Usage Examples, Handlers, Top
 @chapter Request
 @cindex request
 
-Information on requests is stored in a @code{request} object.  The
+Each HTTP requests is represented using a @code{request} object.  The
 request object is used to decide which handler to call, and is passed
-to the called handler.  This object holds information on the request
-including the request process, all HTTP headers, and parameters.
+as an argument to the called handler.  The request object holds the
+network process, all HTTP headers, and any parameters.
 
 The text of the request is parsed into an alist.  HTML Headers are
 keyed using uppercase keywords (e.g., @code{:GET}), and user supplied
@@ -140,27 +159,67 @@ These examples demonstrate usage.
 
 @node Hello World, Hello World UTF8, Usage Examples, Usage Examples
 @section Hello World
+
+The simplest possible ``hello world'' example.  The handler consists
+of a single (matcher . handler) pair.  The function matcher matches
+@emph{every} incoming HTTP request.  The handler responds by setting
+the content type to @code{text/plain}, and then sending the string
+``hello world''.  When the handler exits the network connection of the
+request is closed.
+
 @verbatiminclude ../examples/0-hello-world.el
 
 @node Hello World UTF8, Hello World HTML, Hello World, Usage Examples
 @section Hello World UTF8
+
+This example only differs from the previous in that the
+``Content-type'' indicates UTF8 encoded data, and the hello world sent
+is selected at random from a list of different languages.
+
 @verbatiminclude ../examples/1-hello-world-utf8.el
 
 @node Hello World HTML, File Server, Hello World UTF8, Usage Examples
 @section Hello World HTML
 @verbatiminclude ../examples/2-hello-world-html.el
 
+This variation of the ``hello world'' example sends a @code{text/html}
+response instead of a simple @code{text/plain} response.
+
 @node File Server, URL Parameter Echo, Hello World HTML, Usage Examples
 @section File Server
+
+The following example implements a file server which will serve files
+from the @code{docroot} document root set to the current working
+directory in this example.  Three helper functions are used;
+@code{ews-subdirectoryp} is used to check if the requested path is
+within the document root, if so the file is served and
+@code{ews-send-file} is used to appropriately set the mime-type of the
+response based on the extension of the file, if not then
+@code{ews-send-404} is used to send a default ``File Not Found''
+response.
+
 @verbatiminclude ../examples/3-file-server.el
 
 @node URL Parameter Echo, POST Echo, File Server, Usage Examples
 @section URL Parameter Echo
+
+This example demonstrates access of URL-encoded parameters in a
+@code{GET} request.  For example the following URL
+@url{http://localhost:9005/example?foo=bar&baz=qux} will render as an
+the following HTML table.
+
+@multitable @columnfractions .5 .5
+@item foo @tab bar
+@item baz @tab qux
+@end multitable
+
 @verbatiminclude ../examples/4-url-param-echo.el
 
 @node POST Echo, Function Index, URL Parameter Echo, Usage Examples
 @section POST Echo
-POST parameters are used for example when HTML forms are submitted.
+
+The following example echos back the content of the ``message'' field
+in a @code{POST} request.
 
 @verbatiminclude ../examples/5-post-echo.el
 
index 62dad3e1df9abf6d13d8566d1cbe947ba0f0451f..aa80442434f42323526d40901925de66c2193bab 100644 (file)
@@ -165,4 +165,18 @@ org=-+one%0A-+two%0A-+three%0A-+four%0A%0A&beg=646&end=667&path=%2Fcomplex.org")
 "))))
       (ews-stop server))))
 
+(ert-deftest ews/simple-post ()
+  "Test a simple POST server."
+  (ews-test-with
+      '(((:POST . ".*") .
+         (lambda (request)
+           (with-slots (process headers) request
+             (let ((message (cdr (assoc "message" headers))))
+               (ews-response-header process 200
+                 '("Content-type" . "text/plain"))
+               (process-send-string process
+                 (format "you said %S\n" message)))))))
+    (should (string= (ews-test-curl-to-string "" nil '(("message" . "foo")))
+                     "you said \"foo\"\n"))))
+
 (provide 'emacs-web-server-test)
index a97e56e74bf5661363b4331a61504441f0943341..29838f9c1b188c28ad8caa2249f33df176939e63 100644 (file)
@@ -24,6 +24,7 @@
 (defclass ews-request ()
   ((process  :initarg :process  :accessor process  :initform nil)
    (pending  :initarg :pending  :accessor pending  :initform "")
+   (context  :initarg :context  :accessor context  :initform nil)
    (boundary :initarg :boundary :accessor boundary :initform nil)
    (headers  :initarg :headers  :accessor headers  :initform (list nil))))
 
@@ -162,15 +163,12 @@ function.
 (defun ews-parse-request (request string)
   "Parse request STRING from REQUEST with process PROC.
 Return non-nil only when parsing is complete."
-  (with-slots (process pending boundary headers) request
+  (with-slots (process pending context boundary headers) request
     (setq pending (concat pending string))
     (let ((delimiter (concat "\r\n" (if boundary (concat "--" boundary) "")))
           ;; Track progress through string, always work with the
           ;; section of string between LAST-INDEX and INDEX.
-          (last-index 0) index
-          ;; Current context, either a particular content-type for
-          ;; custom parsing or nil for no special parsing.
-          context)
+          (last-index 0) index)
       (catch 'finished-parsing-headers
         ;; parse headers and append to request
         (while (setq index (string-match delimiter pending last-index))
index 76ae4f3b3fb3348a7c812b630fbe87d8b57fecbe..91e819a6d1fe014b87a6da0908ce068c4aa71c2b 100644 (file)
@@ -1,7 +1,8 @@
 ;;; hello-world.el --- simple hello world server using Emacs Web Server
 (ews-start
  '(((lambda (_) t) .
-    (lambda (proc request)
-      (ews-response-header proc 200 '("Content-type" . "text/plain"))
-      (process-send-string proc "hello world"))))
+    (lambda (request)
+      (with-slots (process headers) request
+        (ews-response-header process 200 '("Content-type" . "text/plain"))
+        (process-send-string process "hello world")))))
  9000)
index d7804b2c43e97ff7678122b1a534ebdb4c703e0f..0e00362a6153f6bb5de261bd9404b47afa289636 100644 (file)
@@ -1,20 +1,21 @@
 ;;; hello-world-utf8.el --- utf8 hello world server using Emacs Web Server
 (ews-start
  '(((lambda (_) t) .
-    (lambda (proc request)
-      (let ((hellos '("こんにちは"
-                      "안녕하세요"
-                      "góðan dag"
-                      "Grüßgott"
-                      "hyvää päivää"
-                      "yá'át'ééh"
-                      "Γεια σας"
-                      "Вiтаю"
-                      "გამარჯობა"
-                      "नमस्ते"
-                      "你好")))
-        (ews-response-header proc 200
-          '("Content-type" . "text/plain; charset=utf-8"))
-        (process-send-string proc
-          (concat (nth (random (length hellos)) hellos) " world"))))))
+    (lambda (request)
+      (with-slots (process headers) request
+        (let ((hellos '("こんにちは"
+                        "안녕하세요"
+                        "góðan dag"
+                        "Grüßgott"
+                        "hyvää päivää"
+                        "yá'át'ééh"
+                        "Γεια σας"
+                        "Вiтаю"
+                        "გამარჯობა"
+                        "नमस्ते"
+                        "你好")))
+          (ews-response-header process 200
+            '("Content-type" . "text/plain; charset=utf-8"))
+          (process-send-string process
+            (concat (nth (random (length hellos)) hellos) " world")))))))
  9001)
index 49d4420c9304af1c6b93397c424c84359126f258..5f2587a82191e62889356a32506630cfcdf4b767 100644 (file)
@@ -1,9 +1,10 @@
 ;;; hello-world-html.el --- html hello world server using Emacs Web Server
 (ews-start
  '(((lambda (_) t) .
-    (lambda (proc request)
-      (ews-response-header proc 200 '("Content-type" . "text/html"))
-      (process-send-string proc "<html>
+    (lambda (request)
+      (with-slots (process headers) request
+        (ews-response-header process 200 '("Content-type" . "text/html"))
+        (process-send-string process "<html>
   <head>
     <title>Hello World</title>
   </head>
@@ -11,5 +12,5 @@
     <b>hello world</b>
   </body>
 </html>
-"))))
+")))))
  9002)
index ba32c65799e0a994b0f91957c3f6f4bb47331f39..ba7cfb10b852024156edc77b5eea4a79a8c8bf93 100644 (file)
@@ -1,7 +1,11 @@
 ;;; file-server.el --- serve any files using Emacs Web Server
-;; This example uses absolute paths and will try to serve files from
-;; the root of the file-system, so don't run it on a public server.
-(ews-start
- '(((:GET . ".*") .
-    (lambda (proc request) (ews-send-file proc (cdr (assoc :GET request))))))
- 9004)
+(lexical-let ((docroot default-directory))
+  (ews-start
+   (list (cons (cons :GET ".*")
+               (lambda (request)
+                 (with-slots (process headers) request
+                   (let ((path (substring (cdr (assoc :GET headers)) 1)))
+                     (if (ews-subdirectoryp docroot path)
+                         (ews-send-file process (expand-file-name path docroot))
+                       (ews-send-404 process)))))))
+   9004))
index 48a5284a3e8920015348ab49aaa9766abdcd28d6..2277b4d206ad1e5229f763128bcdc75e347e7e55 100644 (file)
@@ -1,15 +1,16 @@
 ;;; url-param-echo.el --- echo back url-paramed message using Emacs Web Server
 (ews-start
  '(((:GET . ".*") .
-    (lambda (proc request)
-      (ews-response-header proc 200 '("Content-type" . "text/html"))
-      (process-send-string proc
-        (concat "URL Parameters:</br><table><tr>"
-                (mapconcat (lambda (pair)
-                             (format "<th>%s</th><td>%s</td>"
-                                     (car pair) (cdr pair)))
-                           (cl-remove-if-not (lambda (el) (stringp (car el)))
-                                             request)
-                           "</tr><tr>")
-                "</tr></table>")))))
+    (lambda (request)
+      (with-slots (process headers) request
+        (ews-response-header process 200 '("Content-type" . "text/html"))
+        (process-send-string process
+          (concat "URL Parameters:</br><table><tr>"
+                  (mapconcat (lambda (pair)
+                               (format "<th>%s</th><td>%s</td>"
+                                       (car pair) (cdr pair)))
+                             (cl-remove-if-not (lambda (el) (stringp (car el)))
+                                               headers)
+                             "</tr><tr>")
+                  "</tr></table>"))))))
  9005)
index b4d81d4adf9c7706ab6fc65f3f4a2f09389f8993..b7cd78dc9718e2fe4a5a8a2359e519ddc74c1336 100644 (file)
@@ -1,16 +1,18 @@
 ;;; post-echo.el --- echo back posted message using Emacs Web Server
 (ews-start
  '(((:POST . ".*") .
-    (lambda (proc request)
-      (let ((message (cdr (assoc "message" request))))
-        (ews-response-header proc 200 '("Content-type" . "text/plain"))
-        (process-send-string proc
-          (if message
-              (format "you said %S\n" message)
-            "This is a POST request, but it has no \"message\".\n")))))
+    (lambda (request)
+      (with-slots (process headers) request
+        (let ((message (cdr (assoc "message" headers))))
+          (ews-response-header process 200 '("Content-type" . "text/plain"))
+          (process-send-string process
+            (if message
+                (format "you said %S\n" message)
+              "This is a POST request, but it has no \"message\".\n"))))))
    ((:GET . ".*") .
-    (lambda (proc request)
-      (ews-response-header proc 200 '("Content-type" . "text/plain"))
-      (process-send-string proc
-        "This is a GET request not a POST request.\n"))))
+    (lambda (request)
+      (with-slots (process) request
+        (ews-response-header process 200 '("Content-type" . "text/plain"))
+        (process-send-string process
+          "This is a GET request not a POST request.\n")))))
  9003)