{"action":"created","issue":{"url":"https://api.github.com/repos/golang/go/issues/16264","repository_url":"https://api.github.com/repos/golang/go","labels_url":"https://api.github.com/repos/golang/go/issues/16264/labels{/name}","comments_url":"https://api.github.com/repos/golang/go/issues/16264/comments","events_url":"https://api.github.com/repos/golang/go/issues/16264/events","html_url":"https://github.com/golang/go/issues/16264","id":163762936,"number":16264,"title":"http client POST io.ReadCloser,需要显示指定Request.TransferEncoding为chunked","user":{"login":"yuehai1117","id":3071953,"avatar_url":"https://avatars.githubusercontent.com/u/3071953?v=3","gravatar_id":"","url":"https://api.github.com/users/yuehai1117","html_url":"https://github.com/yuehai1117","followers_url":"https://api.github.com/users/yuehai1117/followers","following_url":"https://api.github.com/users/yuehai1117/following{/other_user}","gists_url":"https://api.github.com/users/yuehai1117/gists{/gist_id}","starred_url":"https://api.github.com/users/yuehai1117/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yuehai1117/subscriptions","organizations_url":"https://api.github.com/users/yuehai1117/orgs","repos_url":"https://api.github.com/users/yuehai1117/repos","events_url":"https://api.github.com/users/yuehai1117/events{/privacy}","received_events_url":"https://api.github.com/users/yuehai1117/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/golang/go/labels/WaitingForInfo","name":"WaitingForInfo","color":"ededed"}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":4,"created_at":"2016-07-05T02:53:09Z","updated_at":"2016-07-07T19:50:54Z","closed_at":null,"body":"1. What version of Go are you using (`go version`)?\r\ngo1.5\r\n\r\n2. What operating system and processor architecture are you using (`go env`)?\r\nLinux xxxxxx 3.10.0-327.el7.x86_64 #1 SMP Thu Nov 19 22:10:57 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux\r\n\r\nhttp client POST io.ReadCloser流，如果不显示指定Request.TransferEncoding为chunked，这时io.ReadCloser数据将被分成多个chunked，并且第一个chunked的大小总是为1.\r\n下面是一个例子：\r\npackage main\r\nimport (\r\n    \"io/ioutil\"\r\n    \"net/http\"\r\n    \"strings\"\r\n)\r\nfunc main() {\r\n    http.Post(\"http://172.18.1.218/hehe/test.php\", \"application/x-www-form-urlencoded\", ioutil.NopCloser(strings.NewReader(\"teststring\")))\r\n}\r\ntcpdump抓包，request请求包如下：\r\nFlags [P.], seq 1:191, ack 1, win 229, options [nop,nop,TS val 3434923483 ecr 1719817619], length 190\r\n    0x0000:  4500 00f2 aa2d 4000 4006 3409 ac12 01d1  E....-@.@.4.....\r\n    0x0010:  ac12 01da d166 0050 33bd 6ff2 4f4a afc1  .....f.P3.o.OJ..\r\n    0x0020:  8018 00e5 5cb4 0000 0101 080a ccbc c5db  ....\\...........\r\n    0x0030:  6682 5593 504f 5354 202f 6865 6865 2f74  f.U.POST./hehe/t\r\n    0x0040:  6573 742e 7068 7020 4854 5450 2f31 2e31  est.php.HTTP/1.1\r\n    0x0050:  0d0a 486f 7374 3a20 3137 322e 3138 2e31  ..Host:.172.18.1\r\n    0x0060:  2e32 3138 0d0a 5573 6572 2d41 6765 6e74  .218..User-Agent\r\n    0x0070:  3a20 476f 2d68 7474 702d 636c 6965 6e74  :.Go-http-client\r\n    0x0080:  2f31 2e31 0d0a 5472 616e 7366 6572 2d45  /1.1..Transfer-E\r\n    0x0090:  6e63 6f64 696e 673a 2063 6875 6e6b 6564  ncoding:.chunked\r\n    0x00a0:  0d0a 436f 6e74 656e 742d 5479 7065 3a20  ..Content-Type:.\r\n    0x00b0:  6170 706c 6963 6174 696f 6e2f 782d 7777  application/x-ww\r\n    0x00c0:  772d 666f 726d 2d75 726c 656e 636f 6465  w-form-urlencode\r\n    0x00d0:  640d 0a41 6363 6570 742d 456e 636f 6469  d..Accept-Encodi\r\n    0x00e0:  6e67 3a20 677a 6970 0d0a 0d0a 310d 0a74  ng:.gzip....1..t\r\n    0x00f0:  0d0a 0000 0000 0000 0000 0000 0000 0000  ................\r\n    0x0100:  0000                                     ..\r\nFlags [P.], seq 191:205, ack 1, win 229, options [nop,nop,TS val 3434923483 ecr 1719817619], length 14\r\n    0x0000:  4500 0042 aa2e 4000 4006 34b8 ac12 01d1  E..B..@.@.4.....\r\n    0x0010:  ac12 01da d166 0050 33bd 70b0 4f4a afc1  .....f.P3.p.OJ..\r\n    0x0020:  8018 00e5 5c04 0000 0101 080a ccbc c5db  ....\\...........\r\n    0x0030:  6682 5593 390d 0a65 7374 7374 7269 6e67  f.U.9..eststring\r\n    0x0040:  0d0a 0000 0000 0000 0000 0000 0000 0000  ................\r\n    0x0050:  0000                                     ..\r\n从上面的抓包数据可以看到，\"teststring\"被分成两个chunked，第一个chunked长度为1，chunked为\"t\"，第二个chunked长度为9,chunked为\"eststring\"\r\n\r\n如果POST io.Reader流，Request.TransferEncoding并不会设置为chunked，而是Request.ContentLength被设置为POST数据的长度，如下面这个例子：\r\npackage main\r\nimport (\r\n    \"io/ioutil\"\r\n    \"net/http\"\r\n    \"strings\"\r\n)\r\nfunc main() {\r\n    http.Post(\"http://172.18.1.218/hehe/test.php\", \"application/x-www-form-urlencoded\", strings.NewReader(\"teststring\"))\r\n}\r\ntcpdump抓包，request请求包如下：\r\nFlags [P.], seq 1:187, ack 1, win 229, options [nop,nop,TS val 3434923486 ecr 1719817622], length 186\r\n    0x0000:  4500 00ee 7c21 4000 4006 6219 ac12 01d1  E...|!@.@.b.....\r\n    0x0010:  ac12 01da d167 0050 c2a4 b9c3 ba98 6644  .....g.P......fD\r\n    0x0020:  8018 00e5 5cb0 0000 0101 080a ccbc c5de  ....\\...........\r\n    0x0030:  6682 5596 504f 5354 202f 6865 6865 2f74  f.U.POST./hehe/t\r\n    0x0040:  6573 742e 7068 7020 4854 5450 2f31 2e31  est.php.HTTP/1.1\r\n    0x0050:  0d0a 486f 7374 3a20 3137 322e 3138 2e31  ..Host:.172.18.1\r\n    0x0060:  2e32 3138 0d0a 5573 6572 2d41 6765 6e74  .218..User-Agent\r\n    0x0070:  3a20 476f 2d68 7474 702d 636c 6965 6e74  :.Go-http-client\r\n    0x0080:  2f31 2e31 0d0a 436f 6e74 656e 742d 4c65  /1.1..Content-Le\r\n    0x0090:  6e67 7468 3a20 3130 0d0a 436f 6e74 656e  ngth:.10..Conten\r\n    0x00a0:  742d 5479 7065 3a20 6170 706c 6963 6174  t-Type:.applicat\r\n    0x00b0:  696f 6e2f 782d 7777 772d 666f 726d 2d75  ion/x-www-form-u\r\n    0x00c0:  726c 656e 636f 6465 640d 0a41 6363 6570  rlencoded..Accep\r\n    0x00d0:  742d 456e 636f 6469 6e67 3a20 677a 6970  t-Encoding:.gzip\r\n    0x00e0:  0d0a 0d0a 7465 7374 7374 7269 6e67 0000  ....teststring..\r\n    0x00f0:  0000 0000 0000 0000 0000 0000 0000       ..............\r\n可以看出，request请求包中有Content-Length header，数据并不是以chunked方式传输的。\r\n\r\n\r\n如果POST io.ReadCloser流，并显示指定Request.TransferEncoding为chunked，POST的数据并不会被分为两个chunked。如下面例子：\r\npackage main\r\nimport (\r\n    \"fmt\"\r\n    \"io/ioutil\"\r\n    \"net/http\"\r\n    \"strings\"\r\n)\r\nfunc main() {\r\n    client := &http.Client{\r\n        Transport: &http.Transport{\r\n            DisableCompression: true,\r\n        },\r\n    }\r\n    req, err := http.NewRequest(\"POST\", \"http://172.18.1.218/hehe/test.php\", ioutil.NopCloser(strings.NewReader(\"teststring\")))\r\n    if err != nil {\r\n        fmt.Println(err)\r\n        return\r\n    }\r\n    req.TransferEncoding = []string{\"chunked\"}\r\n    client.Do(req)\r\n}\r\ntcpdump抓包，request请求包如下：\r\nFlags [P.], seq 1:128, ack 1, win 229, options [nop,nop,TS val 3437830591 ecr 154725], length 127\r\n    0x0000:  4500 00b3 f410 4000 4006 ea64 ac12 01d1  E.....@.@..d....\r\n    0x0010:  ac12 01da ee2f 0050 6ebc d5b1 8c5e 75d2  ...../.Pn....^u.\r\n    0x0020:  8018 00e5 5c75 0000 0101 080a cce9 21bf  ....\\u........!.\r\n    0x0030:  0002 5c65 504f 5354 202f 6865 6865 2f74  ..\\ePOST./hehe/t\r\n    0x0040:  6573 742e 7068 7020 4854 5450 2f31 2e31  est.php.HTTP/1.1\r\n    0x0050:  0d0a 486f 7374 3a20 3137 322e 3138 2e31  ..Host:.172.18.1\r\n    0x0060:  2e32 3138 0d0a 5573 6572 2d41 6765 6e74  .218..User-Agent\r\n    0x0070:  3a20 476f 2d68 7474 702d 636c 6965 6e74  :.Go-http-client\r\n    0x0080:  2f31 2e31 0d0a 5472 616e 7366 6572 2d45  /1.1..Transfer-E\r\n    0x0090:  6e63 6f64 696e 673a 2063 6875 6e6b 6564  ncoding:.chunked\r\n    0x00a0:  0d0a 0d0a 610d 0a74 6573 7473 7472 696e  ....a..teststrin\r\n    0x00b0:  670d 0a00 0000 0000 0000 0000 0000 0000  g...............\r\n    0x00c0:  0000 00                                  ...\r\n可以看出，request POST是以chunked方式发出去，并且chunked没有分片。"},"comment":{"url":"https://api.github.com/repos/golang/go/issues/comments/231187715","html_url":"https://github.com/golang/go/issues/16264#issuecomment-231187715","issue_url":"https://api.github.com/repos/golang/go/issues/16264","id":231187715,"user":{"login":"bradfitz","id":2621,"avatar_url":"https://avatars.githubusercontent.com/u/2621?v=3","gravatar_id":"","url":"https://api.github.com/users/bradfitz","html_url":"https://github.com/bradfitz","followers_url":"https://api.github.com/users/bradfitz/followers","following_url":"https://api.github.com/users/bradfitz/following{/other_user}","gists_url":"https://api.github.com/users/bradfitz/gists{/gist_id}","starred_url":"https://api.github.com/users/bradfitz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bradfitz/subscriptions","organizations_url":"https://api.github.com/users/bradfitz/orgs","repos_url":"https://api.github.com/users/bradfitz/repos","events_url":"https://api.github.com/users/bradfitz/events{/privacy}","received_events_url":"https://api.github.com/users/bradfitz/received_events","type":"User","site_admin":false},"created_at":"2016-07-07T19:50:54Z","updated_at":"2016-07-07T19:50:54Z","body":"Yes, the 1 byte chunk is to determine whether the Request.Body's io.Reader is actually ContentLength 0, or whether it just wasn't populated.\r\n\r\nI don't see an example where Go generates invalid HTTP.\r\n\r\nWhat is the bug here? Or is this just a question?\r\n\r\n\r\n\r\n"}}
{"actor":{"display_login":"bradfitz"}}