Quick guide to Nginx and Swift Perfect server on your local linux machine.

Posted by on May 02 2020, in kraxn

The goal is a to use the Swift Perfect server to use the default “hello world” template, but let Nginx serve static files

I am currently testing out the Swift Perfect Server (eg from [Perfect.org][1]) and Nginx. The idea here is to let Swift Perfect to serve dynamic stuff, and Nginx to serve static stuff (like images, css etc).

This quick & dirty guide assumes the following:

  • You’ve got [Nginx][2] installed and running.
  • You’ve got [Swift][3] installed and working.
  • You’ve got [Perfect Server][4] installed and running

This is for a local configuration, since I don’t have a domain yet, I want to run it locally. So add this line the hosts file

sudo nano /etc/hosts

and add this:

127.0.0.1       swift.local

This will allow you to access swift.local on your laptop or desktop computer.

Let’s create a directory for static files (eg images/pdfs/css ) that your Nginx server will serve.

mkdir -p /var/www/swift.local/html/images

the -p will make sure that all directories along the way will be created. Let’s download an image. I [used this image][5] and renamed it swift.jpeg and placed it in the images folder from above. I set the permissions to 0755:

sudo chmod 0755 /var/www/swift.local/html/images/swift.jpeg

Now that we have directory stucture, lets create virtual hosts file. In my nginx set up, I go to the conf.d directory and create a file called swift.local.conf:

sudo nano /etc/nginx/conf.d/swift.local.conf

and add the following:

server {
        listen 80;

        root /var/www/swift.local/html;
        server_name swift.local www.swift.local;

        access_log /var/www/swift.local/access.log;
        error_log /var/www/swift.local/error.log;

       location / {
                #swift perfect server access
                proxy_pass        http://localhost:8181;
                proxy_set_header  X-Real-IP  $remote_addr;
        }


        # serve static files

        location ~ ^/(images|javascript|js|css|media|static)/  {
          root /var/www/swift.local/html;
          expires 30d;
        }


}

What’s going on here:

  • Our Nginx Server is listening to port 80. If we were on a remote live server, I would port this to 443 for https
  • I’ve stuck my my access/error logs just above the root directory
  • location / : This is where Swift does its magic; the proxy pass goes to port 8181 which is the default Perfect server port. All the dynamic goodies will be here.
  • finally the static files are all service by Nginx and given a 30d caching expiry.

Once you saved this, try the following commands. The first will test your nginx configuration. If anything is wrong with the virtual host you just set up, it should show here. The second will reload your nginx.

sudo nginx -t 
sudo systemctl reload nginx

Finally, create a new PerfectHTTP project anywhere on your local machine. I have build mine in /home//swift/, but you can build this anywhere it makes sense to you. Run the following the commands to create a start project:

git clone https://github.com/PerfectlySoft/PerfectTemplate.git
cd PerfectTemplate
swift build

Now go and edit the the main.swift file. There are 2 changes to make:

  1. add the image code in the responsebody

    1. Remove or comment out the second route handler. Why, because this the work we want nginx to do, not PerfectHTTP: swift
      //routes.add(method: .get, uri: “/**”, handler:
      StaticFileHandler(documentRoot: “./webroot”, allowResponseFilters:
      true).handleRequest)

Here’s the main.swift boilerplate file that has been edited:

  import PerfectHTTP
  import PerfectHTTPServer


func handler(request: HTTPRequest, response: HTTPResponse) {
    response.setHeader(.contentType, value: "text/html")
    response.appendBody(string: "<html><title>Hello, world!</title><body>Hello, world! |<img src=’/images/swift.jpeg’></body></html>")
    response.completed()

}

  var routes = Routes()<br />
  routes.add(method: .get, uri: "/", handler: handler)

  try <span>HTTPS</span>erver.launch(name: "localhost",
                        port: 8181,
                        routes: routes,
                        responseFilters: [
                          (PerfectHTTPServer.HTTPFilter.contentCompression(data: [:]), HTTPFilterPriority.high)])

Almost there! The final step is that we have rebuild the main.swift file, then launch PerfectHTTP:

    swift build
    .build/debug/PerfectTemplate

Now if all went well, you can bring up your browser to http://swift.local and see the “Hello World” served by PerfectHTTPServer and the swift image served by nginx!

Quick Note:

Another way to test how nginx is handling images, is simply stop the perfect server. One the homepage you should get a 502 error from Nginx (because it can’t find the swift server at port 8080 which we stopped). However you should still be able to see the image at http://swift.local/images/swift.jpeg

I had an issue where I couldn’t get additional routes working other than root:

     routes.add(method: .get, uri: "/", handler: handler)

For instance this arbitrary route was giving me an nginx 404:

    routes.add(method: .get, uri: "/scooby/dooby/doo", handler: handler)

The PerfectHTTPServer code looked good; it turned I had this nginx try code under my Nginx server: swift
location / {
#swift perfect server access
proxy_pass http://localhost:8181;
proxy_set_header X-Real-IP $remote_addr;
try_files $uri $uri/ =404;
}

The “try_files $uri $uri/ =404;” by nginx seemed to supercede Perfect’s routing. I deleted the offending line and my Perfect routing again worked. In the future, error handling like 404’s should be done from Perfect, not Nginx.