Sunday, January 22, 2017

Building Web Services with Swift and the Kitura framework

The Kitura framework, developed by IBM, is a light-weight, high-performance, web framework and server written in the Swift language.  This framework allows us to very quickly develop complex web services using the Swift language and Swift’s standard Foundation APIs.  This allows us to use our existing Swift knowledge to very easily create server-side services.  We can run our Kitura based services as stand-alone applications, deploy them to IBM’s Bluemix cloud or use IBM’s pre-build Docker image. 

If you are not familiar with what IBM is doing with Swift on the Server, you should check out their site:  https://developer.ibm.com/swift/. 

In the next few posts we will be looking at how we can use Kitura to develop web services with Swift.  For this first post, we will create some very basic web services to show how to setup a project that uses the Kitura framework and also how easy it is to use.  We will start off by creating the project using the Swift Package Manager. 

All the code for this post was written and tested on an Ubuntu 16.10 laptop.  The code should also work on macOS but you may need to install other dependencies. 

Let’s start off by creating a directory named hello and then initialize the project with the Swift package manager:

    mkdir hello
    cd hello
    swift package init

The first thing we need to do is add the Kitura framework as a dependency for our project.  We do this by adding the dependency to the Package.swift file.  The Package.swift file should have the following code in it:

  import PackageDescription

  let package = Package(
    name: "hello",
     dependencies: [
        .Package(url: "https://github.com/IBM-Swift/Kitura.git", majorVersion: 1, minor: 4)
     ]
  )

Now let’s create the main.swift file under the Sources directory and add the following code to it:

  import Kitura

  let router = Router()

  router.get("/hello") {
    request, response, next in
    response.send("Hello World!")
    next()
  }

  Kitura.addHTTPServer(onPort: 8090, with: router)

  Kitura.run()

The first line imports the Kitura framework so we can use it.  Next, we create an instance of the Router class.  The Router class provides an interface for routing the incoming requests.  This class lets us handle all the REST request types, Get, Post, Put and Delete. You can find the reference page for the Router class here:  http://ibm-swift.github.io/Kitura/Classes/Router.html#/s:FC6Kitura6Router3getFtGSqSS_7handlerGSaFzTCS_13RouterRequestCS_14RouterResponseFT_T__T___S0_

We use the get method to set up a router handler that will be invoked when an HTTP GET request comes in with a path pattern that matches the supplied path.  In this example, the path that we are matching against is “/hello”.   If a request does come in with the correct path, the code in the closure is called.  In our service, we send back a response that simply contains “Hello World!”.

We use the addHTTPServer(onPort:with:) to register the router.  The onPort parameter is the port number to bind the server too.  In this example, we will bind to port 8090.  This call only registers the server, it does not start listening until we call the run method in the last line.

Now let’s save the file and run Swift build from our project’s root directory.

If you receive an error that the compile could not find curl/curl.h, you will need to install the libcurl4-openssl-dev package (or any of the other three packages that provide the curl.h header file).  To do this run the following command:
sudo apt-get install libcurl4-openssl-dev

If everything compiles correct we can run the application with the following command from the project’s root directory:

  ./.build/debug/hello

We can verify that the application is running correctly but opening a web browser and putting 127.0.0.1:8090 in the address bar.  The screen that comes up should look something like this:


Now that we know Kitura is running, let’s test our service.  On Linux we have a command line tool called curl that can be used to make http requests.  To test our service run the following command:


If everything is running correctly we should receive the “Hello World!” message back from the server.  Getting back static messages isn’t that exciting but it does illustrate how to setup a project with Kitura and run it. 

Now let’s see how we could handle both path and request parameters with our GET requests.  A path parameter is a parameter that is part of the URL.  An example of a path parameter is:


where jon may be a parameter. 

An example of a request parameter is:


where we have a parameter named “name” with a value of “jon

Let’s look at how we would use path parameters first.  The only thing in our code that will change is the router’s get method and the closure.  The following code shows how we would handle path parameters:

  router.get("/hello/:name") {
    request, response, next in
    let name = request.parameters["name"] ?? ""
    response.send("Hello, \(name)")
    next()
  }

The path that we provide for the get method changed from “/hello” to “hello/:name”.  The “:name” part of the path defines the parameter.  We then use the coalescing operator to check the request to see if there is a parameter named “name”.  The line that does this is:

  let name = request.parameters["name"] ?? ""

Finally we send back a personalized response saying hello to the person whose name was provided in the path parameter.  We can build our project and test it with the following curl command:


Now let’s see how we can handle a request parameter.  The following code shows how we would handle request parameters:

  router.get("/hello") {
    request, response, next in
    let name = request.queryParameters["name"] ?? ""
 response.send("-Hello, \(name)")
    next()
  }

In this example the path that we provide for the get method is “/hello” which is the same as the path we provided in our original example.  We check the queryParameters property to see if we have a parameter named “name”.  The line that does this is:

  let name = request.queryParameters["name"] ?? ""

We can build our project and test it with the following curl command:

  curl http://127.0.0.1:8090/hello?name=jon


As we can see, from this brief introduction, Kitura is very easy to use.  IBM offers a number of frameworks that go along with Kitura, you can see the list here:  https://github.com/IBM-Swift/Kitura/wiki/Kitura-repositories.    In future posts, we will look at how we can use the projects to add logging to our Kitura services, use POST request, serialize and deserialize JSON and how to add a database for our services to use.

Sunday, January 15, 2017

Multi-Client echo server with the BlueSocket framework and libdispatch

In a previous post we showed how to create an echo server using IBM’s BlueSocket framework.  This server was single threaded therefore it could only communicate with one client at a time.  In this post we will show how we can add libdispatch (GCD) to our code to create a multi-client server.
To read an explanation on how the server itself was written, please refer to the previous post Using the BlueSocket framework tocreate an echo server.  The complete code for this project can be found in the Mastering Swift github repository.
The first thing we need to do is add the dispatch framework to the import list.  We should now have the following imports: 

#if os(Linux)
    import Glibc
#else
    import Darwin
#endif
import Foundation
import Socket
import Dispatch

Now we will need to change the start() method a little bit so when it calls the newConnection(socket:) method it uses a separate thread.  The new start() method looks like this:

    func start() throws {
        let socket = try Socket.create()

        listenSocket = socket
        try socket.listen(on: port)
        print("Listening port: \(socket.listeningPort)")
        let queue = DispatchQueue(label: "clientQueue.hoffman.jon", attributes: .concurrent)
        repeat {
            let connectedSocket = try socket.acceptClientConnection()
            print("Connection from: \(connectedSocket.remoteHostname)")
            queue.async{self.newConnection(socket: connectedSocket)}
        } while acceptNewConnection     
    }

There are two parts of this code that changed.  The first is the let queue = DispatchQueue(label: "clientQueue.hoffman.jon", attributes: .concurrent) line which was added to our code.  This line creates a new concurrent dispatch queue.  The second change is we used the queue.async() method to submit the newConnection(socket:) call to the dispatch queue.  You can use the EchoServer class exactly they same way as we did in the previous post.

Those are the only changes you need to make the code a multi-client server.  Here is the full code for the multi-client EchoServer class :

class EchoServer {

    let bufferSize = 1024
    let port: Int
    var listenSocket: Socket? = nil
    var connected = [Int32: Socket]()
    var acceptNewConnection = true

    init(port: Int) {
        self.port = port
    }

    deinit {
        for socket in connected.values {
            socket.close()
        }
        listenSocket?.close()
    }

    func start() throws {
        let socket = try Socket.create()

        listenSocket = socket
        try socket.listen(on: port)
        print("Listening port: \(socket.listeningPort)")
        let queue = DispatchQueue(label: "clientQueue.hoffman.jon", attributes: .concurrent)
        repeat {
            let connectedSocket = try socket.acceptClientConnection()
            print("Connection from: \(connectedSocket.remoteHostname)")
            queue.async{self.newConnection(socket: connectedSocket)}
        } while acceptNewConnection     
    }

    func newConnection(socket: Socket) {
        connected[socket.socketfd] = socket

        var cont = true
        var dataRead = Data(capacity: bufferSize)
        repeat {
            do {
                let bytes = try socket.read(into: &dataRead)
                if bytes > 0 {
                    if let readStr = String(data: dataRead, encoding: .utf8) {
                        print("Received: \(readStr)")
                        try socket.write(from: readStr)
                        if readStr.hasPrefix("quit") {
                            cont = false
                            socket.close()
                        }
                        dataRead.count = 0
                    }
                }
            } catch let error {
                print("error: \(error)")
            }
        } while cont
        connected.removeValue(forKey: socket.socketfd)
        socket.close()
    }
}

You can download the full code listing from the MasteringSwift github repository.