A Taste of Monte: Hello Web¶
Let’s see what a simple web server looks like in Monte:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import "lib/http/server" =~ [=> makeHTTPEndpoint]
exports (main)
def helloWeb(_request) as DeepFrozen:
"Build a simple HTML response."
return [200, ["Content-Type" => "text/html"], b`<p>Hello!</p>`]
def main(argv, => makeTCP4ServerEndpoint) :Int as DeepFrozen:
"Obtain a port and create an HTTP server on that port."
def portNum :Int := _makeInt(argv.last())
def ep := makeHTTPEndpoint(makeTCP4ServerEndpoint(portNum))
traceln(`serving on port $portNum`)
ep.listen(helloWeb)
return 0
|
The makeHTTPEndpoint
import reads much like Python’s from
http.server import makeHTTPEndpoint
, though the mechanics of a
module declaration in monte are a bit different:
it uses pattern matching to bind names to objects
imported from modules.
We declare that this module exports
its main
function, as is
conventional for executable programs.
Todo
Document how to compile and run such a script.
Blocks in Monte are typically written with indentation, like Python, though blocks in general may be written with curly-braces as well.
Note
Tabs are a syntax error in Monte.
Expressions¶
The def-expr for defining the helloWeb
function is similar to
Python’s syntax for defining functions.
The expression inside the call to traceln(…)
does string interpolation,
similar to Perl, Ruby, and bash. It is a quasiliteral
expression:
▲> def portNum := 8080
▲> `serving on port $portNum`
"serving on port 8080"
Another quasiliteral is b`<p>Hello!</p>`
, which denotes a Bytes
object
rather than a character string.
Objects and Message Passing¶
Monte is a pure object language, which means that all values in Monte are
objects. All operations on objects are done by passing
messages. This includes ordinary method calls like
argv.last()
as well as function calls such as
traceln(portNum)
and even syntax for constructing lists
like [200, [], body]
and maps like ["C" => "t"]
.
Cooperation Without Vulerability¶
Suppose our server takes an arbitrary expression from the web client and evaluates it:
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 | import "lib/http/server" =~ [=> makeHTTPEndpoint]
import "lib/http/tag" =~ [=> tag]
import "formData" =~ [=> fieldMap]
exports (main)
object calculator as DeepFrozen:
to run(request):
return switch (request.getVerb()):
match =="GET":
calculator.get(request)
match =="POST":
calculator.post(request)
to get(_request):
def body := b`
<form method="POST">
<label>Arbitrary code to execute:<input name="code" /></label>
</form>
`
return [200, ["Content-Type" => "text/html"], body]
to post(request):
def code := fieldMap(request.getBody())["code"]
def result := eval(code, safeScope)
# NB: The `tag` object does automatic HTML escaping. No extra effort
# is required to prevent XSS. ~ C.
def html := tag.pre(M.toString(result))
return [200, ["Content-Type" => "text/plain"], b`$html`]
def main(argv, => makeTCP4ServerEndpoint) :Int as DeepFrozen:
def portNum := _makeInt(argv.last())
def ep := makeHTTPEndpoint(makeTCP4ServerEndpoint(portNum))
traceln(`serving $calculator on port $portNum`)
ep.listen(calculator)
return 0
|
With conventional languages and frameworks, this would be injection, #1 on the list of top 10 web application security flaws:
Injection can result in data loss or corruption, lack of accountability, or denial of access. Injection can sometimes lead to complete host takeover.
But using object capability discipline, untrusted code has only the authority
that we explicitly give it. This rich form of cooperation comes with
dramatically less vulnerability [1]. The environment in this example is
safeScope, which is the same environment modules are evaluated in – it
provides basic runtime services such as constructors for lists, maps, and
other structures, but no “powerful” objects. In particular,
makeTCP4ServerEndpoint
is not in scope when the remote code is executed,
so the code cannot use it to access the network. Neither does the code have
any access to read from nor write to files, clobber global state, nor launch
missiles.
Notes
[1] | We implicitly grant authority to compute indefinitely. Object capability discipline does not address denial of service. Monte’s vats include a conventional mechanism to put a finite limit on computation. |