Gotcha: Segfault when HTTP headers include newlines with Go

Featured image for sharing metadata for article

I'm writing a Go program to allow me to upload images to my Micropub server more easily from the command-line.

As part of this, I wanted to store my Micropub access token in a file, ~/.secrets/micropub.txt, and read it at runtime.

The code is roughly the same as below:

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

func main() {
	content, err := ioutil.ReadFile(os.ExpandEnv("$HOME/.secrets/micropub.txt"))
	if err != nil {
		log.Fatal(err)
	}

	token := string(content)
	call("http://localhost:8080/micropub", token)
}

func call(urlPath string, accessToken string) {
	client := &http.Client{}

	req, err := http.NewRequest("GET", urlPath, nil)
	if err != nil {
		panic(err)
	}

	req.Header.Set("Accept", "application/json")
	req.Header.Set("Authorization", "Bearer "+accessToken)
	rsp, _ := client.Do(req)
	if rsp.StatusCode != http.StatusOK {
		log.Println(rsp)
		log.Printf("Request failed with response code: %d", rsp.StatusCode)
	}
}

What was very odd is that when executing it, I was receiving the following segfault:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x10 pc=0x5fc31d]

goroutine 1 [running]:
main.call({0x663a2c, 0x24}, {0xc00004ff38, 0x4})
	/home/jamie/workspaces/tmp/media-upload/main.go:31 +0x23d
main.main()
	/home/jamie/workspaces/tmp/media-upload/main.go:17 +0x9d
exit status 2

Those of you more clued up with Go may have spotted that I wasn't handling the err from client.Do, which if I was, it'd have told me:

panic: Get "http://localhost:8080/micropub/media": net/http: invalid header field value "Bearer eyJ...\n" for key Authorization

goroutine 1 [running]:
main.call({0x663a2c, 0x24}, {0xc00004ff38, 0x4})
        /home/jamie/workspaces/tmp/media-upload/main.go:32 +0x307
main.main()
        /home/jamie/workspaces/tmp/media-upload/main.go:17 +0x9d
exit status 2

The solution here is that we need to make sure we trim the newline from the end of the string:

 import (
 	"log"
 	"net/http"
 	"os"
+	"strings"
 )

 func main() {
 		log.Fatal(err)
 	}

-	token := string(content)
+	token := strings.TrimSuffix(string(content), "\n")
 	call("http://localhost:8080/micropub/media", token)
 }

As writing this post, I wonder if it's actually worth documenting, as it seems pretty standard and expected, but I thought I may as well hit publish as I'd gone to the effort of writing it up!

Written by Jamie Tanna's profile image Jamie Tanna on , and last updated on .

Content for this article is shared under the terms of the Creative Commons Attribution Non Commercial Share Alike 4.0 International, and code is shared under the Apache License 2.0.

#blogumentation #go.

Also on:

This post was filed under articles.

Interactions with this post

Interactions with this post

Below you can find the interactions that this page has had using WebMention.

Have you written a response to this post? Let me know the URL:

Do you not have a website set up with WebMention capabilities? You can use Comment Parade.