Skip to content

Writing basic HTTP Server on Golang using Servion Framework

The first basic http server on servion framework in golang

Today, we will try to build the first server application on servion framework. This framework was build to simplify bootstrapping of the web application, having CLI (Command Line Interface) in place and provide DI (Dependency Injection) capabilities from day one.

Having those things combined in simple from in golang has a certain value.

You do not need to start from the scratch and re-invent the wheel, instead, you can use robust and flexible framework to achieve your goals.

Let’s create the first application.

We can start from dependencies. In this page select the latest version of the servion and modify the go.mod file.

module github.com/yourhandle/yourprojectgo 1.23.0toolchain go1.24.1require ( github.com/gorilla/websocket v1.5.3 go.arpabet.com/cligo v0.1.6 go.arpabet.com/glue v1.2.4 go.arpabet.com/servion v0.1.2)

Then run the command to update the dependencies

go mod tidy

Now, we are ready to create main.go file

package mainimport ( "go.arpabet.com/cligo" "go.arpabet.com/glue" "go.arpabet.com/servion")func main() { properties := glue.MapPropertySource{  "http-server.bind-address": "0.0.0.0:8000", } beans := []interface{}{  properties,  servion.RunCommand(servion.HttpServerScanner("http-server")),  servion.ZapLogFactory(), } cligo.Main(cligo.Beans(beans...))}

In this simple example we are creating application context containing three beans:

  • properties with host and port
  • RunCommand instance to provide simple CLI with “run” command.
  • Zap Logger

The Zap Logger is using by default in servion since it is provides JSON logging output, that is good fit for any cloud hosting.

The HTTP server is located in RunCommand child context, so it would not be created for other commands if we add to the server. This is considered as a good practice.

Creation of the HTTP server is based on bean name, that is in our case “http-server”. The same bean name we are using in properties.

Each HTTP server requires as minimum “bind-address” property to correctly start listening the port.

Let’s build the project

go build

And run it

./basic run2025-03-26T14:39:01.422-0700    INFO    [email protected]/http_server_factory.go:93        HTTPServerFactory       {"listenAddr": "0.0.0.0:8000", "bean": "http-server", "handlers": [], "assets": [], "options": {}, "tls": false}2025-03-26T14:39:01.422-0700    INFO    [email protected]/utils.go:166     ServionStarted  {"Servers": 1}2025-03-26T14:39:01.423-0700    INFO    [email protected]/http_server.go:105       HttpServerServe {"addr": "0.0.0.0:8000", "network": "tcp", "tls": false}

As we can see our server is started, to stop is press Ctrl-C in terminal

^C2025-03-26T14:39:03.468-0700  INFO    [email protected]/utils.go:193     StopSignal      {"signal": "interrupt"}2025-03-26T14:39:03.468-0700    INFO    [email protected]/http_server.go:66        HttpServerShutdown      {"addr": "0.0.0.0:8000", "network": "tcp"}2025-03-26T14:39:03.468-0700    INFO    [email protected]/run_command.go:69        RunServers      {"restarting": false}

What we learn from the logs, there is a restarting mode of the server, that would close child context and re-create it again to restart all servers without stopping the application. This functionality designed in case if you do not want to kill the pod in Kubernetes cluster, but at the same time need to reload some servers, for example update the secrets or credentials, tls certificates and so on.

To restart the server user this command.

kill -1 <pid>

Now we are ready to modify this server a little bit.

Let’s add additional HTTP Server to the servion.

func main() { properties := &glue.PropertySource{Map: map[string]interface{}{  "web-server.bind-address": "0.0.0.0:8000",  "cdr-server.bind-address": "0.0.0.0:8001", }} beans := []interface{}{  properties,  servion.RunCommand(servion.HttpServerScanner("web-server"),   servion.HttpServerScanner("cdr-server")),  servion.ZapLogFactory(), } cligo.Main(cligo.Beans(beans...))}

Now we have two servers: web-server and cdr-server. They both are sharing the same child context, root properties and root logger.

Let’s build the project

go build

And run

 ./two_servers run2025-03-26T17:04:10.144-0700    INFO    [email protected]/http_server_factory.go:93        HTTPServerFactory       {"listenAddr": "0.0.0.0:8000", "bean": "web-server", "handlers": [], "assets": [], "options": {}, "tls": false}2025-03-26T17:04:10.144-0700    INFO    [email protected]/http_server_factory.go:93        HTTPServerFactory       {"listenAddr": "0.0.0.0:8001", "bean": "cdr-server", "handlers": [], "assets": [], "options": {}, "tls": false}2025-03-26T17:04:10.144-0700    INFO    [email protected]/utils.go:166     ServionStarted  {"Servers": 2}2025-03-26T17:04:10.144-0700    INFO    [email protected]/http_server.go:105       HttpServerServe {"addr": "0.0.0.0:8001", "network": "tcp", "tls": false}2025-03-26T17:04:10.145-0700    INFO    [email protected]/http_server.go:105       HttpServerServe {"addr": "0.0.0.0:8000", "network": "tcp", "tls": false}

Now we see that both servers are started and also demonstrated that servion can have multiple servers in one context.

Web-sockets

Additionally, the servion framework supports web-sockets indirectly. It provides the flexible interface of initialization of http server, that look up only the HttpHandler. Inside HttpHandler the websocket could be initialized.

Let’s create the structure for the handler

type implWebsocketHandler struct { upgrader *websocket.Upgrader}

Inside this structure we have additional private field “upgrader”.

This field is using to upgrade the connection to the web-socket inside the ServerHTTP method.

Let’s create the constructor

func WebsocketHander() servion.HttpHandler { return &implWebsocketHandler{  upgrader: &websocket.Upgrader{   CheckOrigin: func(r *http.Request) bool {    return true // allow all origins for now   },  }, }}

In the constructor we need to initialize the “upgrader” and provide the “CheckOrigin” function. This is needed because web-sockets by default could be used from any page. Literally, web-sockets are ideal technology to distribute some functionality by SaaS.

And finally, we need to implement the method for websocket handler

func (t *implWebsocketHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { conn, err := t.upgrader.Upgrade(writer, request, nil) if err != nil {  log.Printf("[WebSocket] Error upgrading connection: %v", err)  return } defer conn.Close() log.Println("Client connected") for {  // Read message from client  _, msg, err := conn.ReadMessage()  if err != nil {   log.Println("Read error:", err)   break  }  log.Println("Received:", string(msg))  // Echo message back to client  err = conn.WriteMessage(websocket.TextMessage, msg)  if err != nil {   fmt.Println("Write error:", err)   break  } } log.Println("Client disconnected")}

Please, so not forget to close the connection by “defer conn.Close()”.

Additionally, we need to provide the Pattern for the handler:

func (t *implWebsocketHandler) Pattern() string { return "/v1/examples/websocket"}

Since we are building our application by components, we can not have dedicated code for the mux, we would define pattern in each component separately. In this case this code became reusable, we can package it separately and ship to another applications.

And in order this WebsocketHandler to be used we need to modify our main.go

func main() { properties := glue.MapPropertySource{  "http-server.bind-address": "0.0.0.0:8000",  "http-server.options":      "handlers", } beans := []interface{}{  properties,  servion.RunCommand(servion.HttpServerScanner("http-server"), WebsocketHander()),  servion.ZapLogFactory(), } cligo.Main(cligo.Beans(beans...))}

Additional setting “<bean_name>.options” added to the property. This option is responsible for the enabling certain options in the HttpServerFactory.

Options:

  • “handlers” — it would lookup in context classes HttpHandler and combine them in “mux”
  • “assets” — it would lookup in context resources having name with suffix “asset” and use their http.FileSystem to serve static content
  • “tls” — it would lookup in context *tls.Config instance and use it for TLS connection

This framework was created to provide the simple way of building micro-services in Kubernetes and Docker, having minimum functionality only needed for this purpose. Support popular protocols and being lightweight.

Servion examples: https://github.com/arpabet/servion-examples

Servion Vue Vite example: https://github.com/arpabet/servion-vue-example

Last updated:

Deep Learning · Algorithms · Engineering