reusing Body in http.Requests (goproxy) Goproxy is a Go package implementing an HTTP proxy.
I am using it to store requests from a client (eg: a web browser) for further processing.

In an http.Request struct, the Body is an io.ReadCloser, it can only be read once.

The problem I faced is that I need to read the Body to store it, but still make it accessible to goproxy so that it can be sent to the server.

I solved it by reading the body with ioutil.ReadAll, and then creating a   ReadCloser with the byte slice I got. I then assign this ReadCloser to the request Body. 

package main

import (
    "bytes"
    "flag"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/url"
    "regexp"
    "sync"

    "github.com/elazarl/goproxy"
)

var requests map[string][]Request
var mutex sync.Mutex

type Request struct {
    Method        string
    URL           url.URL
    Proto         string // "HTTP/1.0"
    ProtoMajor    int    // 1
    ProtoMinor    int    // 0
    Header        http.Header
    Body          []byte
    ContentLength int64
    Host          string
    PostForm      url.Values
    RequestURI    string
}

type RequestBody struct {
    *bytes.Reader
}

func (r RequestBody) Close() error {
    return nil
}

func storeRequest(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        // only print error for now
        log.Println(err)
        return r, nil
    }
    r.Body.Close()
    r.Body = RequestBody{bytes.NewReader(body)}
    fmt.Println(string(body))
    mutex.Lock()
    requests[r.URL.Host] = append(requests[r.URL.Host], Request{
        Method:        r.Method,
        URL:           *r.URL,
        Proto:         r.Proto,
        ProtoMajor:    r.ProtoMajor,
        ProtoMinor:    r.ProtoMinor,
        Header:        r.Header,
        Body:          body,
        ContentLength: r.ContentLength,
        Host:          r.Host,
        PostForm:      r.PostForm,
        RequestURI:    r.RequestURI,
    })
    mutex.Unlock()
    return r, nil
}

func main() {
    proxy := goproxy.NewProxyHttpServer()
    proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*$"))).
        HandleConnect(goproxy.AlwaysMitm)
    proxy.OnRequest().DoFunc(storeRequest)

    requests = make(map[string][]Request)

    addr := flag.String("addr", ":8080", "proxy listen address")
    flag.Parse()
    log.Fatal(http.ListenAndServe(*addr, proxy))
}


I would like to know if this is a correct way of solving this problem.
It is fine to read the body and replace it, although see ioutil.NopCloser.

However, 


it does not make sense to duplicate the http.Request type. Re use it instead. If you need finer control, embed it.
take care to make true copy when passing around slices, maps and pointers.
the code is lacking structured factorization, make use of type definition


This is a revised version i suggest,

package main

import (
    "bytes"
    "flag"
    "io"
    "io/ioutil"
    "log"
    "net/http"
    "net/url"
    "regexp"
    "sync"

    "github.com/elazarl/goproxy"
)

type store struct {
    requests map[string][]http.Request
    sync.Mutex
}

func (s *store) Add(r *http.Request) {
    var c http.Request
    c.Method = r.Method
    c.Proto = r.Proto
    c.ProtoMajor = r.ProtoMajor
    c.ProtoMinor = r.ProtoMinor
    c.Host = r.Host
    c.RequestURI = r.RequestURI
    c.ContentLength = r.ContentLength
    c.URL = r.URL

    c.Header = http.Header{}
    for k, vv := range r.Header {
        for _, v := range vv {
            c.Header.Set(k, v)
        }
    }

    c.PostForm = url.Values{}
    for k, vv := range r.PostForm {
        for _, v := range vv {
            c.PostForm.Set(k, v)
        }
    }

    c.Form = url.Values{}
    for k, vv := range r.Form {
        for _, v := range vv {
            c.Form.Set(k, v)
        }
    }

    var b bytes.Buffer
    io.Copy(&amp;b, r.Body)
    r.Body.Close()
    buf := b.Bytes()

    var b1 bytes.Buffer
    b1.Write(buf)
    r.Body = ioutil.NopCloser(&amp;b1)

    c.Body = ioutil.NopCloser(bytes.NewBuffer(buf))

    key := r.URL.Host
    s.Lock()
    s.requests[key] = append(s.requests[key], c)
    s.Unlock()
}

type storeHandler struct {
    requests *store
}

func (s storeHandler) Handle(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
    s.requests.Add(r)
    return r, nil
}

func main() {
    h := storeHandler{requests: &amp;store{requests: map[string][]http.Request{}}}

    proxy := goproxy.NewProxyHttpServer()
    proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*$"))).
        HandleConnect(goproxy.AlwaysMitm)
    proxy.OnRequest().DoFunc(h.Handle)

    addr := flag.String("addr", ":8080", "proxy listen address")
    flag.Parse()
    log.Fatal(http.ListenAndServe(*addr, proxy))
}