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

Last change on this file since 25624 was 25624, checked in by sjamaan, 9 years ago

Update spiffy Changelog

File size: 35.0 KB
Line 
1[[tags: egg]]
2
3== Spiffy
4
5[[toc:]]
6
7=== Description
8
9A small web-server written in [[http://www.call-with-current-continuation.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]], [[defstruct]], [[matchable]]
19and [[sendfile]] extensions.
20
21=== Documentation
22
23Spiffy is a web-server library for the Chicken Scheme system. It's
24quite easy to set up and use (whether as a library or a standalone
25server application) and it can be customized in numerous ways.
26
27=== Starting the server
28
29To start the server with minimal configuration hassle, there's a simple
30procedure to help you do that:
31
32<procedure>(start-server [port: port-number] [bind-address: address] [listen: listen-procedure] [accept: accept-procedure] [addresses: addresses-procedure])</procedure>
33
34Starts the server, to listen on the given port. Other configuration
35can be tweaked through SRFI-39 parameters. These are listed below.
36Once the server is started, server behaviour can be controlled through
37these parameters as well.  After the listener is started, when
38{{spiffy-user}} and/or {{spiffy-group}} are provided, this procedure
39will drop privileges before starting the accept loop.
40
41By default, Spiffy will only serve static files. On directories, it
42will give a "403 forbidden", unless there is an index-file. If there
43is, that file's contents will be shown.
44
45All arguments directly supplied to {{start-server}} override the
46configuration parameter values and will be parameterized to reflect
47this new setting.
48
49{{port-number}} defaults to the value of {{server-port}} (see below).
50{{bind-address}} defaults to the value of {{server-bind-address}} (see below).
51{{listen}} defaults to {{tcp-listen}} and should accept a port number, backlog and bind address.
52{{accept}} defaults to {{tcp-accept}}, and is passed on as-is to {{accept-loop}}.
53{{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.
54
55<procedure>(accept-loop listener accept [addresses])</procedure>
56
57This procedure starts the loop which accepts incoming connections and
58fires off threads to handle requests on those connections.  You can
59use it if you need more control over the startup process than
60{{start-server}} offers.
61
62The listener object should be an object which is accepted by the
63{{accept}} procedure, which should return two values; an input and
64an output port which represent an incoming connection from a client.
65The optional {{addresses}} procedure should accept the input port
66returned by the {{accept}} procedure and return two strings; the
67local and remote addresses of the server and client, respectively.
68
69For example, you can set up an SSL context and drop privileges,
70and possibly load extra code before starting the accept loop
71(Spiffy contains the required code to detect SSL ports,
72and will handle those more-or-less transparently):
73
74<enscript highlight="scheme">
75(use spiffy openssl)
76
77(server-port 443)
78(spiffy-user "www")
79(spiffy-group "www")
80
81;; Bind the port as root, before we drop privileges
82(define listener (ssl-listen (server-port)))
83
84;; Load the certificate files as root so we can secure their permissions
85(ssl-load-certificate-chain! listener "server.pem")
86(ssl-load-private-key! listener "server.key")
87
88;; Drop root privileges
89(switch-user/group (spiffy-user) (spiffy-group))
90 
91;; We don't want to load this extra code as root!
92(load "extra-code.scm")
93
94;; Done! Start listening for connections.
95(accept-loop listener ssl-accept)
96</enscript>
97
98<procedure>(switch-user/group user group)</procedure>
99
100This is a helper procedure which allows you to easily drop privileges
101before running the accept loop.  The user and group must be either
102strings or UID/GID numbers which indicate the username and groupname
103to which you want to switch.  Either is also allowed to be {{#f}}, if
104you don't want to switch that aspect of the process.
105
106=== Configuration parameters
107
108The following parameters can be used to control spiffy's behaviour.
109Besides these parameters, you can also influence spiffy's behaviour by
110tweaking the [[intarweb]] parameters.
111
112<parameter>(server-software [product])</parameter>
113
114The server software product description. This should be a valid
115product value as used in the server and user-agent headers by intarweb;
116this is a list of lists. The inner lists contain the product name,
117the product version and a comment, all either a string or {{#f}}.
118Default: {{(("Spiffy" "a.b" "Running on Chicken x.y"))}}, with {{a.b}}
119being the Spiffy major/minor version and {{x.y}} being Chicken's.
120
121<parameter>(root-path [path])</parameter>
122
123The path to the document root, for the current vhost.
124Defaults to {{"./web"}}.
125
126<parameter>(server-port [port-number])</parameter>
127
128The port number on which to listen. Defaults to 8080.
129
130<parameter>(server-bind-address [address])</parameter>
131
132The IP address on which to listen, or all addresses if {{#f}}.
133Defaults to {{#f}}.
134
135<parameter>(max-connections [number])</parameter>
136
137The maximum number of simultaneously active connections. Defaults to 1024.
138
139Any new connection that comes in when this number is reached must wait
140until one of the active connections is closed.
141
142<parameter>(spiffy-user [name-or-uid])</parameter>
143
144The name or UID of a user to switch to just after binding the
145port. This only works if you start Spiffy as root, so it can bind port
14680 and then drop privileges. If {{#f}}, no switch will occur.
147Defaults to {{#f}}.
148
149<parameter>(spiffy-group [name-or-gid])</parameter>
150
151The name or GID of a group to switch to just after binding the
152port. This only works if you start Spiffy as root, so it can bind port
15380 and then drop privileges. If {{#f}}, it will be set to the primary
154group of {{spiffy-user}} if the user was selected. Otherwise, no
155change will occur.  Defaults to {{#f}}.
156
157<parameter>(index-files [file-list])</parameter>
158
159A list of filenames which are to be used as index files to serve when
160the requested URL identifies a directory.  Defaults to
161{{'("index.html" "index.xhtml")}}
162
163<parameter>(mime-type-map [extension->mimetype-list])</parameter>
164
165An alist of extensions (strings) to mime-types (symbols), to use
166for the content-type header when serving up a static file. Defaults to
167
168  '(("html" . text/html)
169    ("xhtml" . application/xhtml+xml)
170    ("js"  . application/javascript)
171    ("css" . text/css)
172    ("png" . image/png)
173    ("xml" . application/xml)
174    ("pdf" . application/pdf)
175    ("jpeg" . image/jpeg)
176    ("jpg" . image/jpeg)
177    ("gif" . image/gif)
178    ("ico" . image/vnd.microsoft.icon)
179    ("txt" . text/plain))
180
181See also {{file-extension->mime-type}} for a procedure which
182can look up file extensions for you.
183
184<parameter>(default-mime-type [mime-type])</parameter>
185
186The mime-type (a symbol) to use if none was found in the
187{{mime-type-map}}. Defaults to {{'application/octet-stream}}
188
189<parameter>(default-host [hostname])</parameter>
190
191The host name to use when no virtual host could be determined from the
192request.  See the section on virtual hosts below.
193
194<parameter>(vhost-map [host-regex->vhost-handler])</parameter>
195
196A mapping of virtual hosts (regex) to handlers (procedures of one
197argument; a continuation thunk). See the section on virtual hosts
198below. Defaults to {{`((".*" . ,(lambda (continue) (continue))))}}
199
200<parameter>(file-extension-handlers [extension->handler-list])</parameter>
201
202An alist mapping file extensions (strings) to handler procedures
203(lambdas of one argument; the file name relative to the webroot).
204Defaults to {{'()}}. If no handler was found, defaults to just sending
205a static file.
206
207<parameter>(access-log [log-file-or-port])</parameter>
208
209Filename (string) or port to append access log output to.  Default:
210{{#f}} (disabled)
211
212<parameter>(error-log [log-file-or-port])</parameter>
213
214Filename (string) or port to which error messages from evaluated code
215should be output. Default: {{(current-error-port)}}
216
217<parameter>(debug-log [log-file-or-port])</parameter>
218
219Filename (string) or port to write debugging messages to.  Default:
220{{#f}} (disabled)
221
222<parameter>(access-file [string])</parameter>
223
224The name of an access file, or {{#f}} if not applicable.  This file is
225read when the directory is entered by the directory traversal system,
226and allows you to write dynamic handlers that can assign new values
227for parameters only for resources below that directory, very much like
228adding parameters in code before calling a procedure.  See the section
229"Access files" for more information.
230
231<parameter>(trusted-proxies [list-of-strings])</parameter>
232
233When an incoming request is first accepted, the {{remote-address}} is
234initialized to the IP address of the remote peer.  When this peer is a
235reverse proxy in an internal network, that value is not so useful
236because all requests would seem to come from there.
237
238If you want to have a more meaningful value, you can add the IP
239addresses of proxies to this list, and {{X-Forwarded-For}} entries
240from these proxies will be stripped, and the first entry just before
241the most-distant trusted proxy will be used.
242
243Be careful: all IP addresses in this list will be trusted on their
244word.
245
246Default: {{()}} (trust no one)
247
248
249=== Handlers
250
251Besides "static" configuration, Spiffy also has several handlers for
252when something is to be served.
253
254<parameter>(handle-directory [proc])</parameter>
255
256The handler for directory entries. If the requested URL points to a
257directory which has no index file, this handler is invoked. It is a
258procedure of one argument, the path (a string) relative to the
259webroot. Defaults to a procedure which returns a "403 forbidden".
260
261<parameter>(handle-file [proc])</parameter>
262
263The handler for files. If the requested URL points to a file, this
264handler is invoked to serve the file. It is a procedure of one
265argument, the path (a string) relative to the webroot. Defaults to a
266procedure which sets the content-type and determines a handler based
267on the {{file-extension-handlers}}, or {{send-static-file}} if none
268was found.
269
270<parameter>(handle-not-found [proc])</parameter>
271
272The handler for nonexistent files. If the requested URL does not point
273to an existing file or directory, this procedure is called. It is a
274procedure of one argument, the path (a string) to the first missing file
275in the request path. This path should be interpreted as being relative
276to the webroot (even though it points to no existing file). Defaults to
277a procedure which returns a "404 Not found".
278
279<parameter>(handle-exception [proc])</parameter>
280
281The handler for when an exception occurs. This defaults to a procedure
282that logs the error to the error log. While debugging or developing, it
283may be more convenient to use a procedure that sends the error back to
284the client:
285
286<enscript highlight=scheme>
287(handle-exception
288  (lambda (exn chain)
289    (send-status 'internal-server-error (build-error-message exn chain))))
290</enscript>
291
292<parameter>(handle-access-logging [proc])</parameter>
293
294The handler for access logging. This is a procedure of zero arguments
295which should write a line to the access log. Defaults to a procedure which
296writes a line to {{access-log}} which looks like this:
297
298   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)"
299
300=== Runtime information
301
302During the handling of a request, Spiffy adds more information to the
303environment by parameterizing the following parameters whenever the
304information becomes available:
305
306<parameter>(current-request [request])</parameter>
307
308An intarweb request-object that defines the current request. Available
309from the moment the request comes in and is parsed. Contains, among
310other things, the query parameters and the request-headers, in fully
311parsed form (as intarweb returns them).
312
313The URI is automatically augmented with the host, scheme and port if
314it is not an absolute URI.
315
316<parameter>(current-response [response])</parameter>
317
318An intarweb response-object that defines the current
319response. Available from the same time current-request is available.
320This keeps getting updated along the way, while the response data is
321being refined (like when headers are being added).
322
323<parameter>(current-file [path])</parameter>
324
325The path to the requested file (a string). Available from the moment
326Spiffy determined the requested URL points to a file (just before the
327{{handle-file}} procedure is called). This file is relative to the
328{{root-path}}.
329
330<parameter>(current-pathinfo [path])</parameter>
331
332The trailing path ''fragments'' (a list of strings) that were passed
333in the URL after the requested filename. Available from the moment
334Spiffy determined the requested URL points to a file (just before the
335{{handle-file}} procedure is called).
336
337<parameter>(remote-address [address])</parameter>
338
339The IP address (a string) of the user-agent performing the current
340request.  See also {{trusted-proxies}}.
341
342<parameter>(local-address [address])</parameter>
343
344The IP address (a string) on which the current request came in.
345
346<parameter>(secure-connection? [boolean])</parameter>
347
348{{#t}} when the current connection is a secure one (SSL), {{#f}} if it
349isn't (regular HTTP).  This pertains only to the direct connection
350itself, so if Spiffy is behind a proxy this will be {{#f}} even if the
351proxy itself is connected to the client over SSL.
352
353One way to get around this is to ''always'' add a custom header to
354your reverse proxy's configuration file.  Then you can read this out
355in Spiffy and set secure-connection? to {{#t}} or {{#f}}, as well as
356updating the request URI's scheme.  There is no standardized header
357for this, so the default Spiffy won't do this.
358
359An easier way around this is to set up two spiffies listening on
360different ports and configure one to have {{secure-connection?}} set
361to {{#t}}, which you redirect incoming HTTPS requests to.
362
363'''This parameter may disappear or change in the future''', when there
364are more smart people using Spiffy who know how to deal with this or
365the Spiffy maintainer has a moment of clarity and decides how to do
366this cleanly.
367
368=== Virtual hosts
369
370Spiffy has support for virtual hosting, using the HTTP/1.1 Host
371header.  This allows you to use one Spiffy instance running on one IP
372address/port number to serve multiple webpages, as determined by the
373hostname that was requested.
374
375The virtual host is defined by a procedure, which can set arbitrary
376parameters on-the-fly. It is passed a continuation thunk, which it
377should explicitly call if it wants the processing to continue.  The
378most used parameter in virtual host setups is the {{root-path}}
379parameter, so that another docroot can be selected based on the
380requested hostname, showing different websites for different hosts:
381
382<enscript highlight="scheme">
383(vhost-map `(("foo\\.bar\\.com" .
384               ,(lambda (continue)
385                  (parameterize ((file-extension-handlers
386                                   `(("ssp" . ,ssp-handler) ("ws" . ,web-scheme-handler)))
387                                 (root-path "/var/www/domains/foo.bar.com"))
388                     (continue))))
389             (,(glob->regexp "*.domain.com") .
390                ,(lambda (continue)
391                   (parameterize ((file-extension-handlers
392                                    `(("php" . ,(cgi-handler* "/usr/pkg/libexec/cgi-bin/php"))))
393                                  ;; You can also change PHP's arg_separator.input
394                                  ;; to be ";&" instead of this parameter
395                                  (form-urlencoded-separator "&")
396                                  (root-path "/var/www/domains/domain.com"))
397                     (continue))))))
398</enscript>
399
400In this example, if a client accesses
401{{foo.bar.com/mumble/blah.html}}, the file
402{{/var/www/domains/foo.bar.com/mumble/blah.html}} will be served.  Any
403files ending in {{.ssp}} or {{.ws}} will be served by the
404corresponding file type handler.  If there's any PHP file, its source
405will simply be displayed.  In case of
406{{my.domain.com/something/bar.html}}, the file
407{{/var/www/domains/domain.com/something/bar.html}} will be served.  If
408there's a {{.ssp}} or {{.ws}} file there, it will not be interpreted.
409Its source will be displayed instead.  A {{.php}} file, on the other
410hand, will be passed via CGI to the program {{/usr/pkg/bin/php}}.
411
412Domain names are mapped to a lambda that sets up any parameters it
413wants to override from the defaults.  The host names are matched using
414{{string-match}}.  If the host name is not yet a regexp, it will be
415converted to a ''case-insensitive'' regexp.
416
417=== Access files
418
419Fine-grained access-control can be implemented by using so-called
420access files.  When a request for a specific file is made and a file
421with the name given in the {{access-file}} parameter exists in any
422directory between the {{root-path}} of that vhost and the directory in
423which the file resides, then the access file is loaded as an
424s-expression containing a function and is evaluated with a single
425argument, the function that should be called to continue processing
426the request.
427
428This works just like vhosting.  The function that gets called can call
429{{parameterize}} to set additional constraints on the code that
430handles deeper directories.
431
432For example, if we evaluate {{(access-file ".access")}} before
433starting the server, and we put the following code in a file named
434{{.access}} into the root-directory, then all accesses to any file
435in the root-directory or any subdirectory will be denied unless the
436request comes from localhost:
437
438<enscript highlight=scheme>
439 (lambda (continue)
440   (if (string=? (remote-address) "127.0.0.1")
441       (continue)
442       (send-status 'forbidden "Sorry, you're not allowed here")))
443</enscript>
444
445If we only want to deny access to files that start with an X, put this
446in the {{.access}} file:
447
448<enscript highlight=scheme>
449 (lambda (continue)
450   (let ((old-handler (handle-file)))
451     (parameterize ((handle-file
452                      (lambda (path)
453                        (if (not (string-prefix? "X" (pathname-file path)))
454                            (send-status 'forbidden "No X-files allowed!")
455                            (old-handler path)))))
456       (continue))))
457</enscript>
458
459Of course, access files can be used for much more than just access
460checks.  One can put anything in them that could be put in vhost
461configuration or in top-level configuration.
462
463They are very useful for making deployable web applications, so you
464can just drop a directory on your server which has its own
465configuration embedded in an access file in the root directory of the
466application, without having to edit the server's main configuration
467files.
468
469=== Procedures and macros
470
471The following procedures and macros can be used in dynamic web
472programs, or dynamic server configuration:
473
474<procedure>(with-headers new-headers thunk)</procedure>
475
476Call {{thunk}} with the header list {{new-headers}}. This
477parameterizes the current response to contain the new headers.  The
478existing headers are extended with {{new-headers}} through intarweb's
479{{headers}} procedure.
480
481<procedure>(write-logged-response)</procedure>
482
483This procedure simply writes {{current-response}} after calling
484{{handle-access-logging}}. Responses should always go through this
485procedure instead of directly using {{write-response}} from intarweb.
486
487<procedure>(log-to log format . rest)</procedure>
488
489Write a printf-style format string to the specified log (one of
490{{access-log}}, {{error-log}} or {{debug-log}}). {{format}} is a
491{{printf}}-style format string, and rest arguments should match
492the arguments one would pass to printf. A newline is appended to
493the end of the log message automatically.
494
495<procedure>(send-response #!key status code reason body headers)</procedure>
496
497Easy way to send string data to the client, with additional headers.
498It will add appropriate headers and will automatically detect {{HEAD}}
499requests.  If BODY is {{#f}}, no body is sent and the {{content-length}}
500header is set to zero.
501
502The status is a symbol describing the response status, which is looked
503up in [[intarweb]]'s {{http-status-code}} parameter.  If {{code}} and/or
504{{reason}} are supplied, these take precedence.  If {{status}} and {{code}}
505are missing, the default status is {{ok}}.
506
507<procedure>(send-status code reason [message])</procedure><br>
508<procedure>(send-status status [message])</procedure>
509
510Easy way to send a page and a status code to the client.  The optional
511{{message}} is a string containing HTML to add in the body of the
512response. Some structure will be added around the message, so message
513should only be the actual message you want to send.
514
515This can be called either with a numeric code, string reason and
516optional message or with a symbolic status and optional message.
517
518Example:
519
520<enscript highight="scheme">
521(send-status 404 "Not found"
522 "Sorry, page not found! Please try <a href='/search.ws'>our search page</a>")
523
524;; Alternative way of doing this:
525(send-status 'not-found
526 "Sorry, page not found! Please try <a href='/search.ws'>our search page</a>")
527</enscript>
528
529<procedure>(send-static-file filename)</procedure>
530
531Send a file to the client. This sets the {{content-length}} header and
532tries to send the file as quickly as possible to the client. The
533filename is interpreted relative to {{root-path}}.
534
535<procedure>(file-extension->mime-type EXT)</procedure>
536
537Looks up the file extension {{EXT}} (without a trailing dot)
538in {{mime-type-map}}, or uses {{default-mime-type}} when the
539extension can't be found.
540
541If {{EXT}} is {{#f}}, it'll look up the extension that is the
542empty string.
543
544This returns a symbol which indicates the mime-type which is matched
545to the extension (for example {{text/html}}).
546
547<procedure>(restart-request request)</procedure>
548
549Restart the entire request-handling starting at the point where the
550request was just parsed. The argument is the new request to use.
551Be careful, this makes it very easy to introduce unwanted endless loops!
552
553<procedure>(htmlize string) => string</procedure>
554
555Encode "special" html symbols like tag and attribute characters so
556they will not be interpreted by the browser.
557
558<procedure>(build-error-message exn chain [raw-output])</procedure>
559
560Build an error message for the exception {{exn}}, with call chain
561{{chain}}. Defaults to HTML output, unless {{raw-output}} is given and
562nonfalse.
563
564=== Modules
565
566This section will describe what the various modules that come with
567Spiffy are and how they work.
568
569==== ssp-handler
570
571SSP, or Scheme Server Pages, are a way to embed Scheme in HTML
572pages. Files with an extension of {{.ssp}} are handled specifically,
573by replacing occurrences of certain reserved tags with Scheme code.
574There are two possible forms, either the long version, where all
575output is redirected to the HTTP response port, or the short, where
576the result of the embedded expression is displayed in the response.
577The tags default to {{<?scheme}} and {{<?}}, see Configuration for how
578to change them.
579
580<enscript highlight=scheme>
581   <html><body>
582   <ol><?scheme (for-each (lambda (i) (printf "<li>~S~%" i)) (iota 5))?></ol>
583   <br />
584   <b><?(call-with-values (lambda () (user-information (current-user-id))) (lambda (name . _) name))?><b>
585   </body></html>
586</enscript>
587
588would generate for example something like this:
589
590     1. 0
591     2. 1
592     3. 2
593     4. 3
594     5. 4
595
596  (felix x 500 100 /home/felix /bin/bash)
597
598When a {{.ssp}} file is loaded the first time, or when it has been
599modified, then a translation takes place that generates a loadable
600Scheme source file (with the extension {{.sspx}}, in the same
601directory as the original file) from the original data, so in the
602above example something like this would be generated:
603
604<enscript highlight=scheme>
605  (let ()
606    (display "<html><body>\n<ol>")
607    (for-each (lambda (i) (printf "<li>~S~%" i)) (iota 5))
608    (display "</ol>\n<br />\n<b>")
609    (display (call-with-values (lambda () (user-information (current-user-id))) (lambda (name . _) name)))
610    (display "<b>\n</body></html>\n") )
611</enscript>
612
613Note that the body is evaluated in a {{(let () ...)}} form.
614
615Note: each request runs in a separate thread, so code in {{.ssp}}
616pages should take care when using global variables.
617
618===== Configuration
619
620The SSP handler can be configured with the following options:
621
622<parameter>(ssp-short-open-tag [tag-regexp])</parameter>
623
624The opening tag for short fragments. Default: {{<?}}
625
626<parameter>(ssp-long-open-tag [tag-regexp])</parameter>
627
628The opening tag for long fragments. Default: {{<?scheme}}
629
630<parameter>(ssp-close-tag [tag-regexp])</parameter>
631
632The closing tag for Scheme fragments in {{.ssp}} files. Default: {{?>}}
633
634<parameter>(ssp-eval-environment [environment])</parameter>
635
636The environment passed to {{eval}} when evaluating Scheme code inside
637{{.ssp}}-pages.  Default: {{interaction-environment}}
638
639<parameter>(ssp-cache-dir [directory-name])</parameter>
640
641The directory under which to store cached .ssp files (these end in .sspx
642and are pure Scheme files).  Useful if you want to block write access to
643the webserver under your docroot.  Default: {{"."}}
644
645If it's a relative path, it is relative to {{root-path}}, if absolute
646it's taken to be relative to the filesystem root.  A directory structure
647similar to the docroot will be created underneath this path, so for
648example if the file {{/foo/bar/qux.ssp}} exists, and the cache dir is
649set to {{/cache}}, it will create the file {{/cache/foo/bar/qux.sspx}}.
650
651===== Runtime information
652
653For the duration of evaluating a SSP page, the following parameters
654will have a value assigned to them:
655
656<parameter>(current-workdir [path])</parameter>
657
658During execution, the current working directory of the SSP handler.
659Any of the "include" procedures (ssp-include, ssp-stringize) will
660interpret their file arguments to be relative to this directory.
661
662<parameter>(ssp-exit-handler [handler])</parameter>
663
664During execution of an ssp page, {{ssp-exit-handler}} is bound to a
665procedure that will finish the current page, ignoring any further
666content or code.
667
668===== Procedures
669
670The ssp-handler module adds the following procedures to the environment:
671
672<procedure>(ssp-handler filename)</procedure>
673
674The handler itself, which should be used in the {{file-extension-handlers}}
675parameter list.
676
677<procedure>(ssp-include filename)</procedure>
678
679Translates the file {{filename}} into Scheme by replacing {{<?scheme ... ?>}}
680and {{<? ... ?>}} sequences (if needed) and writes the translated contents
681to the current output-port.
682
683<procedure>(ssp-stringize FILENAME)</procedure>
684
685Similar to {{ssp-include}}, but instead of writing the translated
686text, the text is returned as a string.
687
688==== web-scheme-handler
689
690Another way of executing Scheme code to produce content are {{.ws}}
691files: these should contain a Scheme expression that is expected to
692evaluate to a string which will be directly written as the response to
693the current request. This facility is intended for Scheme code that
694uses the [[web-scheme]] extension.
695
696You can use the {{web-scheme-handler}} for any Scheme file which
697returns HTML as a string or which has a side-effect of outputting the
698HTML. If it's the latter, make sure the final statement in your file
699does not return a string or it will be appended to the output (just
700like in the {{csi}} REPL).
701
702'''Tip''' This handler type is perfect not only for web-scheme but
703also for when you're using {{SRV:send-reply}} with SXML or for example a
704wiki-to-string translator.
705
706Note: each request runs in a separate thread, so code in {{.ws}} pages
707should take care when using global variables.
708
709Note: web-scheme-handler is a separate extension and must be imported
710as such.
711
712<enscript highlight=scheme>
713(require-extension web-scheme-handler)
714</enscript>
715
716===== Configuration
717
718The Web-scheme handler can be configured with the following options:
719
720<parameter>(web-scheme-eval-environment [environment])</parameter>
721
722The environment passed to {{eval}} when evaluating Scheme code inside
723{{.ws}}-pages.  Default: {{interaction-environment}}
724
725===== Procedures
726
727The Web-scheme handler adds only one procedure to the environment:
728
729<procedure>(web-scheme-handler filename)</procedure>
730
731The handler itself, which should be used in the {{file-extension-handlers}}
732parameter list.
733
734
735==== cgi-handler
736
737Spiffy supports [[http://www.ietf.org/rfc/rfc3875|CGI/1.1 as specified by RFC 3875]].
738
739All request headers will be passed as environment variables to the CGI
740program, prefixed with {{"HTTP_"}}, and converted to uppercase, with
741hyphens ("-") replaced by an underscore ("_"). The CGI program will
742receive the request body in unparsed form from stdin and should write
743a complete HTTP response to stdout.  Any headers that are missing but
744required for HTTP will be added by Spiffy.  For more info on how a CGI
745script is called, consult the spec.
746
747The {{AUTH_TYPE}} and {{REMOTE_USER}} environment variables are
748currently not set during invocation of CGI subprocesses.
749The {{REMOTE_IDENT}} environment variable is not and never will be supported.
750
751===== Configuration
752
753CGI handler can be configured with the following parameters:
754
755<procedure>(cgi-default-environment [env-alist])</procedure>
756
757The environment variables that should be in the default environnment
758of every CGI program.  Variables like {{SCRIPT_NAME}} will be added
759dynamically to the end of this alist.
760
761Default:
762
763<enscript highlight=scheme>
764(("GATEWAY_INTERFACE" . "CGI/1.1"))
765</enscript>
766
767===== Procedures
768
769CGI-handler adds two procedures to the environment:
770
771<procedure>(cgi-handler filename [interpreter])</procedure>
772
773The cgi handler simply calls CGI scripts. It is assumed the
774requested file is executable if no interpreter is given.
775(If used as a regular handler, it will only receive the
776filename).  If the filename is a relative path, it is assumed
777to be relative to {{(root-path)}}.  It's safer to store your
778scripts outside the docroot, though!
779
780<procedure>(cgi-handler* [interpreter])</procedure>
781
782The {{cgi-handler*}} procedure is usually more useful.  It allows you
783to define an interpreter to use for files and returns a new handler.
784See the example above for {{file-extension-handlers}}.
785
786
787==== simple-directory-handler
788
789In order to get directory listings, you can use
790{{simple-directory-handler}}.  Just parameterize {{handle-directory}}'s
791value with the {{simple-directory-handler}} procedure and you're set.
792
793===== Configuration
794
795The simple directory handler has a few configuration options:
796
797<procedure>(simple-directory-dotfiles? [dotfiles?])</procedure>
798
799Determines if dotfiles should show up in the directory listings.
800Default: {{#f}}
801
802<procedure>(simple-directory-display-file [displayer])</procedure>
803
804A lambda that accepts three arguments: the remote filename, the local
805filename and a boolean that says if the file is a directory.  This
806lambda should output a table row with the desired information.
807Defaults to a lambda that prints the name, size and date when the file
808was last modified.
809
810===== Procedures
811
812The simple-directory handler adds only one procedure to the environment:
813
814<procedure>(simple-directory-handler pathname)</procedure>
815
816The handler itself, which should be used in the {{handle-directory}}
817parameter.
818
819=== Examples
820
821Spiffy is very easy to use for simple cases:
822
823<enscript highlight=scheme>
824(use spiffy)
825
826(server-port 80)
827(root-path "/var/www")
828(start-server)
829</enscript>
830
831One could also use parameterize:
832
833<enscript highlight=scheme>
834(use spiffy)
835
836(parameterize ((server-port 80)
837               (root-path "/var/www"))
838  (start-server))
839</enscript>
840
841==== Network tweaks
842
843Spiffy does not activate Chicken's TCP buffering, which results in
844extra traffic: one packet sent per header line.  With a TCP buffer
845size greater than the total header length, all headers will be
846coalesced into a single write; generally the response body will be
847coalesced as well.  For example:
848
849 (tcp-buffer-size 2048)   ; from unit tcp
850 (start-server)
851
852=== Changelog
853
854* trunk Fix ssp-handler's pathname construction so it doesn't error on files in the rootdirectory.
855* 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.
856* 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)
857* 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).
858* 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.
859* 4.9 Export {{file-extension->mime-type}}. Add more info about exceptional situations to the debugging logs when enabled.
860* 4.8 Fix ssl support. Get rid of {{spiffy-root-uri}}. Add timestamp and request info to error log.
861* 4.7 Fix redirects for directories again (thanks to [[/users/mario-domenech-goulart|Mario]]). NO TESTCASE (possibly to be fixed in [[uri-generic]])
862* 4.6 Fix redirects for directories with special URI characters in them (thanks to [[/users/felix-winkelmann|Felix]])
863* 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]
864* 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.
865* 4.3 Fix crash with extensionless files and nonempty extension handlers
866* 4.2 Add support for caching headers and proper If-Modified-Since support
867* 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)
868* 4.0 Rewrite from scratch, using Intarweb
869* pre-4.0 See the changelog for the [[/eggref/3/spiffy|old spiffy]]
870
871=== License
872
873  Copyright (c) 2005-2011, Felix L. Winkelmann and Peter Bex
874  All rights reserved.
875 
876  Redistribution and use in source and binary forms, with or without
877  modification, are permitted provided that the following conditions are
878  met:
879 
880  Redistributions of source code must retain the above copyright
881  notice, this list of conditions and the following disclaimer.
882 
883  Redistributions in binary form must reproduce the above copyright
884  notice, this list of conditions and the following disclaimer in the
885  documentation and/or other materials provided with the distribution.
886 
887  Neither the name of the author nor the names of its contributors may
888  be used to endorse or promote products derived from this software
889  without specific prior written permission.
890 
891  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
892  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
893  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
894  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
895  COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
896  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
897  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
898  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
899  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
900  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
901  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
902  OF THE POSSIBILITY OF SUCH DAMAGE.
Note: See TracBrowser for help on using the repository browser.