Skip to content

Instantly share code, notes, and snippets.

@nobonobo
Created March 29, 2016 10:40
Show Gist options
  • Select an option

  • Save nobonobo/bc3cba5bc97b1ff0f90c to your computer and use it in GitHub Desktop.

Select an option

Save nobonobo/bc3cba5bc97b1ff0f90c to your computer and use it in GitHub Desktop.
httpクライアントBodyの読み残し問題
package main
import (
"crypto/tls"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"time"
)
func withDrain(client *http.Client, url string) interface{} {
resp, err := client.Get(url)
if err != nil {
log.Fatal(err)
}
defer func() {
io.Copy(ioutil.Discard, resp.Body)
resp.Body.Close()
}()
fmt.Println(url, resp.Status)
var v interface{}
if err := json.NewDecoder(resp.Body).Decode(&v); err != nil {
log.Fatal(err)
}
return v
}
func withoutDrain(client *http.Client, url string) interface{} {
resp, err := client.Get(url)
if err != nil {
log.Fatal(err)
}
defer func() {
resp.Body.Close()
}()
fmt.Println(url, resp.Status)
var v interface{}
if err := json.NewDecoder(resp.Body).Decode(&v); err != nil {
log.Fatal(err)
}
return v
}
func run(url string) {
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
log.Println(via)
return nil
},
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
for i := 0; i < 2; i++ {
begin := time.Now()
withDrain(client, url)
fmt.Println("with-dorain:", i+1, time.Since(begin))
}
client = &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
log.Println(via)
return nil
},
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
for i := 0; i < 2; i++ {
begin := time.Now()
withoutDrain(client, url)
fmt.Println("without-dorain:", i+1, time.Since(begin))
}
}
func main() {
run("https://api.github.com/repos/nobonobo/jsonrpc")
}
@nobonobo
Copy link
Copy Markdown
Author

$ go run main.go 
https://api.github.com/repos/nobonobo/jsonrpc 200 OK
with-dorain: 1 803.654857ms
https://api.github.com/repos/nobonobo/jsonrpc 200 OK
with-dorain: 2 225.665911ms
https://api.github.com/repos/nobonobo/jsonrpc 200 OK
without-dorain: 1 809.395476ms
https://api.github.com/repos/nobonobo/jsonrpc 200 OK
without-dorain: 2 793.444934ms

without-dorainだといつも800ms前後かかっちゃう・・。

@nobonobo
Copy link
Copy Markdown
Author

google/go-github#317 問題のIssue

@nobonobo
Copy link
Copy Markdown
Author

でもこれをGoのサーバーに振り向けると問題が再現しないのですー。

@nobonobo
Copy link
Copy Markdown
Author

全く同じ内容のJSONをファイルにいれてServeFileで返しても問題が再現しない。(no-chunked)

@nobonobo
Copy link
Copy Markdown
Author

確かにwithoutDrainではearlyCloseFnが呼ばれ再接続するみたい。withDrainならearlyCloseFnは呼ばれない。
しかし、ドレイン時のio.Copyはゼロとerror(nil)を返すのか。

@nobonobo
Copy link
Copy Markdown
Author

何か複合条件があるのかなぁ?

@nobonobo
Copy link
Copy Markdown
Author

全く同じ内容のJSONをレンタルサーバーのApacheからサーブしても問題が再現しない。

@nobonobo
Copy link
Copy Markdown
Author

github-apiのレスポンスヘッダー

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
Cache-Control: public, max-age=60, s-maxage=60
Content-Length: 4956
Content-Security-Policy: default-src 'none'
Content-Type: application/json; charset=utf-8
Etag: "445ad429c42b15b94b68c582c723d12e"
Last-Modified: Tue, 07 Apr 2015 23:57:49 GMT
Status: 200 OK
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
Vary: Accept
Vary: Accept-Encoding
X-Content-Type-Options: nosniff
X-Frame-Options: deny
X-Github-Media-Type: github.v3
X-Github-Request-Id: 3A004893:36CA:10971306:56FA50C4
X-Ratelimit-Limit: 60
X-Ratelimit-Remaining: 22
X-Ratelimit-Reset: 1459247634
X-Served-By: 474556b853193c38f1b14328ce2d1b7d
X-Xss-Protection: 1; mode=block
Date: Tue, 29 Mar 2016 10:01:42 GMT

Apacheでサーブしたときのレスポンスヘッダー

HTTP/1.1 200 OK
Date: Wed, 30 Mar 2016 00:40:19 GMT
Server: Apache/2.2.31
Last-Modified: Wed, 30 Mar 2016 00:39:18 GMT
ETag: "a9129ab-135c-52f39603ff980"
Accept-Ranges: bytes
Content-Length: 4956
Content-Type: application/json

@nobonobo
Copy link
Copy Markdown
Author

gzipレスポンスを返すようにしたら再現した。http.Clientは Accept-Encodingにgzipを含めるのか。

@nobonobo
Copy link
Copy Markdown
Author

それで https://go-review.googlesource.com/#/c/21290/ このパッチというわけか。

@nobonobo
Copy link
Copy Markdown
Author

そして上記は取りやめになって https://go-review.googlesource.com/#/c/21291/3/src/net/http/transport.go httpのtransportが修正・マージされましたと。

@nobonobo
Copy link
Copy Markdown
Author

結果は golang/go@18072ad これ。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment