Grokking Go - A collection of Go recipes and code snippets to enhance applications
Go code snippets that I've found useful in the development of the applications that I build.
Table of Contents
↑Preventing Go following redirects when using http.PostForm
Consider the following example below. Staff data is submitted to an endpoint using http.PostFrom in its standard format. The endpoint returns a 302 HTTP header and you need to grab the location of the URL returned in the HTTP response, so that you can use the URL in and IFRAME HTML tag. As it stands, the code below will follow the redirect and the response body will contain the HTML from the redirected URL.
1staff := url.Values{
2 "username": {s.Staff.Email},
3 "role": {s.Staff.Role},
4 "organisation": {s.Organisation.ODS},
5}
6
7response, err := http.PostForm("https://example.org/endpoint/", staff)
8if err != nil {
9 log.Fatal("WARN: " + err.Error())
10}
Well you can prevent/override the default behavior by defining your own http.Client and setting the CheckRedirect to a function which returns the last HTTP response error. You can then check the response status code and if it equals 302 then you can grab the Location header in the response to fetch the new URL address.
1staff := url.Values{
2 "username": {s.Staff.Email},
3 "role": {s.Staff.Role},
4 "organisation": {s.Organisation.ODS},
5}
6
7client := &http.Client{
8 Timeout: 5 * time.Second,
9 CheckRedirect: func(req *http.Request, via []*http.Request) error {
10 return http.ErrUseLastResponse
11 },
12}
13
14response, err := client.PostForm("https://example.org/endpoint/", staff)
15if err != nil {
16 log.Fatal("WARN: " + err.Error())
17}
18
19if response.StatusCode == 302 {
20 fmt.Println(response.Header.Get("Location"))
21}
↑Making a Go HTTP client proxy aware
One of my web applications after being installed at a local county council, started failing. The HTTP client code which called an API endpoint was failing because the council used proxy servers and my code wasn’t proxy aware. It turned out to be an easy fix. I needed to check for the presence of an environment variable called HTTP_PROXY. If the environment variable was present, then I needed to parse the value using the url standard library and check that the format was valid.
Below is the code snippet I cam up with. If the parsed proxy url is valid, then we can pass it into the Proxy struct of the http.Transport method.
1// define an HTTP client with a sensible
2// timeout value for the API service
3var migClient = &http.Client{
4 Timeout: 10 * time.Second,
5}
6
7// check if the proxy environment
8// variable is present and use it
9if len(os.Getenv("HTTPS_PROXY")) > 0 {
10
11 proxyURL, err := url.Parse(os.Getenv("HTTPS_PROXY"))
12 if err != nil {
13 log.Println("WARN: ERROR PARSING PROXY URL : " + err.Error())
14 } else {
15 migClient.Transport = &http.Transport{
16 TLSClientConfig: cfg,
17 IdleConnTimeout: 10 * time.Second,
18 ResponseHeaderTimeout: 10 * time.Second,
19 Proxy: http.ProxyURL(proxyURL),
20 }
21 }
22}
If your only writing software for your company, where you’re in control of the whole infrastructure, then this is probably not needed. However, if your writing software that will be used in organisations where you don’t control the server stack; then take the extra care to ensure your HTTP clients are proxy aware.
↑Query DNS TXT records using the Go standard library
While working on a Windows service written in Go I needed a way of changing the execution state remotely. The service would be installed on computers within a secure Covid testing lab, and hence the configuration needed to be changed remotely to remove the need for visiting the laboratory.
Initially, the software was installed and configured to use the central development system. However, at some point, we needed to flip the switch and make the software run in production mode.
I realised I could set the run mode within a DNS TXT record for the applications domain name.
The application then periodically runs the lookupResultsModeFromDNS() function shown below to query the TXT records using net.LookupTXT. After capturing all the TXT records, the function iterates over the results finding the runmode entry. Using the value of the runmode we set the environment boolean, which is used to control how the windows service connects to the backend.
1func (app *App) lookupResultsModeFromDNS() {
2
3 txtrecords, err := net.LookupTXT("domain.app")
4 if err != nil {
5 log.Fatal(err.Error())
6 }
7
8 for _, txt := range txtrecords {
9 if strings.Contains(txt, "runmode=") {
10 mode := strings.Replace(txt, "runmode=", "", -1)
11
12 switch mode {
13 case "dev":
14 app.testEnvironment = true
15 case "prod":
16 app.testEnvironment = false
17 default:
18 app.testEnvironment = true
19 }
20 }
21 }
22}
↑Check if a Go program is running within a Docker container
Over the last couple of months I’ve been migrating a Go application into Docker containers. If the Go program is running within a Docker container, as opposed to my local development machine, I’d set-up different paths to the logging files and configuration files. So I needed a way to detect if the running instance of the program is actually running within a Docker container. Luckily, Docker creates a .dockerenv file within the root folder of the container. All I needed to do was check for the existence of this file.
1func (app *App) isRunningInDockerContainer() bool {
2 // docker creates a .dockerenv file at the root
3 // of the directory tree inside the container.
4 // if this file exists then the program is running
5 // from inside a container so return true
6
7 if _, err := os.Stat("/.dockerenv"); err == nil {
8 return true
9 }
10
11 return false
12}
Above is the function I use to check if the program is running within a Docker container. I use the return value from this function along with checking if a known mount point is also accessible.