jbowes/httpsig

Body length 0 error with basic example

Opened this issue · 1 comments

eest commented

Hello,

Trying out the library I looked at the example in the README and came up with this as a basic example for using a POST request:

package main

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/http/httptest"

	"github.com/jbowes/httpsig"
)

func main() {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Printf("%#v\n", r.Header)
		body, _ := io.ReadAll(r.Body)
		fmt.Fprintf(w, "Hello, client: %s", body)
	}))
	defer ts.Close()

	privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		log.Fatal(err)
	}

	//client := http.Client{}
	client := http.Client{
		// Wrap the transport:
		Transport: httpsig.NewSignTransport(http.DefaultTransport,
			httpsig.WithSignEcdsaP256Sha256("key1", privKey)),
	}

	data := map[string]string{
		"hello": "world",
	}

	b, err := json.Marshal(data)
	if err != nil {
		log.Fatal(err)
	}

	buf := bytes.NewReader(b)

	resp, err := client.Post(ts.URL, "application/json", buf)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(body))
}

Trying to run this results in the following output:

go run .
2023/10/23 23:21:20 Post "http://127.0.0.1:55020": http: ContentLength=17 with Body length 0
exit status 1

I think the problem is that

r.Body = io.NopCloser(bytes.NewReader(b.Bytes()))
... should be setting nr.Body, not r.Body. I think the reason for this is the function returns transport.RoundTrip(nr) which at that point holds a reference to the original (and at that point consumed) r.Body.

eest commented

... with the change:

go run .
http.Header{"Accept-Encoding":[]string{"gzip"}, "Content-Length":[]string{"17"}, "Content-Type":[]string{"application/json"}, "Digest":[]string{"id-sha256=k6I5cakU5erL8KjSUVTNownDwccvu5kU1Hxg88toFYg="}, "Signature":[]string{"sig1=:MEQCICLBWjGdG6uD+oOmi4ckEymseSh7T+FQgK0duF/ykISGAiAn1DSEtGQrPEMKqDBVacUSFkf/LLfA5JE1QiIFW/QgGQ==:"}, "Signature-Input":[]string{"sig1=(\"@method\" \"@path\" \"@query\" \"digest\" \"content-type\");created=1698098941;keyid=\"key1\";alg=\"ecdsa-p256-sha256\""}, "User-Agent":[]string{"Go-http-client/1.1"}}
Hello, client: {"hello":"world"}

I can open a PR for this if you agree with my solution.