(index ("start-server" 0) ("accept-loop" 1605) ("switch-user/group" 3338) ("server-software" 3760) ("root-path" 4281) ("server-port" 4435) ("server-bind-address" 4566) ("max-connections" 4752) ("spiffy-user" 5031) ("spiffy-group" 5337) ("index-files" 5746) ("mime-type-map" 5988) ("default-mime-type" 6737) ("default-host" 6953) ("vhost-map" 7150) ("file-extension-handlers" 7442) ("access-log" 7779) ("error-log" 7952) ("debug-log" 8156) ("access-file" 8327) ("trusted-proxies" 8796) ("handle-directory" 9532) ("handle-file" 9879) ("handle-not-found" 10418) ("handle-exception" 11550) ("handle-access-logging" 11994) ("current-request" 12465) ("current-response" 12896) ("current-file" 13222) ("current-pathinfo" 13529) ("remote-address" 13866) ("local-address" 14059) ("secure-connection?" 14200) ("with-headers" 15351) ("write-logged-response" 15681) ("log-to" 16129) ("send-response" 16535) ("send-status" 17221) ("send-status" 17221) ("send-static-file" 18063) ("file-extension->mime-type" 18346) ("restart-request" 18833) ("htmlize" 19118) ("build-error-message" 19304) ("simple-directory-dotfiles?" 19574) ("simple-directory-display-file" 19764) ("simple-directory-handler" 20165))
(def (sig (procedure "(start-server [port: port-number] [bind-address: address] [listen: listen-procedure] [accept: accept-procedure] [addresses: addresses-procedure])" (id start-server))) (p "This is the most convenient way to get a server up and running quickly. It starts the server to listen on the given port. Other configuration can be tweaked through SRFI-39 parameters. These are listed below. Once the server is started, server behaviour can be controlled through these parameters as well.  After the listener is started, when " (tt "spiffy-user") " and/or " (tt "spiffy-group") " are provided, this procedure will drop privileges before starting the accept loop.") (p "By default, Spiffy will only serve static files. On directories, it will give a \"403 forbidden\", unless there is an index-file. If there is, that file's contents will be shown.") (p "All arguments directly supplied to " (tt "start-server") " override the configuration parameter values and will be parameterized to reflect this new setting.") (p (tt "port-number") " defaults to the value of " (tt "server-port") " (see below). " (tt "bind-address") " defaults to the value of " (tt "server-bind-address") " (see below). " (tt "listen") " defaults to " (tt "tcp-listen") " and should accept a port number, backlog and bind address. " (tt "accept") " defaults to " (tt "tcp-accept") ", and is passed on as-is to " (tt "accept-loop") ". " (tt "addresses-procedure") " defaults to a procedure which works like " (tt "tcp-addresses") " but can also detect SSL ports and return the addresses of the underlying TCP connection."))
(def (sig (procedure "(accept-loop listener accept [addresses])" (id accept-loop))) (p "This procedure starts the loop which accepts incoming connections and fires off threads to handle requests on those connections.  You can use it if you need more control over the startup process than " (tt "start-server") " offers.") (p "The listener object should be an object which is accepted by the " (tt "accept") " procedure, which should return two values; an input and an output port which represent an incoming connection from a client. The optional " (tt "addresses") " procedure should accept the input port returned by the " (tt "accept") " procedure and return two strings; the local and remote addresses of the server and client, respectively.") (p "For example, you can set up an SSL context and drop privileges, and possibly load extra code before starting the accept loop (Spiffy contains the required code to detect SSL ports, and will handle those more-or-less transparently):") (highlight scheme "(use chicken-syntax) ; This must done at the very toplevel so that it is available in the interaction-environment.\n(use spiffy openssl)\n\n(server-port 443)\n(spiffy-user \"www\")\n(spiffy-group \"www\")\n\n;; Bind the port as root, before we drop privileges\n(define listener (ssl-listen (server-port)))\n\n;; Load the certificate files as root so we can secure their permissions\n(ssl-load-certificate-chain! listener \"server.pem\")\n(ssl-load-private-key! listener \"server.key\")\n\n;; Drop root privileges\n(switch-user/group (spiffy-user) (spiffy-group))\n  \n;; We don't want to load this extra code as root!\n(load \"extra-code.scm\")\n\n;; Done! Start listening for connections.\n(accept-loop listener ssl-accept)"))
(def (sig (procedure "(switch-user/group user group)" (id switch-user/group))) (p "This is a helper procedure which allows you to easily drop privileges before running the accept loop.  The user and group must be either strings or UID/GID numbers which indicate the username and groupname to which you want to switch.  Either is also allowed to be " (tt "#f") ", if you don't want to switch that aspect of the process."))
(def (sig (parameter "(server-software [product])" (id server-software))) (p "The server software product description. This should be a valid product value as used in the server and user-agent headers by intarweb; this is a list of lists. The inner lists contain the product name, the product version and a comment, all either a string or " (tt "#f") ". Default: " (tt "((\"Spiffy\" \"a.b\" \"Running on Chicken x.y\"))") ", with " (tt "a.b") " being the Spiffy major/minor version and " (tt "x.y") " being Chicken's."))
(def (sig (parameter "(root-path [path])" (id root-path))) (p "The path to the document root, for the current vhost. Defaults to " (tt "\"./web\"") "."))
(def (sig (parameter "(server-port [port-number])" (id server-port))) (p "The port number on which to listen. Defaults to 8080."))
(def (sig (parameter "(server-bind-address [address])" (id server-bind-address))) (p "The IP address on which to listen, or all addresses if " (tt "#f") ". Defaults to " (tt "#f") "."))
(def (sig (parameter "(max-connections [number])" (id max-connections))) (p "The maximum number of simultaneously active connections. Defaults to 1024.") (p "Any new connection that comes in when this number is reached must wait until one of the active connections is closed."))
(def (sig (parameter "(spiffy-user [name-or-uid])" (id spiffy-user))) (p "The name or UID of a user to switch to just after binding the port. This only works if you start Spiffy as root, so it can bind port 80 and then drop privileges. If " (tt "#f") ", no switch will occur. Defaults to " (tt "#f") "."))
(def (sig (parameter "(spiffy-group [name-or-gid])" (id spiffy-group))) (p "The name or GID of a group to switch to just after binding the port. This only works if you start Spiffy as root, so it can bind port 80 and then drop privileges. If " (tt "#f") ", it will be set to the primary group of " (tt "spiffy-user") " if the user was selected. Otherwise, no change will occur.  Defaults to " (tt "#f") "."))
(def (sig (parameter "(index-files [file-list])" (id index-files))) (p "A list of filenames which are to be used as index files to serve when the requested URL identifies a directory.  Defaults to " (tt "'(\"index.html\" \"index.xhtml\")")))
(def (sig (parameter "(mime-type-map [extension->mimetype-list])" (id mime-type-map))) (p "An alist of extensions (strings) to mime-types (symbols), to use for the content-type header when serving up a static file. Defaults to") (pre " '((\"html\" . text/html)\n   (\"xhtml\" . application/xhtml+xml)\n   (\"js\"  . application/javascript)\n   (\"css\" . text/css)\n   (\"png\" . image/png)\n   (\"xml\" . application/xml)\n   (\"pdf\" . application/pdf)\n   (\"jpeg\" . image/jpeg)\n   (\"jpg\" . image/jpeg)\n   (\"gif\" . image/gif)\n   (\"ico\" . image/vnd.microsoft.icon)\n   (\"svg\" . image/svg+xml)\n   (\"txt\" . text/plain))") (p "See also " (tt "file-extension->mime-type") " for a procedure which can look up file extensions for you."))
(def (sig (parameter "(default-mime-type [mime-type])" (id default-mime-type))) (p "The mime-type (a symbol) to use if none was found in the " (tt "mime-type-map") ". Defaults to " (tt "'application/octet-stream")))
(def (sig (parameter "(default-host [hostname])" (id default-host))) (p "The host name to use when no virtual host could be determined from the request.  See the section on virtual hosts below."))
(def (sig (parameter "(vhost-map [host-regex->vhost-handler])" (id vhost-map))) (p "A mapping of virtual hosts (regex) to handlers (procedures of one argument; a continuation thunk). See the section on virtual hosts below. Defaults to " (tt "`((\".*\" . ,(lambda (continue) (continue))))")))
(def (sig (parameter "(file-extension-handlers [extension->handler-list])" (id file-extension-handlers))) (p "An alist mapping file extensions (strings) to handler procedures (lambdas of one argument; the file name relative to the webroot). Defaults to " (tt "'()") ". If no handler was found, defaults to just sending a static file."))
(def (sig (parameter "(access-log [log-file-or-port])" (id access-log))) (p "Filename (string) or port to append access log output to.  Default: " (tt "#f") " (disabled)"))
(def (sig (parameter "(error-log [log-file-or-port])" (id error-log))) (p "Filename (string) or port to which error messages from evaluated code should be output. Default: " (tt "(current-error-port)")))
(def (sig (parameter "(debug-log [log-file-or-port])" (id debug-log))) (p "Filename (string) or port to write debugging messages to.  Default: " (tt "#f") " (disabled)"))
(def (sig (parameter "(access-file [string])" (id access-file))) (p "The name of an access file, or " (tt "#f") " if not applicable.  This file is read when the directory is entered by the directory traversal system, and allows you to write dynamic handlers that can assign new values for parameters only for resources below that directory, very much like adding parameters in code before calling a procedure.  See the section \"Access files\" for more information."))
(def (sig (parameter "(trusted-proxies [list-of-strings])" (id trusted-proxies))) (p "When an incoming request is first accepted, the " (tt "remote-address") " is initialized to the IP address of the remote peer.  When this peer is a reverse proxy in an internal network, that value is not so useful because all requests would seem to come from there.") (p "If you want to have a more meaningful value, you can add the IP addresses of proxies to this list, and " (tt "X-Forwarded-For") " entries from these proxies will be stripped, and the first entry just before the most-distant trusted proxy will be used.") (p "Be careful: all IP addresses in this list will be trusted on their word.") (p "Default: " (tt "()") " (trust no one)"))
(def (sig (parameter "(handle-directory [proc])" (id handle-directory))) (p "The handler for directory entries. If the requested URL points to a directory which has no index file, this handler is invoked. It is a procedure of one argument, the path (a string) relative to the webroot. Defaults to a procedure which returns a \"403 forbidden\"."))
(def (sig (parameter "(handle-file [proc])" (id handle-file))) (p "The handler for files. If the requested URL points to a file, this handler is invoked to serve the file. It is a procedure of one argument, the path (a string) relative to the webroot. Defaults to a procedure which sets the content-type and determines a handler based on the " (tt "file-extension-handlers") ", or " (tt "send-static-file") " if none was found and the method was " (tt "HEAD") " or " (tt "GET") " (otherwise it replies with 405 \"Method Not Allowed\")."))
(def (sig (parameter "(handle-not-found [proc])" (id handle-not-found))) (p "The handler for nonexistent files. If the requested URL does not point to an existing file or directory, this procedure is called. It is a procedure of one argument, the path (a string) to the first missing file in the request path.  Defaults to a procedure which returns a \"404 Not found\".") (p "The path path which is passed to the handler is handled in a particular way: It contains all the components that exist, and the final component is the file or directory which does not exist.  That means this path may differ from the concatenated URI path; this can be seen as \"pathinfo\" for the would-be file, if it had existed.") (p "The path should be interpreted as relative to " (tt "root-path") ".") (p (b "Important security note:") " If you are going to resolve the URI's path in the file system, please remember to check that it doesn't contain slashes in any of the components, as that could be used in a path traversal attack. Alternatively (and possibly even safer), try to ensure that the final path lies under the desired root directory."))
(def (sig (parameter "(handle-exception [proc])" (id handle-exception))) (p "The handler for when an exception occurs. This defaults to a procedure that logs the error to the error log. While debugging or developing, it may be more convenient to use a procedure that sends the error back to the client:") (highlight scheme "(handle-exception\n  (lambda (exn chain)\n    (send-status 'internal-server-error (build-error-message exn chain))))"))
(def (sig (parameter "(handle-access-logging [proc])" (id handle-access-logging))) (p "The handler for access logging. This is a procedure of zero arguments which should write a line to the access log. Defaults to a procedure which writes a line to " (tt "access-log") " which looks like this:") (pre "  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)\""))
(def (sig (parameter "(current-request [request])" (id current-request))) (p "An intarweb request-object that defines the current request. Available from the moment the request comes in and is parsed. Contains, among other things, the query parameters and the request-headers, in fully parsed form (as intarweb returns them).") (p "The URI is automatically augmented with the host, scheme and port if it is not an absolute URI."))
(def (sig (parameter "(current-response [response])" (id current-response))) (p "An intarweb response-object that defines the current response. Available from the same time current-request is available. This keeps getting updated along the way, while the response data is being refined (like when headers are being added)."))
(def (sig (parameter "(current-file [path])" (id current-file))) (p "The path to the requested file (a string). Available from the moment Spiffy determined the requested URL points to a file (just before the " (tt "handle-file") " procedure is called). This file is relative to the " (tt "root-path") "."))
(def (sig (parameter "(current-pathinfo [path])" (id current-pathinfo))) (p "The trailing path " (i "fragments") " (a list of strings) that were passed in the URL after the requested filename. Available from the moment Spiffy determined the requested URL points to a file (just before the " (tt "handle-file") " procedure is called)."))
(def (sig (parameter "(remote-address [address])" (id remote-address))) (p "The IP address (a string) of the user-agent performing the current request.  See also " (tt "trusted-proxies") "."))
(def (sig (parameter "(local-address [address])" (id local-address))) (p "The IP address (a string) on which the current request came in."))
(def (sig (parameter "(secure-connection? [boolean])" (id secure-connection?))) (p (tt "#t") " when the current connection is a secure one (SSL), " (tt "#f") " if it isn't (regular HTTP).  This pertains only to the direct connection itself, so if Spiffy is behind a proxy this will be " (tt "#f") " even if the proxy itself is connected to the client over SSL.") (p "One way to get around this is to " (i "always") " add a custom header to your reverse proxy's configuration file.  Then you can read this out in Spiffy and set secure-connection? to " (tt "#t") " or " (tt "#f") ", as well as updating the request URI's scheme.  There is no standardized header for this, so the default Spiffy won't do this.") (p "An easier way around this is to set up two spiffies listening on different ports and configure one to have " (tt "secure-connection?") " set to " (tt "#t") ", which you redirect incoming HTTPS requests to.") (p (b "This parameter may disappear or change in the future") ", when there are more smart people using Spiffy who know how to deal with this or the Spiffy maintainer has a moment of clarity and decides how to do this cleanly."))
(def (sig (procedure "(with-headers new-headers thunk)" (id with-headers))) (p "Call " (tt "thunk") " with the header list " (tt "new-headers") ". This parameterizes the current response to contain the new headers.  The existing headers are extended with " (tt "new-headers") " through intarweb's " (tt "headers") " procedure."))
(def (sig (procedure "(write-logged-response)" (id write-logged-response))) (p "This procedure simply writes " (tt "current-response") " after calling " (tt "handle-access-logging") ". Responses should always go through this procedure instead of directly using " (tt "write-response") " from intarweb.") (p "If you have a response body to write, you still need to remember to call " (tt "finish-response-body") " (from intarweb) after doing so."))
(def (sig (procedure "(log-to log format . rest)" (id log-to))) (p "Write a printf-style format string to the specified log (one of " (tt "access-log") ", " (tt "error-log") " or " (tt "debug-log") "). " (tt "format") " is a " (tt "printf") "-style format string, and rest arguments should match the arguments one would pass to printf. A newline is appended to the end of the log message automatically."))
(def (sig (procedure "(send-response #!key status code reason body headers)" (id send-response))) (p "Easy way to send string data to the client, with additional headers. It will add appropriate headers and will automatically detect " (tt "HEAD") " requests.  If BODY is " (tt "#f") ", no body is sent and the " (tt "content-length") " header is set to zero.") (p "The status is a symbol describing the response status, which is looked up in " (int-link "intarweb") "'s " (tt "http-status-code") " parameter.  If " (tt "code") " and/or " (tt "reason") " are supplied, these take precedence.  If " (tt "status") " and " (tt "code") " are missing, the default status is " (tt "ok") "."))
(def (sig (procedure "(send-status code reason [message])" (id send-status)) (procedure "(send-status status [message])" (id send-status))) (p "Easy way to send a page and a status code to the client.  The optional " (tt "message") " is a string containing HTML to add in the body of the response. Some structure will be added around the message, so message should only be the actual message you want to send.") (p "This can be called either with a numeric code, string reason and optional message or with a symbolic status and optional message.") (p "Example:") (highlight scheme "(send-status 404 \"Not found\"\n \"Sorry, page not found! Please try <a href='/search.ws'>our search page</a>\")\n\n;; Alternative way of doing this:\n(send-status 'not-found\n \"Sorry, page not found! Please try <a href='/search.ws'>our search page</a>\")"))
(def (sig (procedure "(send-static-file filename)" (id send-static-file))) (p "Send a file to the client. This sets the " (tt "content-length") " header and tries to send the file as quickly as possible to the client. The filename is interpreted relative to " (tt "root-path") "."))
(def (sig (procedure "(file-extension->mime-type EXT)" (id file-extension->mime-type))) (p "Looks up the file extension " (tt "EXT") " (without a leading dot) in " (tt "mime-type-map") ", or uses " (tt "default-mime-type") " when the extension can't be found.") (p "If " (tt "EXT") " is " (tt "#f") ", it'll look up the extension that is the empty string.") (p "This returns a symbol which indicates the mime-type which is matched to the extension (for example " (tt "text/html") ")."))
(def (sig (procedure "(restart-request request)" (id restart-request))) (p "Restart the entire request-handling starting at the point where the request was just parsed. The argument is the new request to use. Be careful, this makes it very easy to introduce unwanted endless loops!"))
(def (sig (procedure "(htmlize string) => string" (id htmlize))) (p "Encode \"special\" html symbols like tag and attribute characters so they will not be interpreted by the browser."))
(def (sig (procedure "(build-error-message exn chain [raw-output])" (id build-error-message))) (p "Build an error message for the exception " (tt "exn") ", with call chain " (tt "chain") ". Defaults to HTML output, unless " (tt "raw-output") " is given and nonfalse."))
(def (sig (procedure "(simple-directory-dotfiles? [dotfiles?])" (id simple-directory-dotfiles?))) (p "Determines if dotfiles should show up in the directory listings. Default: " (tt "#f")))
(def (sig (procedure "(simple-directory-display-file [displayer])" (id simple-directory-display-file))) (p "A lambda that accepts three arguments: the remote filename, the local filename and a boolean that says if the file is a directory.  This lambda should output a table row with the desired information. Defaults to a lambda that prints the name, size and date when the file was last modified."))
(def (sig (procedure "(simple-directory-handler pathname)" (id simple-directory-handler))) (p "The handler itself, which should be used in the " (tt "handle-directory") " parameter."))
