source: project/wiki/eggref/4/spiffy @ 33263

Last change on this file since 33263 was 33263, checked in by sjamaan, 5 years ago

Summary: Add a dynamic example to spiffy docs

File size: 35.7 KB
Line 
1[[tags: egg]]
2
3== Spiffy
4
5[[toc:]]
6
7=== Description
8
9A small web-server written in [[http://www.call-cc.org|Chicken]].
10
11=== Author
12
13[[/users/felix-winkelmann|Felix Winkelmann]].
14Currently maintained by [[/users/peter-bex|Peter Bex]].
15
16=== Requirements
17
18Requires the [[intarweb]], [[uri-common]], [[uri-generic]] and [[sendfile]] extensions.
19
20=== Documentation
21
22Spiffy is a web-server library for the Chicken Scheme system. It's
23quite easy to set up and use (whether as a library or a standalone
24server application) and it can be customized in numerous ways.
25
26To test it out immediately, try the following command after installing:
27
28  $ csi -e "(use spiffy) (start-server)"
29
30This starts up a spiffy server which listens on port 8080, and serves
31documents from the default docroot, which is a directory called {{web}}
32underneath the current working directory.
33
34A typical httpd would be a more complete program which first
35configures a few things like the {{port-number}} and {{root-path}}
36parameters and set up some logging through {{access-log}} and
37{{error-log}}.  It could also configure some extensions (like
38{{simple-directory-handler}}) and register some user-defined path
39handlers by adding them to {{vhost-map}}.  Finally it could daemonize by
40forking and exiting.  The mentioned parameters are explained below.
41
42=== Starting the server
43
44<procedure>(start-server [port: port-number] [bind-address: address] [listen: listen-procedure] [accept: accept-procedure] [addresses: addresses-procedure])</procedure>
45
46This is the most convenient way to get a server up and running quickly.
47It starts the server to listen on the given port. Other configuration
48can be tweaked through SRFI-39 parameters. These are listed below.
49Once the server is started, server behaviour can be controlled through
50these parameters as well.  After the listener is started, when
51{{spiffy-user}} and/or {{spiffy-group}} are provided, this procedure
52will drop privileges before starting the accept loop.
53
54By default, Spiffy will only serve static files. On directories, it
55will give a "403 forbidden", unless there is an index-file. If there
56is, that file's contents will be shown.
57
58All arguments directly supplied to {{start-server}} override the
59configuration parameter values and will be parameterized to reflect
60this new setting.
61
62{{port-number}} defaults to the value of {{server-port}} (see below).
63{{bind-address}} defaults to the value of {{server-bind-address}} (see below).
64{{listen}} defaults to {{tcp-listen}} and should accept a port number, backlog and bind address.
65{{accept}} defaults to {{tcp-accept}}, and is passed on as-is to {{accept-loop}}.
66{{addresses-procedure}} defaults to a procedure which works like {{tcp-addresses}} but can also detect SSL ports and return the addresses of the underlying TCP connection.
67
68<procedure>(accept-loop listener accept [addresses])</procedure>
69
70This procedure starts the loop which accepts incoming connections and
71fires off threads to handle requests on those connections.  You can
72use it if you need more control over the startup process than
73{{start-server}} offers.
74
75The listener object should be an object which is accepted by the
76{{accept}} procedure, which should return two values; an input and
77an output port which represent an incoming connection from a client.
78The optional {{addresses}} procedure should accept the input port
79returned by the {{accept}} procedure and return two strings; the
80local and remote addresses of the server and client, respectively.
81
82For example, you can set up an SSL context and drop privileges,
83and possibly load extra code before starting the accept loop
84(Spiffy contains the required code to detect SSL ports,
85and will handle those more-or-less transparently):
86
87<enscript highlight="scheme">
88(use chicken-syntax) ; This must done at the very toplevel so that it is available in the interaction-environment.
89(use spiffy openssl)
90
91(server-port 443)
92(spiffy-user "www")
93(spiffy-group "www")
94
95;; Bind the port as root, before we drop privileges
96(define listener (ssl-listen (server-port)))
97
98;; Load the certificate files as root so we can secure their permissions
99(ssl-load-certificate-chain! listener "server.pem")
100(ssl-load-private-key! listener "server.key")
101
102;; Drop root privileges
103(switch-user/group (spiffy-user) (spiffy-group))
104 
105;; We don't want to load this extra code as root!
106(load "extra-code.scm")
107
108;; Done! Start listening for connections.
109(accept-loop listener ssl-accept)
110</enscript>
111
112<procedure>(switch-user/group user group)</procedure>
113
114This is a helper procedure which allows you to easily drop privileges
115before running the accept loop.  The user and group must be either
116strings or UID/GID numbers which indicate the username and groupname
117to which you want to switch.  Either is also allowed to be {{#f}}, if
118you don't want to switch that aspect of the process.
119
120=== Configuration parameters
121
122The following parameters can be used to control spiffy's behaviour.
123Besides these parameters, you can also influence spiffy's behaviour by
124tweaking the [[intarweb]] parameters.
125
126<parameter>(server-software [product])</parameter>
127
128The server software product description. This should be a valid
129product value as used in the server and user-agent headers by intarweb;
130this is a list of lists. The inner lists contain the product name,
131the product version and a comment, all either a string or {{#f}}.
132Default: {{(("Spiffy" "a.b" "Running on Chicken x.y"))}}, with {{a.b}}
133being the Spiffy major/minor version and {{x.y}} being Chicken's.
134
135<parameter>(root-path [path])</parameter>
136
137The path to the document root, for the current vhost.
138Defaults to {{"./web"}}.
139
140<parameter>(server-port [port-number])</parameter>
141
142The port number on which to listen. Defaults to 8080.
143
144<parameter>(server-bind-address [address])</parameter>
145
146The IP address on which to listen, or all addresses if {{#f}}.
147Defaults to {{#f}}.
148
149<parameter>(max-connections [number])</parameter>
150
151The maximum number of simultaneously active connections. Defaults to 1024.
152
153Any new connection that comes in when this number is reached must wait
154until one of the active connections is closed.
155
156<parameter>(spiffy-user [name-or-uid])</parameter>
157
158The name or UID of a user to switch to just after binding the
159port. This only works if you start Spiffy as root, so it can bind port
16080 and then drop privileges. If {{#f}}, no switch will occur.
161Defaults to {{#f}}.
162
163<parameter>(spiffy-group [name-or-gid])</parameter>
164
165The name or GID of a group to switch to just after binding the
166port. This only works if you start Spiffy as root, so it can bind port
16780 and then drop privileges. If {{#f}}, it will be set to the primary
168group of {{spiffy-user}} if the user was selected. Otherwise, no
169change will occur.  Defaults to {{#f}}.
170
171<parameter>(index-files [file-list])</parameter>
172
173A list of filenames which are to be used as index files to serve when
174the requested URL identifies a directory.  Defaults to
175{{'("index.html" "index.xhtml")}}
176
177<parameter>(mime-type-map [extension->mimetype-list])</parameter>
178
179An alist of extensions (strings) to mime-types (symbols), to use
180for the content-type header when serving up a static file. Defaults to
181
182  '(("html" . text/html)
183    ("xhtml" . application/xhtml+xml)
184    ("js"  . application/javascript)
185    ("css" . text/css)
186    ("png" . image/png)
187    ("xml" . application/xml)
188    ("pdf" . application/pdf)
189    ("jpeg" . image/jpeg)
190    ("jpg" . image/jpeg)
191    ("gif" . image/gif)
192    ("ico" . image/vnd.microsoft.icon)
193    ("svg" . image/svg+xml)
194    ("txt" . text/plain))
195
196See also {{file-extension->mime-type}} for a procedure which
197can look up file extensions for you.
198
199<parameter>(default-mime-type [mime-type])</parameter>
200
201The mime-type (a symbol) to use if none was found in the
202{{mime-type-map}}. Defaults to {{'application/octet-stream}}
203
204<parameter>(default-host [hostname])</parameter>
205
206The host name to use when no virtual host could be determined from the
207request.  See the section on virtual hosts below.
208
209<parameter>(vhost-map [host-regex->vhost-handler])</parameter>
210
211A mapping of virtual hosts (regex) to handlers (procedures of one
212argument; a continuation thunk). See the section on virtual hosts
213below. Defaults to {{`((".*" . ,(lambda (continue) (continue))))}}
214
215<parameter>(file-extension-handlers [extension->handler-list])</parameter>
216
217An alist mapping file extensions (strings) to handler procedures
218(lambdas of one argument; the file name relative to the webroot).
219Defaults to {{'()}}. If no handler was found, defaults to just sending
220a static file.
221
222<parameter>(access-log [log-file-or-port])</parameter>
223
224Filename (string) or port to append access log output to.  Default:
225{{#f}} (disabled)
226
227<parameter>(error-log [log-file-or-port])</parameter>
228
229Filename (string) or port to which error messages from evaluated code
230should be output. Default: {{(current-error-port)}}
231
232<parameter>(debug-log [log-file-or-port])</parameter>
233
234Filename (string) or port to write debugging messages to.  Default:
235{{#f}} (disabled)
236
237<parameter>(access-file [string])</parameter>
238
239The name of an access file, or {{#f}} if not applicable.  This file is
240read when the directory is entered by the directory traversal system,
241and allows you to write dynamic handlers that can assign new values
242for parameters only for resources below that directory, very much like
243adding parameters in code before calling a procedure.  See the section
244"Access files" for more information.
245
246<parameter>(trusted-proxies [list-of-strings])</parameter>
247
248When an incoming request is first accepted, the {{remote-address}} is
249initialized to the IP address of the remote peer.  When this peer is a
250reverse proxy in an internal network, that value is not so useful
251because all requests would seem to come from there.
252
253If you want to have a more meaningful value, you can add the IP
254addresses of proxies to this list, and {{X-Forwarded-For}} entries
255from these proxies will be stripped, and the first entry just before
256the most-distant trusted proxy will be used.
257
258Be careful: all IP addresses in this list will be trusted on their
259word.
260
261Default: {{()}} (trust no one)
262
263
264=== Handlers
265
266Besides "static" configuration, Spiffy also has several handlers for
267when something is to be served.
268
269<parameter>(handle-directory [proc])</parameter>
270
271The handler for directory entries. If the requested URL points to a
272directory which has no index file, this handler is invoked. It is a
273procedure of one argument, the path (a string) relative to the
274webroot. Defaults to a procedure which returns a "403 forbidden".
275
276<parameter>(handle-file [proc])</parameter>
277
278The handler for files. If the requested URL points to a file, this
279handler is invoked to serve the file. It is a procedure of one
280argument, the path (a string) relative to the webroot. Defaults to a
281procedure which sets the content-type and determines a handler based
282on the {{file-extension-handlers}}, or {{send-static-file}} if none
283was found and the method was {{HEAD}} or {{GET}} (otherwise it replies
284with 405 "Method Not Allowed").
285
286<parameter>(handle-not-found [proc])</parameter>
287
288The handler for nonexistent files. If the requested URL does not point
289to an existing file or directory, this procedure is called. It is a
290procedure of one argument, the path (a string) to the first missing file
291in the request path.  Defaults to a procedure which returns a
292"404 Not found".
293
294The path path which is passed to the handler is handled in a particular
295way: It contains all the components that exist, and the final component
296is the file or directory which does not exist.  That means this path may
297differ from the concatenated URI path; this can be seen as "pathinfo" for
298the would-be file, if it had existed.
299
300The path should be interpreted as relative to {{root-path}}.
301
302<parameter>(handle-exception [proc])</parameter>
303
304The handler for when an exception occurs. This defaults to a procedure
305that logs the error to the error log. While debugging or developing, it
306may be more convenient to use a procedure that sends the error back to
307the client:
308
309<enscript highlight=scheme>
310(handle-exception
311  (lambda (exn chain)
312    (send-status 'internal-server-error (build-error-message exn chain))))
313</enscript>
314
315<parameter>(handle-access-logging [proc])</parameter>
316
317The handler for access logging. This is a procedure of zero arguments
318which should write a line to the access log. Defaults to a procedure which
319writes a line to {{access-log}} which looks like this:
320
321   127.0.0.1 [Sun Nov 16 15:16:01 2008] "GET http://localhost:8080/foo?bar HTTP/1.1" 200 "http://localhost:8080/referer" "Links (2.2; NetBSD 5.99.01 macppc; x)"
322
323=== Runtime information
324
325During the handling of a request, Spiffy adds more information to the
326environment by parameterizing the following parameters whenever the
327information becomes available:
328
329<parameter>(current-request [request])</parameter>
330
331An intarweb request-object that defines the current request. Available
332from the moment the request comes in and is parsed. Contains, among
333other things, the query parameters and the request-headers, in fully
334parsed form (as intarweb returns them).
335
336The URI is automatically augmented with the host, scheme and port if
337it is not an absolute URI.
338
339<parameter>(current-response [response])</parameter>
340
341An intarweb response-object that defines the current
342response. Available from the same time current-request is available.
343This keeps getting updated along the way, while the response data is
344being refined (like when headers are being added).
345
346<parameter>(current-file [path])</parameter>
347
348The path to the requested file (a string). Available from the moment
349Spiffy determined the requested URL points to a file (just before the
350{{handle-file}} procedure is called). This file is relative to the
351{{root-path}}.
352
353<parameter>(current-pathinfo [path])</parameter>
354
355The trailing path ''fragments'' (a list of strings) that were passed
356in the URL after the requested filename. Available from the moment
357Spiffy determined the requested URL points to a file (just before the
358{{handle-file}} procedure is called).
359
360<parameter>(remote-address [address])</parameter>
361
362The IP address (a string) of the user-agent performing the current
363request.  See also {{trusted-proxies}}.
364
365<parameter>(local-address [address])</parameter>
366
367The IP address (a string) on which the current request came in.
368
369<parameter>(secure-connection? [boolean])</parameter>
370
371{{#t}} when the current connection is a secure one (SSL), {{#f}} if it
372isn't (regular HTTP).  This pertains only to the direct connection
373itself, so if Spiffy is behind a proxy this will be {{#f}} even if the
374proxy itself is connected to the client over SSL.
375
376One way to get around this is to ''always'' add a custom header to
377your reverse proxy's configuration file.  Then you can read this out
378in Spiffy and set secure-connection? to {{#t}} or {{#f}}, as well as
379updating the request URI's scheme.  There is no standardized header
380for this, so the default Spiffy won't do this.
381
382An easier way around this is to set up two spiffies listening on
383different ports and configure one to have {{secure-connection?}} set
384to {{#t}}, which you redirect incoming HTTPS requests to.
385
386'''This parameter may disappear or change in the future''', when there
387are more smart people using Spiffy who know how to deal with this or
388the Spiffy maintainer has a moment of clarity and decides how to do
389this cleanly.
390
391=== Virtual hosts
392
393Spiffy has support for virtual hosting, using the HTTP/1.1 Host
394header.  This allows you to use one Spiffy instance running on one IP
395address/port number to serve multiple webpages, as determined by the
396hostname that was requested.
397
398The virtual host is defined by a procedure, which can set arbitrary
399parameters on-the-fly. It is passed a continuation thunk, which it
400should explicitly call if it wants the processing to continue.  The
401most used parameter in virtual host setups is the {{root-path}}
402parameter, so that another docroot can be selected based on the
403requested hostname, showing different websites for different hosts:
404
405<enscript highlight="scheme">
406(vhost-map `(("foo\\.bar\\.com" .
407               ,(lambda (continue)
408                  ;; Requires the spiffy-dynamic-handlers egg...
409                  (parameterize ((file-extension-handlers
410                                   `(("ssp" . ,ssp-handler) ("ws" . ,web-scheme-handler)))
411                                 (root-path "/var/www/domains/foo.bar.com"))
412                     (continue))))
413             (,(glob->regexp "*.domain.com") .
414                ,(lambda (continue)
415                   ;; Requires the spiffy-cgi-handlers egg...
416                   (parameterize ((file-extension-handlers
417                                    `(("php" . ,(cgi-handler* "/usr/pkg/libexec/cgi-bin/php"))))
418                                  ;; You can also change PHP's arg_separator.input
419                                  ;; to be ";&" instead of this parameter
420                                  (form-urlencoded-separator "&")
421                                  (root-path "/var/www/domains/domain.com"))
422                     (continue))))))
423</enscript>
424
425In this example, if a client accesses
426{{foo.bar.com/mumble/blah.html}}, the file
427{{/var/www/domains/foo.bar.com/mumble/blah.html}} will be served.  Any
428files ending in {{.ssp}} or {{.ws}} will be served by the
429corresponding file type handler.  If there's any PHP file, its source
430will simply be displayed.  In case of
431{{my.domain.com/something/bar.html}}, the file
432{{/var/www/domains/domain.com/something/bar.html}} will be served.  If
433there's a {{.ssp}} or {{.ws}} file there, it will not be interpreted.
434Its source will be displayed instead.  A {{.php}} file, on the other
435hand, will be passed via CGI to the program {{/usr/pkg/libexec/cgi-bin/php}}.
436
437Domain names are mapped to a lambda that can set up any parameters it
438wants to override from the defaults.  The host names are matched using
439{{irregex-match}}.  If the host name is not yet a regexp, it will be
440converted to a ''case-insensitive'' regexp.
441
442=== Access files
443
444Fine-grained access-control can be implemented by using so-called
445access files.  When a request for a specific file is made and a file
446with the name given in the {{access-file}} parameter exists in any
447directory between the {{root-path}} of that vhost and the directory in
448which the file resides, then the access file is loaded as an
449s-expression containing a function and is evaluated with a single
450argument, the function that should be called to continue processing
451the request.
452
453This works just like vhosting.  The function that gets called can call
454{{parameterize}} to set additional constraints on the code that
455handles deeper directories.
456
457For example, if we evaluate {{(access-file ".access")}} before
458starting the server, and we put the following code in a file named
459{{.access}} into the root-directory, then all accesses to any file
460in the root-directory or any subdirectory will be denied unless the
461request comes from localhost:
462
463<enscript highlight=scheme>
464 (lambda (continue)
465   (if (string=? (remote-address) "127.0.0.1")
466       (continue)
467       (send-status 'forbidden "Sorry, you're not allowed here")))
468</enscript>
469
470If we only want to deny access to files that start with an X, put this
471in the {{.access}} file:
472
473<enscript highlight=scheme>
474 (lambda (continue)
475   (let ((old-handler (handle-file)))
476     (parameterize ((handle-file
477                      (lambda (path)
478                        (if (not (string-prefix? "X" (pathname-file path)))
479                            (send-status 'forbidden "No X-files allowed!")
480                            (old-handler path)))))
481       (continue))))
482</enscript>
483
484Of course, access files can be used for much more than just access
485checks.  One can put anything in them that could be put in vhost
486configuration or in top-level configuration.
487
488They are very useful for making deployable web applications, so you
489can just drop a directory on your server which has its own
490configuration embedded in an access file in the root directory of the
491application, without having to edit the server's main configuration
492files.
493
494'''Warning:''' Be very careful when using this.  The full Scheme
495language is available from access files.  That means that a small
496mistake in your web application's file permissions might result in a
497complete breach of your server's security.  This feature will be
498removed in a future version, because it is much too dangerous.
499
500=== Procedures and macros
501
502The following procedures and macros can be used in dynamic web
503programs, or dynamic server configuration:
504
505<procedure>(with-headers new-headers thunk)</procedure>
506
507Call {{thunk}} with the header list {{new-headers}}. This
508parameterizes the current response to contain the new headers.  The
509existing headers are extended with {{new-headers}} through intarweb's
510{{headers}} procedure.
511
512<procedure>(write-logged-response)</procedure>
513
514This procedure simply writes {{current-response}} after calling
515{{handle-access-logging}}. Responses should always go through this
516procedure instead of directly using {{write-response}} from intarweb.
517
518If you have a response body to write, you still need to remember to
519call {{finish-response-body}} (from intarweb) after doing so.
520
521<procedure>(log-to log format . rest)</procedure>
522
523Write a printf-style format string to the specified log (one of
524{{access-log}}, {{error-log}} or {{debug-log}}). {{format}} is a
525{{printf}}-style format string, and rest arguments should match
526the arguments one would pass to printf. A newline is appended to
527the end of the log message automatically.
528
529<procedure>(send-response #!key status code reason body headers)</procedure>
530
531Easy way to send string data to the client, with additional headers.
532It will add appropriate headers and will automatically detect {{HEAD}}
533requests.  If BODY is {{#f}}, no body is sent and the {{content-length}}
534header is set to zero.
535
536The status is a symbol describing the response status, which is looked
537up in [[intarweb]]'s {{http-status-code}} parameter.  If {{code}} and/or
538{{reason}} are supplied, these take precedence.  If {{status}} and {{code}}
539are missing, the default status is {{ok}}.
540
541<procedure>(send-status code reason [message])</procedure><br>
542<procedure>(send-status status [message])</procedure>
543
544Easy way to send a page and a status code to the client.  The optional
545{{message}} is a string containing HTML to add in the body of the
546response. Some structure will be added around the message, so message
547should only be the actual message you want to send.
548
549This can be called either with a numeric code, string reason and
550optional message or with a symbolic status and optional message.
551
552Example:
553
554<enscript highight="scheme">
555(send-status 404 "Not found"
556 "Sorry, page not found! Please try <a href='/search.ws'>our search page</a>")
557
558;; Alternative way of doing this:
559(send-status 'not-found
560 "Sorry, page not found! Please try <a href='/search.ws'>our search page</a>")
561</enscript>
562
563<procedure>(send-static-file filename)</procedure>
564
565Send a file to the client. This sets the {{content-length}} header and
566tries to send the file as quickly as possible to the client. The
567filename is interpreted relative to {{root-path}}.
568
569<procedure>(file-extension->mime-type EXT)</procedure>
570
571Looks up the file extension {{EXT}} (without a leading dot)
572in {{mime-type-map}}, or uses {{default-mime-type}} when the
573extension can't be found.
574
575If {{EXT}} is {{#f}}, it'll look up the extension that is the
576empty string.
577
578This returns a symbol which indicates the mime-type which is matched
579to the extension (for example {{text/html}}).
580
581<procedure>(restart-request request)</procedure>
582
583Restart the entire request-handling starting at the point where the
584request was just parsed. The argument is the new request to use.
585Be careful, this makes it very easy to introduce unwanted endless loops!
586
587<procedure>(htmlize string) => string</procedure>
588
589Encode "special" html symbols like tag and attribute characters so
590they will not be interpreted by the browser.
591
592<procedure>(build-error-message exn chain [raw-output])</procedure>
593
594Build an error message for the exception {{exn}}, with call chain
595{{chain}}. Defaults to HTML output, unless {{raw-output}} is given and
596nonfalse.
597
598=== Modules
599
600This section will describe what the various modules that come with
601Spiffy are and how they work.
602
603==== ssp-handler
604
605This was moved to the [[spiffy-dynamic-handlers]] egg.
606
607==== web-scheme-handler
608
609This was moved to the [[spiffy-dynamic-handlers]] egg.
610
611==== cgi-handler
612
613This was moved to the [[spiffy-cgi-handlers]] egg.
614
615==== simple-directory-handler
616
617In order to get directory listings, you can use
618{{simple-directory-handler}}.  Just parameterize {{handle-directory}}'s
619value with the {{simple-directory-handler}} procedure and you're set.
620
621This directory handler truly is very simple and limited in what you
622can customize.  For a more flexible directory handler, see the
623[[spiffy-directory-listing]] egg.
624
625===== Configuration
626
627The simple directory handler has a few configuration options:
628
629<procedure>(simple-directory-dotfiles? [dotfiles?])</procedure>
630
631Determines if dotfiles should show up in the directory listings.
632Default: {{#f}}
633
634<procedure>(simple-directory-display-file [displayer])</procedure>
635
636A lambda that accepts three arguments: the remote filename, the local
637filename and a boolean that says if the file is a directory.  This
638lambda should output a table row with the desired information.
639Defaults to a lambda that prints the name, size and date when the file
640was last modified.
641
642===== Procedures
643
644The simple-directory handler adds only one procedure to the environment:
645
646<procedure>(simple-directory-handler pathname)</procedure>
647
648The handler itself, which should be used in the {{handle-directory}}
649parameter.
650
651=== Examples
652
653==== Quick config for serving up a docroot
654
655Spiffy is very easy to use for simple cases:
656
657<enscript highlight=scheme>
658(use spiffy)
659
660(server-port 80)
661(root-path "/var/www")
662;; When dropping privileges, switch to this user
663(spiffy-user "httpd")
664(spiffy-group "httpd")
665(start-server)
666</enscript>
667
668One could also use parameterize (according to taste):
669
670<enscript highlight=scheme>
671(use spiffy)
672
673(parameterize ((server-port 80)
674               (spiffy-user "httpd")
675               (spiffy-group "httpd")
676               (root-path "/var/www"))
677  (start-server))
678</enscript>
679
680If you put this in {{/usr/local/libexec/spiffy.scm}} you can use [[/spiffy-init-script|this example init.d script]] or [[/spiffy-systemd-scripts|these example systemd scripts]] as-is, to launch Spiffy.
681
682==== A simple dynamic web page example
683
684If you would like to dynamically serve content, you can use a custom
685handler in the vhost-map for the host on which you want to serve
686this content:
687
688<enscript highlight="scheme">
689(use spiffy intarweb uri-common)
690
691(define (handle-greeting continue)
692  (let* ((uri (request-uri (current-request))))
693    (if (equal? (uri-path uri) '(/ "greeting"))
694        (send-response status: 'ok body: "<h1>Hello!</h1>")
695        (continue))))
696
697(vhost-map `(("localhost" . ,handle-greeting)))
698
699(start-server)
700</enscript>
701
702It's not advised to use string concatenation for building HTML, as it
703is rather unsafe and easy to get wrong.  For a slightly larger
704example, see
705[[http://bugs.call-cc.org/browser/project/demonstrations/spiffy|this
706demonstration of Spiffy with SXML]].
707
708
709==== Network tweaks
710
711Spiffy does not activate Chicken's TCP buffering, which results in
712extra traffic: one packet sent per header line.  With a TCP buffer
713size greater than the total header length, all headers will be
714coalesced into a single write; generally the response body will be
715coalesced as well.  For example:
716
717 (tcp-buffer-size 2048)   ; from unit tcp
718 (start-server)
719
720==== Redirecting to another domain
721
722When you have a domain you want to canonicalize so that it will always
723have {{www}} in front of it, you can set up a conditional redirect in
724your {{vhosts}} section:
725
726<enscript highlight=scheme>
727(use spiffy intarweb uri-common)
728
729(vhost-map `(("example.com"
730               . ,(lambda (continue)
731                    (let* ((old-u (request-uri (current-request)))
732                           (new-u (update-uri old-u host: "www.example.com")))
733                      (with-headers `((location ,new-u))
734                        (lambda () (send-status 'moved-permanently))))))
735             ("www.example.com" . ,(lambda (continue) (continue)))))
736
737(start-server)
738</enscript>
739
740Alternatively the following version can be used to generate appropriate handlers for several domains:
741
742<enscript highlight=scheme>
743(use spiffy intarweb uri-common)
744
745; Generates a handler that can be used in vhost-map that will cause all requests to that URL to be rewritten to the domain specified in 'to'.
746(define (canonicalise-domain to)
747  (let ((to (uri-reference to)))
748   (assert (equal? '(/ "") (uri-path to)))
749   (assert (null? (uri-query to)))
750   ; We don't see fragments on the server and choose not to care about usernames and password fields.
751    (lambda (continue)
752      (let* ((old-u (request-uri (current-request)))
753             (new-u (update-uri old-u
754                                scheme: (or (uri-scheme to) (uri-scheme old-u))
755                                port:   (or (uri-port   to) (uri-port   old-u))
756                                host:   (or (uri-host   to) (uri-host   old-u)))))
757        (with-headers `((location ,new-u))
758                      (lambda () (send-status 'moved-permanently)))))))
759
760(vhost-map `(("example.com"
761               . ,(canonicalise-domain "http://www.example.com"))
762 ("www.example.com" . ,(lambda (continue) (continue)))))
763</enscript>
764
765The above code hooks a handler for the regular expression {{"example.com"}},
766checks the request's URI and then updates only the host component, keeping
767all other components (like port and path) intact.  Then it sends a simple
768status response that indicates a 301 "Moved Permanently" response, using
769the new URI in the {{Location:}} header.
770
771The second entry just tells Spiffy to continue serving the request
772when it's made to {{www.example.com}}.  A request on any other host
773will receive a 404 not found response due to not having a vhost entry.
774More elaborate setups can parameterize some other aspects for each host
775before calling {{continue}}.
776
777=== Changelog
778
779* 5.4.1: Once again allow backslashes in paths on UNIX-like platforms in CHICKENs where {{make-pathname}} was fixed.  Improve request handling performance.  Remove over-reliance on undocumented {{alist-ref}} comparison procedure's argument order (which will be changed in CHICKEN 4.11).
780* 5.4: Fix critical security vulnerability (path traversal, CVE-2015-8235) by disallowing backslashes in request paths (thanks to Benedikt Rosenau for reporting the vulnerability).  Use {{spiffy}} in the gensym'ed name of spiffy's threads, for easier debugging.  Thanks to Evan Hanson.
781* 5.3.2 Fix tests so they still function correctly when {{SPIFFY_TEST_PORT}} is overridden.  Thanks to Kon Lovett for pointing this out.
782* 5.3.1 Don't try to handle another request when the input or output ports are closed (thanks to Thomas Hintz.  This makes [[/egg/websockets|websocket]] support work properly!)
783* 5.3 Improved presentation and correctness of HTML in simple-directory-handler (thanks to Roel van der Hoorn).  Fixed serving of static files on Windows due to missing {{#:binary}} option (thanks to "Jubjub").
784* 5.2.1 Handle disconnections more gracefully (as per intarweb 1.2), and improved debugging some more..
785* 5.2 Log prematurely broken connections to the debug-log, to aid in... debugging (thanks to Andy Bennett). Fix locking bug in tests, and disable the "unreadable file is 403" check when run as root.
786* 5.1 Fix static file handling of files which cannot be read by Spiffy (thanks to Andy Bennett). Use intarweb's new {{finish-response-body}} procedure where needed. Add image/svg+xml to the default mime-type lists.
787* 5.0 Work around race conditions in {{simple-directory-handler}} related to deleted files or self-referential symlinks (reported by [[/users/mario-domenech-goulart|Mario]]). Move deprecated dynamic content handlers into separate [[spiffy-dynamic-handlers]] egg. Move cgi handler into separate [[spiffy-cgi-handlers]] egg.
788* 4.15 Disallow non-{{GET}}/{{HEAD}} requests for normal files in {{handle-file}}.  Remove dependencies on [[/eggref/4/matchable|matchable]] and [[/eggref/4/regex|regex]] eggs.  Allow request-uri overrides in user lambdas to affect the default handler (Thanks to [[/users/mario-domenech-goulart|Mario]]).
789* 4.14 Fix ssp-handler's pathname construction so it doesn't error on files in the rootdirectory.  Fix incompatibility with version 1.6.1 of the OpenSSL egg [Thanks to [[/users/mario-domenech-goulart|Mario]] and [[/users/thomas-chust|Thomas]].]
790* 4.13 Add trusted-proxies parameter so remote-address can have a meaningful value in the presence of reverse proxies [thanks to John J Foerch]. Allow cgi scripts to be outside the docroot. Pass on query-strings to CGI scripts as-is, if possible, to avoid breaking scripts that rely on a particular variable-separator (reported by Felix Winkelmann).  Refactor to use symbolic response codes from Intarweb.
791* 4.12 Make Spiffy optionally fully independent of Unit tcp [thanks to [[/users/jim-ursetto|Jim Ursetto]]]  Tests now also work with Chickens greater than 4.6.0 (removed deprecated {{getenv}} calls)
792* 4.11 Drop openssl requirement (suggested by Felix Winkelmann); split up listening and accept loop invocation to allow loading code after dropping root privileges (suggested by Mario Goulart).
793* 4.10 Fix serious bug which caused Spiffy to exit after handling N requests. Fix handling of empty path components now that uri-generic preserves them.
794* 4.9 Export {{file-extension->mime-type}}. Add more info about exceptional situations to the debugging logs when enabled.
795* 4.8 Fix ssl support. Get rid of {{spiffy-root-uri}}. Add timestamp and request info to error log.
796* 4.7 Fix redirects for directories again (thanks to [[/users/mario-domenech-goulart|Mario]]). NO TESTCASE (possibly to be fixed in [[uri-generic]])
797* 4.6 Fix redirects for directories with special URI characters in them (thanks to [[/users/felix-winkelmann|Felix]])
798* 4.5 Add {{send-response}} procedure; flush output after request is handled; use proper IANA-assigned MIME types in default {{mime-type-map}}; fix {{server-root-uri}} when spiffy is behind a proxy [thanks to zbigniew]
799* 4.4 Fix a problem with 304 "not modified", for which Safari incorrectly tries to read a content-body when a content-length is present.
800* 4.3 Fix crash with extensionless files and nonempty extension handlers
801* 4.2 Add support for caching headers and proper If-Modified-Since support
802* 4.1 Make cgi-handler cope with missing pathinfo. Fix statuscode related crash in cgi-handler. Make ssp handler's open/close tags parameters, so it actually matches what the documentation says. Add {{ssp-cache-dir}}. Update for latest intarweb changes (0.2)
803* 4.0 Rewrite from scratch, using Intarweb
804* pre-4.0 See the changelog for the [[/eggref/3/spiffy|old spiffy]]
805
806=== License
807
808  Copyright (c) 2005-2016, Felix L. Winkelmann and Peter Bex
809  All rights reserved.
810 
811  Redistribution and use in source and binary forms, with or without
812  modification, are permitted provided that the following conditions are
813  met:
814 
815  Redistributions of source code must retain the above copyright
816  notice, this list of conditions and the following disclaimer.
817 
818  Redistributions in binary form must reproduce the above copyright
819  notice, this list of conditions and the following disclaimer in the
820  documentation and/or other materials provided with the distribution.
821 
822  Neither the name of the author nor the names of its contributors may
823  be used to endorse or promote products derived from this software
824  without specific prior written permission.
825 
826  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
827  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
828  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
829  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
830  COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
831  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
832  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
833  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
834  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
835  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
836  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
837  OF THE POSSIBILITY OF SUCH DAMAGE.
Note: See TracBrowser for help on using the repository browser.