在两个函数之间发送`Reader`

时间:2014-10-04 06:01:52

标签: io go

我尝试发出GET请求,然后将我收到的内容发送到POST请求中的其他网站。以下代码工作正常:

func deliver(sourceURL *url.URL, destinationURL *url.URL) {
    client := &http.Client{}
    sourceResponse, err := http.Get(sourceURL.String())
    if err == nil {
        body, _ := ioutil.ReadAll(sourceResponse.Body)
        client.Post(destinationURL.String(), "text/html", strings.NewReader(string(body)))
    }
}

但这会将整个响应读入内存 - 如何将响应流式传输到POST请求中?这是我正在尝试的内容:

func deliver(sourceURL *url.URL, destinationURL *url.URL) {
    client := &http.Client{}
    sourceResponse, err := http.Get(sourceURL.String())
    if err == nil {
        client.Post(destinationURL.String(), "text/html", sourceResponse.Body)
    }
}

我认为这会起作用,但它只发送一个空的请求(没有正文)。为什么是这样? client.Post函数是否真的尝试读取响应?

这是Go游乐场的一个例子:http://play.golang.org/p/_EO80aj6wr 发送dataReader代替sourceResponse.Body效果很好,但直接发送sourceResponse.Body并不起作用。

示例链接:

http://localhost:3000/fetch?dest=http://requestb.in/177ji621&src=http://www.google.com
http://requestb.in/177ji621?inspect

1 个答案:

答案 0 :(得分:2)

我做了一个小测试程序试试这个

package main

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

var client = &http.Client{}

func httpd() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        log.Printf("%s Request from %s", r.Method, r.RemoteAddr)
        if r.Method == "GET" {
            fmt.Fprintf(w, "Here is a GET body")
            return
        }
        body, err := ioutil.ReadAll(r.Body)
        if err != nil {
            log.Fatalf("body read failed: %v", err)
        }
        log.Printf("Body received from %s: %q", r.Method, body)
    })
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func deliver(sourceURL *url.URL, destinationURL *url.URL) {
    sourceResponse, err := http.Get(sourceURL.String())
    if err != nil {
        log.Fatalf("post failed: %v", err)
    }
    postResponse, err := client.Post(destinationURL.String(), "text/html", sourceResponse.Body)
    if err != nil {
        log.Fatalf("post failed: %v", err)
    }
    postResponse.Body.Close()
    sourceResponse.Body.Close()
}

func main() {
    go httpd()
    src, err := url.Parse("http://127.0.0.1:8080/")
    if err != nil {
        log.Fatalf("src parse failed: %v", err)
    }
    dst, err := url.Parse("http://127.0.0.1:8080/")
    if err != nil {
        log.Fatalf("dst parse failed: %v", err)
    }
    deliver(src, dst)
}

它工作正常,产生了这个输出

2014/10/04 08:40:26 GET Request from 127.0.0.1:48910
2014/10/04 08:40:26 POST Request from 127.0.0.1:48911
2014/10/04 08:40:26 Body received from POST: "Here is a GET body"

因此,您的代码问题可能会被您未检查的错误所揭示 - 最有可能发布的错误。

另请注意,如果可以,您希望重新使用http.Client,以便获得连接池。


在审核更新问题后

更新

我用http://requestb.in/尝试了我的测试程序,它没有显示身体。但是我确实看到了Transfer-Encoding: chunked,所以我怀疑requestb.in不支持分块传输编码。

如果您将整个正文提供给请求,那么请设置Content-Length标题,但是if you provide a stream然后使用chunked transfer encoding。尽管分块传输编码是HTTP / 1.1规范的非可选部分,但仍有不少服务器不能正确支持它。