In the previous article, we set a goal to deploy a program in one hip command line hop, let’s kickstart the code with an outline of the application server.
Any resemblance to competitor technology is wholly, and fully coincidental.
We want to deploy http web application stuffed into a
hello
library that looks like the following:
;; hello.scm
(library (hello)
(export main)
(import (petit-cloud application))
define main
(lambda (method path headers body)
(values 200
(list (cons 'content-type "text/plain"))
(string->utf8 "Hello schemer!\n"))))) (
The expected, so called workflow, is that given an existing
application server running at www.example:9999
, we want to
deploy the above library with the following one-liner:
#;sh> petit-cloud deploy www.example:9999 hello.scm
When that command is issued, we will be prompted for a password, after a nanowhile, we will be able the manually test it with:
#;sh> curl http://www.example:9999
Hello schemer!
The application server will be listening to port 9999 of all available network interfaces, that can be done with:
(define-values (client-accept close)"0.0.0.0" 9999)) (petit-cloud-http-connect
The variable client-socket
is a generator of three
objects read
, write
, and close
,
that work as follow:
(read)
will produce a representation of an HTTP
request;(write object)
will write OBJECT
as an
HTTP response;(close)
will close the connection.The simplest way to use it is:
(define-values (client-accept close)"0.0.0.0" 9999))
(petit-cloud-http-connect
read write close)
(define-values (;; client-accept will pause
;; until a HTTP request hit
;; the port 9999
(client-accept))
read))
(define-values (method url headers body) (;; let's reply
write 200
(list (cons 'content-type "text/plain"))
(string->utf8 "Hello schemers\n"))
( (close)
It is possible accept multiple clients simultaneously, by passing a
procedure to petit-cloud-http-connect
:
define client-accept
(lambda (read write close)
(;; where is everyone
read))
(define-values (method url headers body) (;; reply
write 200
(list (cons 'content-type "text/plain"))
(string->utf8 "Hello schemers\n"))
(;; the end
(close)))
"0.0.0.0"
(petit-cloud-http-connect 9999
client-accept)
The command call petit-cloud deploy
will hit the
application server with a method
that is the symbol
'POST
, and an url
that is:
list 9999
(list "example" "www")
("_" "api" "petit" "cloud")
(list)
(list)) (
And the body is the bytevector representation of
hello.scm
at the start of the article.
We can implement a predicate that returns #t
when it is
a call by petit-cloud deploy
:
define petit-cloud-deploy?
(lambda (method path)
(equal? (cons 'POST
(list 9999
(list "example" "www")
("_" "api" "petit" "cloud")
(list)
(list)))
(cons method path)))) (
If the request is not a deployment, we fallback to the application
procedure returned by the parameter
petit-cloud-current-application
.
At this point, and without any error handling, our application server code looks like the following:
(import (petit-cloud))
define petit-cloud-deploy?
(lambda (method path)
(cons 'POST
(equal (list 9999
(;; TODO: do not hardcode port, and domain
list "example" "www")
(list "_" "api" "petit" "cloud")
(list)
(list)))
(cons method path))))
(
define client-accept
(lambda (read write close)
(read))
(define-values (method url headers body) (if (petit-cloud-deploy? method path)
(let ((application (make-petit-cloud-application body)))
(
(petit-cloud-current-application application)values 201 (list) (bytevector)))
(let ((application (petit-cloud-current-application)))
(
(application method url headers body)))))
"0.0.0.0" 9999 client-accept) (petit-cloud-http-connect
Let’s summarize:
The evaluation of the expression
(petit-cloud-http-connect ip port handler)
will call
HANDLER
on each new request. The definition of
petit-cloud-http-connect
is interesting for the purpose of
building a petit cloud, call that the cherry on the cake.
The procedure mentioned above called HANDLER
is
passed three arguments: read
, write
, and
close
.
Each procedure of read
, write
, and
close
follow a precise protocol. The way they are applied,
its arguments, and return values, but also the order in which each of
them is called is specified. For instance, calling close
,
thereafter read
or write
does not make sense,
because after (close)
there is no-one listening.
The procedure petit-cloud deploy
will call the
procedure HANDLER
, compute a new application with
make-petit-cloud-application
, with the returned procedure,
reset the parameter
petit-cloud-current-application
.
What (make-petit-cloud-application body)
do is very
interesting. Tho, at this time, that is only food for thought.
home.