Go HTTP Client添加了不受服务支持的分块编码

时间:2017-11-13 23:35:09

标签: go

go HTTP客户端正在向我的客户端请求添加“chunked”传输编码字段。不幸的是,我所连接的服务不支持这种情况,并且它返回时出现错误。

有没有办法禁用它?

这是我的请求代码:

// DoHTTPRequest Do a full HTTP Client Request, with timeout
func DoHTTPRequest(method, url string, body io.Reader, headers map[string]string, timeout time.Duration) (*http.Response, error) {

    // Create the request
    req, err := http.NewRequest(method, url, body)
    if err != nil {
        return nil, err
    }

    // Add headers
    for k, v := range headers {
        req.Header.Set(k, v)
    }

    client := &http.Client{
        Timeout: timeout,
    }

    return client.Do(req)
}

基本上我希望这个标题丢弃。这个客户端正在与S3交谈,这对于它发送的标头非常敏感。

我收到此错误:

A header you provided implies functionality that is not implemented

2 个答案:

答案 0 :(得分:2)

TransferEncoding是直接在Request结构上的字段。如果你明确地设置它,它将不会被覆盖。

req.TransferEncoding = []string{"identity"}

https://golang.org/pkg/net/http/#Request

答案 1 :(得分:2)

像这样设置Transfer Encoding标头不会使用chunked:

req.TransferEncoding = []string{"identity"}

然而,http客户端来源给出了在我的情况下选择chunked的原因。具体来说,我使用“PUT”作为方法,没有指定内容长度。所以我需要的是设置req.ContentLength。

但是,您可以看到我的DoHTTPRequest包装函数不知道设置它有多大。我曾假设设置标题会使其最初工作。好吧,它通过设置标题不起作用。你可以在决定是否使用分块编码的来源中看到原因。

// shouldSendChunkedRequestBody reports whether we should try to send a
// chunked request body to the server. In particular, the case we really
// want to prevent is sending a GET or other typically-bodyless request to a
// server with a chunked body when the body has zero bytes, since GETs with
// bodies (while acceptable according to specs), even zero-byte chunked
// bodies, are approximately never seen in the wild and confuse most
// servers. See Issue 18257, as one example.
//
// The only reason we'd send such a request is if the user set the Body to a
// non-nil value (say, ioutil.NopCloser(bytes.NewReader(nil))) and didn't
// set ContentLength, or NewRequest set it to -1 (unknown), so then we assume
// there's bytes to send.
//
// This code tries to read a byte from the Request.Body in such cases to see
// whether the body actually has content (super rare) or is actually just
// a non-nil content-less ReadCloser (the more common case). In that more
// common case, we act as if their Body were nil instead, and don't send
// a body.
func (t *transferWriter) shouldSendChunkedRequestBody() bool {
    // Note that t.ContentLength is the corrected content length
    // from rr.outgoingLength, so 0 actually means zero, not unknown.
    if t.ContentLength >= 0 || t.Body == nil { // redundant checks; caller did them
        return false
    }
    if requestMethodUsuallyLacksBody(t.Method) {
        // Only probe the Request.Body for GET/HEAD/DELETE/etc
        // requests, because it's only those types of requests
        // that confuse servers.
        t.probeRequestBody() // adjusts t.Body, t.ContentLength
        return t.Body != nil
    }
    // For all other request types (PUT, POST, PATCH, or anything
    // made-up we've never heard of), assume it's normal and the server
    // can deal with a chunked request body. Maybe we'll adjust this
    // later.
    return true
}

所以我的解决方案很简单:

// DoHTTPRequest Do a full HTTP Client Request, with timeout
func DoHTTPRequest(method, url string, body io.Reader, headers map[string]string, timeout time.Duration) (*http.Response, error) {

    // Create the request
    req, err := http.NewRequest(method, url, body)
    if err != nil {
        return nil, err
    }

    // Add headers
    for k, v := range headers {
        req.Header.Set(k, v)
        // Set the Content Length correctly if specified.
        if strings.EqualFold(k, "Content-Length") {
            length, err := strconv.Atoi(v)
            if err != nil {
               return nil, fmt.Errorf("Bad Content-Length header")
            }
            req.ContentLength = int64(length)
        }
    }

    client := &http.Client{
        Timeout:   timeout,
        Transport: &loghttp.Transport{},
    }

    return client.Do(req)
}

只要内容长度正确,就满足S3。我不需要将TransferEncoding设置为identity。