Repository: istio/fortio (go)

Issue:
Setting Headers (-H) causes panic
Run Fortio in `server` mode:
```
$ docker run --name fortio --rm -p 8080:8080 -p 8082:8082 -p 8079:8079 istio/fortio:0.7.0 server -loglevel verbose -redirect-port 8082
Fortio 0.7.0 grpc ping server listening on port :8079
UI starting - visit:
http://localhost:8080/fortio/   (or any host/ip reachable on this server)
Https redirector running on :8082
19:55:58 I logger.go:97> Log level is now 1 Verbose (was 2 Info)
19:55:58 V stats.go:491> Will use [50 75 90 99 99.9] for percentiles
19:55:58 V uihandler.go:92> Using resources directory set at link time: /usr/local/lib/fortio
Fortio 0.7.0 echo server listening on port :8080
```

Use `curl` or `load` with `-H`:
```
$ docker run --name client --rm istio/fortio:0.7.0 load -loglevel verbose -H Foo:Bar -curl http://172.17.0.2:8080/debug
19:56:58 I logger.go:97> Log level is now 1 Verbose (was 2 Info)
19:56:58 V http.go:172> Setting regular extra header Foo: Bar
panic: assignment to entry in nil map

goroutine 1 [running]:
net/textproto.MIMEHeader.Add(0x0, 0x7ffe4edfff3b, 0x3, 0x7ffe4edfff3f, 0x3)
	/usr/local/go/src/net/textproto/header.go:15 +0xf7
net/http.Header.Add(0x0, 0x7ffe4edfff3b, 0x3, 0x7ffe4edfff3f, 0x3)
	/usr/local/go/src/net/http/header.go:24 +0x53
istio.io/fortio/fhttp.(*HTTPOptions).AddAndValidateExtraHeader(0xbd9c20, 0x7ffe4edfff3b, 0x7, 0xc420068301, 0x0)
	/go/src/istio.io/fortio/fhttp/http.go:173 +0x35d
main.(*flagList).Set(0xbf6200, 0x7ffe4edfff3b, 0x7, 0x0, 0x0)
	/go/src/istio.io/fortio/fortio_main.go:49 +0x41
flag.(*FlagSet).parseOne(0xc420064060, 0x1, 0x0, 0x0)
	/usr/local/go/src/flag/flag.go:894 +0x3c2
flag.(*FlagSet).Parse(0xc420064060, 0xc4200f6320, 0x6, 0x6, 0x1, 0xc420142220)
	/usr/local/go/src/flag/flag.go:913 +0x60
flag.Parse()
	/usr/local/go/src/flag/flag.go:941 +0x72
main.main()
	/go/src/istio.io/fortio/fortio_main.go:140 +0x2dc

$ docker run --name client --rm istio/fortio:0.7.0 curl -loglevel verbose -H Foo:Bar -curl http://172.17.0.2:8080/debug
19:56:44 I logger.go:97> Log level is now 1 Verbose (was 2 Info)
19:56:44 V http.go:172> Setting regular extra header Foo: Bar
panic: assignment to entry in nil map

goroutine 1 [running]:
net/textproto.MIMEHeader.Add(0x0, 0x7ffe0e225f3b, 0x3, 0x7ffe0e225f3f, 0x3)
	/usr/local/go/src/net/textproto/header.go:15 +0xf7
net/http.Header.Add(0x0, 0x7ffe0e225f3b, 0x3, 0x7ffe0e225f3f, 0x3)
	/usr/local/go/src/net/http/header.go:24 +0x53
istio.io/fortio/fhttp.(*HTTPOptions).AddAndValidateExtraHeader(0xbd9c20, 0x7ffe0e225f3b, 0x7, 0xc420068701, 0x0)
	/go/src/istio.io/fortio/fhttp/http.go:173 +0x35d
main.(*flagList).Set(0xbf6200, 0x7ffe0e225f3b, 0x7, 0x0, 0x0)
	/go/src/istio.io/fortio/fortio_main.go:49 +0x41
flag.(*FlagSet).parseOne(0xc420064060, 0x1, 0x0, 0x0)
	/usr/local/go/src/flag/flag.go:894 +0x3c2
flag.(*FlagSet).Parse(0xc420064060, 0xc4200f6320, 0x6, 0x6, 0x1, 0xc420142220)
	/usr/local/go/src/flag/flag.go:913 +0x60
flag.Parse()
	/usr/local/go/src/flag/flag.go:941 +0x72
main.main()
	/go/src/istio.io/fortio/fortio_main.go:140 +0x2dc
```
It appears `fhttp.HTTPOptions` is not being initialized before calling `AddAndValidateExtraHeader`. I will submit a PR to fix the issue.

Pull request description:
Fix for headers not initialized panic
Fixes #148
Replacement/code review for #149 

Made the test change first, which were both crashing, then the fix, both pass


Patch:
diff --git a/fhttp/http.go b/fhttp/http.go
index 35b7595..e370f93 100644
--- a/fhttp/http.go
+++ b/fhttp/http.go
@@ -159,6 +159,7 @@ func (h *HTTPOptions) GetHeaders() http.Header {
 
 // AddAndValidateExtraHeader collects extra headers (see main.go for example).
 func (h *HTTPOptions) AddAndValidateExtraHeader(hdr string) error {
+	h.Init(h.URL)
 	s := strings.SplitN(hdr, ":", 2)
 	if len(s) != 2 {
 		return fmt.Errorf("invalid extra header '%s', expecting Key: Value", hdr)


Test patch:
diff --git a/Webtest.sh b/Webtest.sh
index 8ce9a27..e5852c1 100755
--- a/Webtest.sh
+++ b/Webtest.sh
@@ -17,8 +17,8 @@ docker ps
 BASE_URL="http://localhost:8080"
 BASE_FORTIO="$BASE_URL$FORTIO_UI_PREFIX"
 CURL="docker exec fortio_server /usr/local/bin/fortio load -curl -loglevel $LOGLEVEL"
-# Check https works (certs are in the image)
-$CURL -stdclient https://istio.io/robots.txt
+# Check https works (certs are in the image) - also tests autoswitch to std client for https
+$CURL https://istio.io/robots.txt
 # Check that browse doesn't 404s
 $CURL ${BASE_FORTIO}browse
 # Check we can connect, and run a QPS test against ourselves through fetch
@@ -40,8 +40,8 @@ fi
 $CURL $BASE_FORTIO
 # Do a small load using std client
 docker exec fortio_server /usr/local/bin/fortio load -stdclient -qps 1 -t 2s -c 1 https://www.google.com/
-# and with normal
-docker exec fortio_server /usr/local/bin/fortio load -qps 1 -t 2s -c 2 http://www.google.com/
+# and with normal and with custom headers
+docker exec fortio_server /usr/local/bin/fortio load -H Foo:Bar -H Blah:Blah -qps 1 -t 2s -c 2 http://www.google.com/
 # Do a grpcping
 docker exec fortio_server /usr/local/bin/fortio grpcping localhost
 # TODO: check report mode
diff --git a/fhttp/http_test.go b/fhttp/http_test.go
index 93fca2f..ab0ee41 100644
--- a/fhttp/http_test.go
+++ b/fhttp/http_test.go
@@ -31,7 +31,7 @@ func init() {
 }
 
 func TestGetHeaders(t *testing.T) {
-	o := NewHTTPOptions("")
+	o := &HTTPOptions{}
 	o.AddAndValidateExtraHeader("FOo:baR")
 	oo := *o // check that copying works
 	h := oo.GetHeaders()