Host

Listen and Serve

You can start the server(s) listening to any type of net.Listener or even http.Server instance. The method for initialization of the server should be passed at the end, via Run function.

The most common method that Go developers are use to serve their servers are by passing a network address with form of "hostname:ip". With Iris we use the iris.Addr which is an iris.Runner type

import "github.com/kataras/iris/v12"
app := iris.New()
// Listening on tcp with network address 0.0.0.0:8080.
//
// Listen is a shortcut for app.Run(iris.Addr(":8080")).
app.Listen(":8080")

Sometimes you choose to have full control over the http.Server instance.

import "net/http"
// Same as before but using a custom http.Server which may being used somewhere else too
server := &http.Server{Addr:":8080" /* other fields here */}
app.Run(iris.Server(server))

The most advanced usage is to create a custom net.Listener and pass that to app.Run.

// Using a custom net.Listener
l, err := net.Listen("tcp4", ":8080")
if err != nil {
    panic(err)
}
app.Run(iris.Listener(l))

A more complete example, using the unix-only socket files feature.

package main

import (
    "os"
    "net"

    "github.com/kataras/iris/v12"
)

func main() {
    app := iris.New()

    // UNIX socket
    if errOs := os.Remove(socketFile); errOs != nil && !os.IsNotExist(errOs) {
        app.Logger().Fatal(errOs)
    }

    l, err := net.Listen("unix", socketFile)

    if err != nil {
        app.Logger().Fatal(err)
    }

    if err = os.Chmod(socketFile, mode); err != nil {
        app.Logger().Fatal(err)
    }

    app.Run(iris.Listener(l))
}

UNIX and BSD hosts can take advandage of the reuse port feature.

package main

import (
    // Package tcplisten provides customizable TCP net.Listener with various
    // performance-related options:
    //
    //   - SO_REUSEPORT. This option allows linear scaling server performance
    //     on multi-CPU servers.
    //     See https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/ for details.
    //
    //   - TCP_DEFER_ACCEPT. This option expects the server reads from the accepted
    //     connection before writing to them.
    //
    //   - TCP_FASTOPEN. See https://lwn.net/Articles/508865/ for details.
    "github.com/valyala/tcplisten"

    "github.com/kataras/iris/v12"
)

// go get github.com/valyala/tcplisten
// go run main.go

func main() {
    app := iris.New()

    app.Get("/", func(ctx iris.Context) {
        ctx.HTML("<h1>Hello World!</h1>")
    })

    listenerCfg := tcplisten.Config{
        ReusePort:   true,
        DeferAccept: true,
        FastOpen:    true,
    }

    l, err := listenerCfg.NewListener("tcp", ":8080")
    if err != nil {
        app.Logger().Fatal(err)
    }

    app.Run(iris.Listener(l))
}

HTTP/2 and Secure

If you have local certification and server key files you can use the iris.TLS to serve https://.

// TLS using files or raw contents.
app.Run(iris.TLS("127.0.0.1:443", "mycert.cert", "mykey.key"))

The method you should use when your app is ready for the outside world is the iris.AutoTLS which starts a secure server with certifications provided by https://letsencrypt.org for free.

// Automatic TLS.
app.Run(iris.AutoTLS(":443", "example.com", "admin@example.com"))

Any iris.Runner

There may be times that you want something very special to listen on, which is not a type of net.Listener. You are able to do that by iris.Raw, but you're responsible of that method

// Using any func() error,
// the responsibility of starting up a listener is up to you with this way,
// for the sake of simplicity we will use the
// ListenAndServe function of the `net/http` package.
srv := &http.Server{Addr:":8080"}
app.Run(iris.Raw(srv.ListenAndServe))

Host configurators

All the above forms of listening are accepting a last, variadic argument of func(*iris.Supervisor). This is used to add configurators for that specific host you passed via those functions.

For example let's say that we want to add a callback which is fired when the server is shutdown

app.Run(iris.Addr(":8080", func(h *iris.Supervisor) {
    h.RegisterOnShutdown(func() {
        println("server terminated")
    })
}))

You can even do that before app.Run method, but the difference is that these host configurators will be executed to all hosts that you may use to serve your web app (via app.NewHost we'll see that in a minute)

app := iris.New()
app.ConfigureHost(func(h *iris.Supervisor) {
    h.RegisterOnShutdown(func() {
        println("server terminated")
    })
})
app.Listen(":8080")

Access to all hosts that serve your application can be provided by the Application#Hosts field, after the Run method.

But the most common scenario is that you may need access to the host before the app.Run method, there are two ways of gain access to the host supervisor, read below.

We have already saw how to configure all application's hosts by second argument of app.Run or app.ConfigureHost. There is one more way which suits better for simple scenarios and that is to use the app.NewHost to create a new host and use one of its Serve or Listen functions to start the application via the iris#Raw Runner.

Note that this way needs an extra import of the net/http package.

Example Code:

h := app.NewHost(&http.Server{Addr:":8080"})
h.RegisterOnShutdown(func(){
    println("server terminated")
})

app.Run(iris.Raw(h.ListenAndServe))

Multi hosts

You can serve your Iris web app using more than one server, the iris.Router is compatible with the net/http/Handler function therefore, as you can understand, it can be used to be adapted at any net/http server, however there is an easier way, by using the app.NewHost which is also copying all the host configurators and it closes all the hosts attached to the particular web app on app.Shutdown.

app := iris.New()
app.Get("/", indexHandler)

// run in different goroutine in order to not block the main "goroutine".
go app.Listen(":8080")
// start a second server which is listening on tcp 0.0.0.0:9090,
// without "go" keyword because we want to block at the last server-run.
app.NewHost(&http.Server{Addr:":9090"}).ListenAndServe()

Shutdown (Gracefully)

Let's continue by learning how to catch CONTROL+C/COMMAND+C or unix kill command and shutdown the server gracefully.

Gracefully Shutdown on CONTROL+C/COMMAND+C or when kill command sent is ENABLED BY-DEFAULT.

In order to manually manage what to do when app is interrupted, we have to disable the default behavior with the option WithoutInterruptHandler and register a new interrupt handler (globally, across all possible hosts).

Example code:

package main

import (
    "context"
    "time"

    "github.com/kataras/iris/v12"
)


func main() {
    app := iris.New()

    iris.RegisterOnInterrupt(func() {
        timeout := 5 * time.Second
        ctx, cancel := context.WithTimeout(context.Background(), timeout)
        defer cancel()
        // close all hosts
        app.Shutdown(ctx)
    })

    app.Get("/", func(ctx iris.Context) {
        ctx.HTML(" <h1>hi, I just exist in order to see if the server is closed</h1>")
    })

    app.Listen(":8080", iris.WithoutInterruptHandler)
}

Last updated