-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathsrv0.html
152 lines (150 loc) · 9.07 KB
/
srv0.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
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.
<p>
The Arc distribution comes with sample web applications including a new site (<code>news.arc</code>) and a blog (<code>blog.arc</code>).
<h2>Running the server</h2>
The server can be started simply with
<pre class="repl">
arc>(serve 8080)
</pre>
<p>
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.
<pre class="repl">
arc> (thread (serve 8080))
</pre>
A handler can be associated with a URL using the <code>defop</code> macro; this defines a handler function, taking the <code>req</code> parameter:
<pre class="code">
(defop hello req (prn "Hello world!"))
</pre>
The resulting page can be accessed at <a href="http://localhost:8080/hello">http://localhost:8080/hello</a>.
<p>
The Arc web server is rather fragile and platform-dependent. If you encounter problems, the easiest solution is to use the unofficial <a href="http://arcfn.com/2008/02/git-and-anarki-arc-repository-brief.html">Anarki version</a>, which has multiple patches.
<p>
To define a top-level page (<a href="http://localhost:8080">http://localhost:8080</a>), use the page name <code>||</code> (the MzScheme representation of the empty symbol, between <a href="http://download.plt-scheme.org/doc/372/html/mzscheme/mzscheme-Z-H-11.html#node_sec_11.2.4">quoting vertical bars</a>).
<pre class="code">
(defop || req (pr "This is the home page."))
</pre>
<p>
To redirect a page to a different page, the <code>defopr</code> macro is used, with a function that outputs the name of the target page. For example, to redirect <a href="http://localhost:8080/index.html">http://localhost:8080/index.html</a> to the earlier "hello" page:
<pre class="code">
(defopr index.html req (prn "hello"))
</pre>
<p>
The <code>req</code> parameter receives a table that has the field <code>ip</code> holding the IP address of the client, and potentially <code>cooks</code> holding the cookies, and <code>args</code> holding a list of key value pairs from the URL's query string.
<p>
The <code>defop-raw</code> and <code>defopr-raw</code> 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.
<pre class="code">
(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))))
</pre>
On the second reload (after the cookie gets assigned), <a href="http://localhost:8080/bar?x=1&y=2&z">http://localhost:8080/bar?x=1&y=2&z</a> will display:
<pre class="code">
127.0.0.1
((mycookie 42))
((x 1) (y 2) (z ))
</pre>
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.
<p>
This functionality can be used to implement a simple form and form handler; the form is at <a href="http://localhost:8080/myform">http://localhost:8080/myform</a>. This example uses some of the <a href="html.html">HTML functions</a>.
<pre class="code">
(defop myform req (form "myhandler" (single-input "Enter:" 'foo 10 "Submit")))
(defop myhandler req (prn "You entered") (prbold (alref (req 'args) "foo")))
</pre>
<p>
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.
<p>
The following table shows the macros for generating arbitrary server-side handlers of the given types.
<table width=100% class="info">
<tr><th width=16%> </th><th width=16%>HTML</th><th width=16%>Headers + HTML</th><th width=16%>Redirect</th><th width=16%>Headers + Redirect</th></tr>
<tr>
<th>Macro</th>
<td><code>defop</code></td>
<td><code>defop-raw</code></td>
<td><code>defopr</code></td>
<td><code>defopr-raw</code></td>
</tr></table>
<h2>Continuations</h2>
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.)
<p>
The continuations used by the web server are explicit function. (The web server does not use <code>ccc</code> 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.
<p>
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.
<p>
The previous example can be implemented with continuations as follows:
<pre class="code">
(defop myform2 req (aform myfunc (single-input "Enter:" 'foo 10 "Submit")))
(def myfunc (req) (prn "You entered") (prbold (alref (req 'args) "foo")))
</pre>
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.)
<pre class="code">
(defop myform2 req (aform
[do (prn "You entered") (prbold (alref (_ 'args) "foo"))]
(single-input "Enter:" 'foo 10 "Submit")))
</pre>
<p>
Internally, the web server associates a token called the <code>fnid</code> with each instance of a link or form, and records the continuation function for each token. Periodically, old fnids are deleted.
<p>
The following table illustrates the operations to generate links, URLs, or forms, for the four different types of output functions. (In practice, both <code>arform</code> and <code>arformh</code> give access to the headers.)
<table width=100% class="info">
<tr><th width=16%> </th><th width=16%>HTML</th><th width=16%>Headers + HTML</th><th width=16%>Redirect</th><th width=16%>Headers + Redirect</th></tr>
<tr>
<th>Link generation</th>
<td><code>w/link, w/link-if, onlink, linkf</code></td>
<td><code></code></td>
<td><code>w/rlink, rlinkf</code></td>
<td><code></code></td>
</tr>
<tr>
<th>URL generation</th>
<td><code>flink, url-for</code></td>
<td><code></code></td>
<td><code>rflink</code></td>
<td><code></code></td>
</tr>
<tr>
<th>Form generation</th>
<td><code>aform, timed-aform</code></td>
<td><code>aformh</code></td>
<td><code>arform</code></td>
<td><code>arformh</code></td>
</tr>
</table>
<p>
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.
<h2>Explanation of the "Arc Challenge" solution</h2>
The <a href="http://www.paulgraham.com/arcchallenge.html">Arc Challenge</a> 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.
<p>
The solution in Arc is as follows:
<pre class="code">
(defop said req
(aform [w/link (pr "you said: " (arg _ "foo"))
(pr "click here")]
(input "foo")
(submit)))
</pre>
The solution may be easier to understand if the continuation functions are made explicit:
<pre class="code">
(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")))
</pre>
The <code>said</code> operation creates a form that when submitted
executes the <code>form-continuation</code> 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 <code>form-continuation</code>, which results in a closure with the
<code>req</code> object from <code>form-continuation</code>. That is,
when the link continuation executes, potentially a long time after
<code>form-continuation</code> 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.