[Nginx][Go] Configurations
Tags: go, nginx
Published: 2022-08-23 17:41:12

## Intro
This time, I will try loading configuration data on my Go project and adding more properties into nginx.conf.

* [Xubuntu + Nginx + Go(+ Pion/WebRTC) + Node.js](https://dev.to/masanori_msl/xubuntu-nginx-go-pionwebrtc-nodejs-3lgl)

### Examples
* [webappsample - GitHub](https://github.com/masanori840816/webappsample/tree/3c37ad7c474848c1abd09601e0be2bdd3f3b9830)

## Loading configuration data on Go projects
Last time, I changed the URL of my Go project to access through Nginx.
One problem is that this URL isn't same in all environments.

So I want to get the URL from external configuration data.

### Loading files
I can save configuration data as a configuration file at "$HOME/.config"("%APPDATA%" for Windows).
But because I just wanted to load constant texts from an external file in this time, so I decided putting it into my project directory.

#### appsettings.json
```json
{
    "url": "https://192.168.XX.YYY:443/webrtc"
}
```

#### appsettings.go
```go
package main

import (
	"encoding/json"
	"fmt"
	"os"
)

type AppSettings struct {
	URL string `json:"url"`
}

func LoadAppSettings() (setting AppSettings, err error) {
	result := &AppSettings{}
	cur, _ := os.Getwd()
	file, err := os.Open(fmt.Sprintf("%s/appsettings.json", cur))
	if err != nil {
		return *result, err
	}
	fileInfo, err := file.Stat()
	if err != nil {
		return *result, err
	}
	// Read the config file
	fileData := make([]byte, fileInfo.Size())
	_, err = file.Read(fileData)
	if err != nil {
		return *result, err
	}
	err = json.Unmarshal(fileData, &result)
	return *result, err
}
```

#### main.go
```go
...

func main() {
	settings, _ := LoadAppSettings()
	target := getStrippingTargetPrefix(settings.URL)

	hub := *newSSEHub()
	go hub.run()

	if len(target) > 0 {
		http.Handle(fmt.Sprintf("/%s/css/", target), http.StripPrefix(fmt.Sprintf("/%s", target), http.FileServer(http.Dir("templates"))))
		http.Handle(fmt.Sprintf("/%s/js/", target), http.StripPrefix(fmt.Sprintf("/%s", target), http.FileServer(http.Dir("templates"))))

		http.HandleFunc(fmt.Sprintf("/%s/sse/message", target), func(w http.ResponseWriter, r *http.Request) {
			sendSSEMessage(w, r, &hub)
		})
		http.HandleFunc(fmt.Sprintf("/%s/sse/", target), func(w http.ResponseWriter, r *http.Request) {
			registerSSEClient(w, r, &hub)
		})
	} else {
		http.Handle("/css/", http.FileServer(http.Dir("templates")))
		http.Handle("/js/", http.FileServer(http.Dir("templates")))

		http.HandleFunc("/sse/message", func(w http.ResponseWriter, r *http.Request) {
			sendSSEMessage(w, r, &hub)
		})
		http.HandleFunc("/sse/", func(w http.ResponseWriter, r *http.Request) {
			registerSSEClient(w, r, &hub)
		})
	}
	http.Handle("/", &templateHandler{filename: "index.html", serverUrl: settings.URL})
	log.Fatal(http.ListenAndServe("localhost:8080", nil))
}
func getStrippingTargetPrefix(url string) string {
	sURL := strings.Split(url, "/")
	if len(sURL) <= 3 {
		return ""
	}
	for i := len(sURL) - 1; i >= 3; i-- {
		if sURL[i] != "" {
			return sURL[i]
		}
	}
	return ""
}
```

* [How to get file length in Go? - StackOverflow](https://stackoverflow.com/questions/17133590/how-to-get-file-length-in-go)

### Loading environment variables

#### appsettings.go
```go
package main

import (
	"os"
)

type AppSettings struct {
	URL string `json:"url"`
}

func LoadAppSettings() (setting AppSettings, err error) {
	result := &AppSettings{}
	result.URL = os.Getenv("WEBRTCAPP_URL")
	if len(result.URL) <= 0 {
		result.URL = "http://localhost:8080"
	}
	return *result, err
}
```

## Add more Nginx configurations
### Timeout
Last time, I added some properties into the nginx.conf to use SSE and reverse proxy.
I noticed that the application established an SSE connection but automatically disconnected after one minute.

So I added a property to avoid timeout. 

And I split the configurations for my Go project into "/etc/nginx/conf.d/webrtcapp.conf".

#### webrtcapp.conf
```conf
server {
    listen 443 ssl;
    server_name 192.168.XX.YYY;
    proxy_buffering off;
    proxy_cache off;
    proxy_set_header Connection '';
    proxy_http_version 1.1;
    chunked_transfer_encoding off;
    # The connection times out after 24 hours.
    proxy_read_timeout 24h;

    ssl_certificate /home/example/local_cert.pem;
    ssl_certificate_key /home/example/local_key.pem;
    location /webrtc {

        proxy_pass http://localhost:8080;
    }
}
```

### CORS
I can add CORS headers in the Go project like I tried before.

* [[Go][Windows] Try WebView2 and CORS](https://dev.to/masanori_msl/gowindows-try-webview2-and-cors-3n1o)

But I also can do this with Nginx.

#### webrtcapp.conf
```conf
map $http_origin $cors {
    'http://localhost:8080' $http_origin;
    'https://192.168.XX.YYY:443' $http_origin;
}
map $request_method $cors_method {
  OPTIONS 11;
  GET  1;
  POST 1; 
  default 0;
}

server {
...
    location /webrtc {
        if ($cors_method ~ '1') {
            add_header 'Access-Control-Allow-Origin' $cors;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
            add_header 'Access-Control-Allow-Headers' '*';
        }
        if ($cors_method = '11') {
            add_header 'Access-Control-Max-Age' 1728000;
            add_header 'Content-Type' 'text/plain; charset=UTF-8';
            add_header 'Content-Length' 0;
            return 204;
        }
        proxy_pass http://localhost:8080;
    }
}
```

* [Core functionality - Nginx](http://nginx.org/en/docs/ngx_core_module.html)
* [Module ngx_http_proxy_module - Nginx](http://nginx.org/en/docs/http/ngx_http_proxy_module.html)
* [Module ngx_http_core_module - Nginx](http://nginx.org/en/docs/http/ngx_http_core_module.html)
* [NGINX Cookbook, 2nd Edition](https://www.oreilly.com/library/view/nginx-cookbook-2nd/9781098126230/)
* [Nginxで複数Originを許可するCORS設定例](https://qiita.com/rmanzoku/items/5b5ce141c709a9deeb33)
* [How to allow access via CORS to multiple domains within nginx - StackOverflow](https://stackoverflow.com/questions/36582199/how-to-allow-access-via-cors-to-multiple-domains-within-nginx)