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

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

Update spiffy changelog

File size: 34.9 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==== Network tweaks
683
684Spiffy does not activate Chicken's TCP buffering, which results in
685extra traffic: one packet sent per header line.  With a TCP buffer
686size greater than the total header length, all headers will be
687coalesced into a single write; generally the response body will be
688coalesced as well.  For example:
689
690 (tcp-buffer-size 2048)   ; from unit tcp
691 (start-server)
692
693==== Redirecting to another domain
694
695When you have a domain you want to canonicalize so that it will always
696have {{www}} in front of it, you can set up a conditional redirect in
697your {{vhosts}} section:
698
699<enscript highlight=scheme>
700(use spiffy intarweb uri-common)
701
702(vhost-map `(("example.com"
703               . ,(lambda (continue)
704                    (let* ((old-u (request-uri (current-request)))
705                           (new-u (update-uri old-u host: "www.example.com")))
706                      (with-headers `((location ,new-u))
707                        (lambda () (send-status 'moved-permanently))))))
708             ("www.example.com" . ,(lambda (continue) (continue)))))
709
710(start-server)
711</enscript>
712
713Alternatively the following version can be used to generate appropriate handlers for several domains:
714
715<enscript highlight=scheme>
716(use spiffy intarweb uri-common)
717
718; 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'.
719(define (canonicalise-domain to)
720  (let ((to (uri-reference to)))
721   (assert (equal? '(/ "") (uri-path to)))
722   (assert (null? (uri-query to)))
723   ; We don't see fragments on the server and choose not to care about usernames and password fields.
724    (lambda (continue)
725      (let* ((old-u (request-uri (current-request)))
726             (new-u (update-uri old-u
727                                scheme: (or (uri-scheme to) (uri-scheme old-u))
728                                port:   (or (uri-port   to) (uri-port   old-u))
729                                host:   (or (uri-host   to) (uri-host   old-u)))))
730        (with-headers `((location ,new-u))
731                      (lambda () (send-status 'moved-permanently)))))))
732
733(vhost-map `(("example.com"
734               . ,(canonicalise-domain "http://www.example.com"))
735 ("www.example.com" . ,(lambda (continue) (continue)))))
736</enscript>
737
738The above code hooks a handler for the regular expression {{"example.com"}},
739checks the request's URI and then updates only the host component, keeping
740all other components (like port and path) intact.  Then it sends a simple
741status response that indicates a 301 "Moved Permanently" response, using
742the new URI in the {{Location:}} header.
743
744The second entry just tells Spiffy to continue serving the request
745when it's made to {{www.example.com}}.  A request on any other host
746will receive a 404 not found response due to not having a vhost entry.
747More elaborate setups can parameterize some other aspects for each host
748before calling {{continue}}.
749
750=== Changelog
751
752* 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).
753* 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.
754* 5.3.2 Fix tests so they still function correctly when {{SPIFFY_TEST_PORT}} is overridden.  Thanks to Kon Lovett for pointing this out.
755* 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!)
756* 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").
757* 5.2.1 Handle disconnections more gracefully (as per intarweb 1.2), and improved debugging some more..
758* 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.
759* 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.
760* 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.
761* 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]]).
762* 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]].]
763* 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.
764* 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)
765* 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).
766* 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.
767* 4.9 Export {{file-extension->mime-type}}. Add more info about exceptional situations to the debugging logs when enabled.
768* 4.8 Fix ssl support. Get rid of {{spiffy-root-uri}}. Add timestamp and request info to error log.
769* 4.7 Fix redirects for directories again (thanks to [[/users/mario-domenech-goulart|Mario]]). NO TESTCASE (possibly to be fixed in [[uri-generic]])
770* 4.6 Fix redirects for directories with special URI characters in them (thanks to [[/users/felix-winkelmann|Felix]])
771* 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]
772* 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.
773* 4.3 Fix crash with extensionless files and nonempty extension handlers
774* 4.2 Add support for caching headers and proper If-Modified-Since support
775* 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)
776* 4.0 Rewrite from scratch, using Intarweb
777* pre-4.0 See the changelog for the [[/eggref/3/spiffy|old spiffy]]
778
779=== License
780
781  Copyright (c) 2005-2016, Felix L. Winkelmann and Peter Bex
782  All rights reserved.
783 
784  Redistribution and use in source and binary forms, with or without
785  modification, are permitted provided that the following conditions are
786  met:
787 
788  Redistributions of source code must retain the above copyright
789  notice, this list of conditions and the following disclaimer.
790 
791  Redistributions in binary form must reproduce the above copyright
792  notice, this list of conditions and the following disclaimer in the
793  documentation and/or other materials provided with the distribution.
794 
795  Neither the name of the author nor the names of its contributors may
796  be used to endorse or promote products derived from this software
797  without specific prior written permission.
798 
799  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
800  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
801  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
802  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
803  COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
804  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
805  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
806  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
807  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
808  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
809  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
810  OF THE POSSIBILITY OF SUCH DAMAGE.
Note: See TracBrowser for help on using the repository browser.