Arc includes a web server with several interesting features, including a continuation-based design. The web server includes many different operations to create forms and links with associated continuations.

The Arc distribution comes with sample web applications including a new site (news.arc) and a blog (blog.arc).

Running the server

The server can be started simply with
arc>(serve 8080)

However, it is generally better to start the server in a separate thread, so the Arc REPL can be used. This allows the web server to be modified while it is running.

arc> (thread (serve 8080))
A handler can be associated with a URL using the defop macro; this defines a handler function, taking the req parameter:
(defop hello req (prn "Hello world!"))
The resulting page can be accessed at http://localhost:8080/hello.

The Arc web server is rather fragile and platform-dependent. If you encounter problems, the easiest solution is to use the unofficial Anarki version, which has multiple patches.

To define a top-level page (http://localhost:8080), use the page name || (the MzScheme representation of the empty symbol, between quoting vertical bars).

(defop || req (pr "This is the home page."))

To redirect a page to a different page, the defopr macro is used, with a function that outputs the name of the target page. For example, to redirect http://localhost:8080/index.html to the earlier "hello" page:

(defopr index.html req (prn "hello"))

The req parameter receives a table that has the field ip holding the IP address of the client, and potentially cooks holding the cookies, and args holding a list of key value pairs from the URL's query string.

The defop-raw and defopr-raw macros let the handler function add HTTP headers. The handler must then output a blank line followed by the HTML content or redirect path. One use of this is to add cookies to the headers.

 (defop-raw bar (str req) (w/stdout str
   (prn "Set-Cookie: mycookie=42")
   (prn)
   (prn (req 'ip)) (br) (prn (req 'cooks)) (br) (prn (req 'args))))
On the second reload (after the cookie gets assigned), http://localhost:8080/bar?x=1&y=2&z will display:
127.0.0.1
((mycookie 42))
((x 1) (y 2) (z ))
This illustrates how the handler can access the client's IP address, the cookies, and the URL query parameters. The handle does not have access to other HTTP headers.

This functionality can be used to implement a simple form and form handler; the form is at http://localhost:8080/myform. This example uses some of the HTML functions.

(defop myform req (form "myhandler" (single-input "Enter:" 'foo 10 "Submit")))
(defop myhandler req  (prn "You entered") (prbold (alref (req 'args) "foo")))

Several types of output functions are supported by Arc. The simplest is a function that outputs HTML. A function can also optionally output additional HTTP headers. Arc also supports redirect functions that can perform server-side operations and then redirect the browser to a new page; these functions can optionally output additional headers. Finally, Arc has partial support for asynchronous functions, which don't return any response to the request.

The following table shows the macros for generating arbitrary server-side handlers of the given types.
 HTMLHeaders + HTMLRedirectHeaders + Redirect
Macro defop defop-raw defopr defopr-raw

Continuations

The previous section showed how to implement basic HTML and redirect handlers in Arc. Arc's web server also provides continuation support, which is typically used for handlers. That is, links and forms can be assigned a function that will be executed on the server if the link or form is clicked. (The continuation would be called a callback in some languages.)

The continuations used by the web server are explicit function. (The web server does not use ccc first-class continuations.) The functions take a request object as argument and print the appropriate response. By using closures, the functions can maintain state across requests; the state will be included in the closure when the function is defined, and the function can access the state when it is later invoked.

The web server includes many operations to associate continuation functions and fnids with links and forms. The web server contains operations to generate links and forms of the various types, as shown in the following table.

The previous example can be implemented with continuations as follows:

(defop myform2 req (aform myfunc (single-input "Enter:" 'foo 10 "Submit")))
(def myfunc (req) (prn "You entered") (prbold (alref (req 'args) "foo")))
Generally, the continuation function is defined within the original form definition, so a closure can be created. (In this case, the closure is unnecessary, as no state from the first operation is preserved.)
(defop myform2 req (aform
  [do (prn "You entered") (prbold (alref (_ 'args) "foo"))]
  (single-input "Enter:" 'foo 10 "Submit")))

Internally, the web server associates a token called the fnid with each instance of a link or form, and records the continuation function for each token. Periodically, old fnids are deleted.

The following table illustrates the operations to generate links, URLs, or forms, for the four different types of output functions. (In practice, both arform and arformh give access to the headers.)
 HTMLHeaders + HTMLRedirectHeaders + Redirect
Link generation w/link, w/link-if, onlink, linkf w/rlink, rlinkf
URL generation flink, url-for rflink
Form generation aform, timed-aform aformh arform arformh

Different operations use one of five different mechanisms to specify the continuation function. The function may be specified as an expression, a function of one variable (the request object), a request parameter and body, output stream and request parameters and a body, or a body alone. The documentation should be consulted to see which mechanism goes with which function.

Explanation of the "Arc Challenge" solution

The Arc Challenge posed the problem of implementing a simple web function. Under the URL "/said", it provides a form with input field. When submitted, the form goes to a page with a link "click here". The link leads to a page with the text "You said: " followed by the original input.

The solution in Arc is as follows:

(defop said req
  (aform [w/link (pr "you said: " (arg _ "foo"))
           (pr "click here")]
    (input "foo") 
    (submit)))
The solution may be easier to understand if the continuation functions are made explicit:
(defop said req
  (aform form-continuation
    (input "foo") 
    (submit)))

(def form-continuation (req) 
  (w/link 
      (pr "you said: " (arg req "foo")) ;link continuation expression
    (pr "click here")))
The said operation creates a form that when submitted executes the form-continuation function. The continuation function creates a link that will execute the link continuation expression. The important thing to notice is the link continuation expression is defined inside form-continuation, which results in a closure with the req object from form-continuation. That is, when the link continuation executes, potentially a long time after form-continuation completes, it will still have the state. This illustrates how closures can be used to carry state from one request to another. The form can be executed multiple times, and each execution will have a unique state in the link continuation closure.