-
-
Notifications
You must be signed in to change notification settings - Fork 7
Simple Time Server
This article is a tutorial that aims to give a simple server example.
The server serves a static html page that allows user to retrieve the server time by clicking on a button. We will explain how the application is structured to achieve this using tiscaf, so that you can get some insight on the concepts. For more details, please have a look at the getting started guide.
The code of this tutorial is available in the repository under the examples directory. You can start the code in sbt like this:
> project timeserver
> run-main tiscaf.example.TimeServer
and then access the page at http://localhost:8080/.
So how to build this server application?
Before we start, the first thing to do is
import tiscaf._
Now we can go...
Our server must be able to return the current time when some URL is queried, let's say "/currenttime". To do this, we will implement a first application. In tiscaf, an application is done by implementing tiscaf.HApp
and allows you to group actions that belong together. It can dispatch to the correct action using any information available in a request.
object TimeApp extends HApp {
def resolve(req: HReqData): Option[HLet] = req.uriPath match {
case "currenttime" => Some(TimeLet)
case _ => None
}
}
So, you only need to implement the resolve
method, that returns the action to perform. In our case, this application only knows what to do if "/currenttime" is queried, and in this case returns the TimeLet
which is the request handler associated to this action. In other cases, it says that it does not know what to do by returning None
.
A request handler is called an HLet
in tiscaf and in the case of simple handlers, you only need to implement tiscaf.HSimpleLet
.
object TimeLet extends HSimpleLet {
def act(talk: HTalk) {
// simply return the current server time
val time = new Date().getTime.toString
talk.setContentLength(time.length)
.write(time)
}
}
The handler must implement the act
method that gets a HTalk
instance. HTalk
exposes the interface to deal with the request data, the session data, and, what is of interest in our case, it allows us to send a response to the client.
You must explicitly set the length of the answer, and then write the content (you could also set the mime type of the answer, for more details, see here).
Pretty simple, isn't it?
Ok, we have a way to send back the current time to the client. Now, let's see how to server an HTML interface to query the time. Another application of our server is specifically to serve static contents, such as html pages and javascript files (we could add css files as well, or any other file needed). So we simple create a new application, just like the first one, but even simplier:
object StaticApp extends HApp {
override def buffered : Boolean = true // ResourceLet needs buffered or chunked be set
def resolve(req: HReqData) = Some(StaticLet) // generates 404 if resource not found
}
This application returns a simple request handler named, StaticLet
which will do all the work for us!
object StaticLet extends let.ResourceLet {
protected def dirRoot = ""
// what resources are indexes?
override protected def indexes = List("index.html")
}
tiscaf has a default request handlers that allows to map URI to a directory in the classpath. It is called tiscaf.let.ResourceLet
. If the resource is not found, it will generate an error 404.
The only method to implement is dirRoot
which tells tiscaf to which directory the root URL is mapped. In our case, the static resources are in the root package, so this prefix is empty.
Note: We also mentioned here that index.html
is an index, so that we don't have to write it in the URL, but this is not mandatory.
The goal of this tutorial is not to explain how html/javascript code is written, so we won't explain it a lot here. It is pretty basic, there is just an index.html
file that has a button to query the current server time and display it. And we use jquery to make an ajax request when the button is clicked and to extract the answer from the server.
The static content can be found in the repository.
It is placed in the resources
directory so that it is automatically included in the classpath by sbt.
First of all, we will need a server, which is quite easy to achieve by implementing the tiscaf.HServer
trait.
object TimeServer extends HServer with App {
def apps = Seq(TimeApp, StaticApp)
def ports = Set(8080)
// do not start the daemon stop thread
override protected def startStopListener { }
start
println("press enter to stop...")
Console.readLine
stop
}
A server must implement two methods:
-
apps
returns the list of applications on this server. Here we have two of them, one delivering the server time, and one delivering static content. The applications are searched for a matching request handler in the order they appear in the set. The first application returingSome(<hlet>)
for a request is then chosen to handle that request. So the order of the applications matters! If no application is found, tiscaf answers with an error 404. -
ports
returns the list of ports the server is listening to.
Starting the server is simply done by calling the start
method.
Note: we override the startStopListener
method here to do nothing. Usually this method starts a thread that listens for some stop message to shut done the server, which can be useful if you start your server as a daemon.
You can now launch TimeServer
That's all folks, you have your server running!