[インデックス 19201] ファイルの概要
このコミットは、Go言語の標準ライブラリ net/http/httputil
パッケージ内の DumpResponse
関数が、HTTPレスポンスのダンプ時に Content-Length
ヘッダーを適切に含めるように修正するものです。これにより、ダンプされたレスポンスがより正確な情報を持つようになります。
コミット
commit 8f7664123087d1800c214b7dc6fb8ea169208ad6
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Thu Apr 17 14:03:05 2014 -0700
net/http/httputil: include Content-Length in DumpResponse output
Fixes #5357
LGTM=nigeltao
R=nigeltao
CC=golang-codereviews
https://golang.org/cl/87910050
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/8f7664123087d1800c214b7dc6fb8ea169208ad6
元コミット内容
diff --git a/src/pkg/net/http/httputil/dump.go b/src/pkg/net/http/httputil/dump.go
index ab1eab21bc..acd5618454 100644
--- a/src/pkg/net/http/httputil/dump.go
+++ b/src/pkg/net/http/httputil/dump.go
@@ -7,6 +7,7 @@ package httputil
import (
"bufio"
"bytes"
+"errors"
"fmt"
"io"
"io/ioutil"
@@ -230,14 +231,31 @@ func DumpRequest(req *http.Request, body bool) (dump []byte, err error) {
return
}
+// errNoBody is a sentinel error value used by failureToReadBody so we can detect
+// that the lack of body was intentional.
+var errNoBody = errors.New("sentinel error value")
+
+// failureToReadBody is a io.ReadCloser that just returns errNoBody on
+// Read. It's swapped in when we don't actually want to consume the
+// body, but need a non-nil one, and want to distinguish the error
+// from reading the dummy body.
+type failureToReadBody struct{}
+
+func (failureToReadBody) Read([]byte) (int, error) { return 0, errNoBody }
+func (failureToReadBody) Close() error { return nil }
+
+var emptyBody = ioutil.NopCloser(strings.NewReader(""))
+
// DumpResponse is like DumpRequest but dumps a response.
func DumpResponse(resp *http.Response, body bool) (dump []byte, err error) {
var b bytes.Buffer
save := resp.Body
savecl := resp.ContentLength
-\tif !body || resp.Body == nil {\n-\t\tresp.Body = nil\n-\t\tresp.ContentLength = 0\n+\n+\tif !body {\n+\t\tresp.Body = failureToReadBody{}\n+\t} else if resp.Body == nil {\n+\t\tresp.Body = emptyBody\n \t} else {\n \t\tsave, resp.Body, err = drainBody(resp.Body)\n \t\tif err != nil {\n@@ -245,11 +263,13 @@ func DumpResponse(resp *http.Response, body bool) (dump []byte, err error) {\n \t\t}\n \t}\n \terr = resp.Write(&b)\n+\tif err == errNoBody {\n+\t\terr = nil\n+\t}\n \tresp.Body = save
resp.ContentLength = savecl
if err != nil {
-\t\treturn\n+\t\treturn nil, err\n \t}\n-\tdump = b.Bytes()\n-\treturn\n+\treturn b.Bytes(), nil\n }\ndiff --git a/src/pkg/net/http/httputil/dump_test.go b/src/pkg/net/http/httputil/dump_test.go
index a1dbfc39d6..c2902c8ec5 100644
--- a/src/pkg/net/http/httputil/dump_test.go
+++ b/src/pkg/net/http/httputil/dump_test.go
@@ -11,6 +11,7 @@ import (
"io/ioutil"
"net/http"
"net/url"
+"strings"
"testing"
)
@@ -176,3 +177,82 @@ func mustNewRequest(method, url string, body io.Reader) *http.Request {
}\n return req
}\n+\n+var dumpResTests = []struct {\n+\tres *http.Response\n+\tbody bool\n+\twant string\n+}{\n+\t{\n+\t\tres: &http.Response{\n+\t\t\tStatus: "200 OK",\n+\t\t\tStatusCode: 200,\n+\t\t\tProto: "HTTP/1.1",\n+\t\t\tProtoMajor: 1,\n+\t\t\tProtoMinor: 1,\n+\t\t\tContentLength: 50,\n+\t\t\tHeader: http.Header{\n+\t\t\t\t"Foo": []string{"Bar"},\n+\t\t\t},\n+\t\t\tBody: ioutil.NopCloser(strings.NewReader("foo")), // shouldn't be used\n+\t\t},\n+\t\tbody: false, // to verify we see 50, not empty or 3.\n+\t\twant: `HTTP/1.1 200 OK\n+Content-Length: 50\n+Foo: Bar`,\n+\t},\n+\n+\t{\n+\t\tres: &http.Response{\n+\t\t\tStatus: "200 OK",\n+\t\t\tStatusCode: 200,\n+\t\t\tProto: "HTTP/1.1",\n+\t\t\tProtoMajor: 1,\n+\t\t\tProtoMinor: 1,\n+\t\t\tContentLength: 3,\n+\t\t\tBody: ioutil.NopCloser(strings.NewReader("foo")),\n+\t\t},\n+\t\tbody: true,\n+\t\twant: `HTTP/1.1 200 OK\n+Content-Length: 3\n+\n+foo`,\n+\t},\n+\n+\t{\n+\t\tres: &http.Response{\n+\t\t\tStatus: "200 OK",\n+\t\t\tStatusCode: 200,\n+\t\t\tProto: "HTTP/1.1",\n+\t\t\tProtoMajor: 1,\n+\t\t\tProtoMinor: 1,\n+\t\t\tContentLength: -1,\n+\t\t\tBody: ioutil.NopCloser(strings.NewReader("foo")),\n+\t\t\tTransferEncoding: []string{"chunked"},\n+\t\t},\n+\t\tbody: true,\n+\t\twant: `HTTP/1.1 200 OK\n+Transfer-Encoding: chunked\n+\n+3\n+foo\n+0`,\n+\t},\n+}\n+\n+func TestDumpResponse(t *testing.T) {\n+\tfor i, tt := range dumpResTests {\n+\t\tgotb, err := DumpResponse(tt.res, tt.body)\n+\t\tif err != nil {\n+\t\t\tt.Errorf("%d. DumpResponse = %v", i, err)\n+\t\t\tcontinue\n+\t\t}\n+\t\tgot := string(gotb)\n+\t\tgot = strings.TrimSpace(got)\n+\t\tgot = strings.Replace(got, "\\r", "", -1)\n+\n+\t\tif got != tt.want {\n+\t\t\tt.Errorf("%d.\\nDumpResponse got:\\n%s\\n\\nWant:\\n%s\\n", i, got, tt.want)\n+\t\t}\n+\t}\n+}\n```
## 変更の背景
このコミットは、`net/http/httputil` パッケージの `DumpResponse` 関数が、HTTPレスポンスをダンプする際に `Content-Length` ヘッダーを正しく出力しないという問題(`Fixes #5357` で言及されている問題)を解決するために行われました。
以前の `DumpResponse` の実装では、`body` 引数が `false` の場合、つまりレスポンスボディをダンプしない設定の場合に、`resp.Body` を `nil` に設定し、`resp.ContentLength` を `0` に設定していました。これにより、`http.Response.Write` メソッドがレスポンスを書き出す際に、本来の `Content-Length` の値が失われ、ダンプ結果に反映されないという問題がありました。
特に、`Content-Length` ヘッダーはHTTPメッセージのボディの長さを正確に伝えるために非常に重要であり、デバッグやプロトコル解析の際に不可欠な情報です。この情報が欠落していると、ダンプされたレスポンスの解釈が不正確になる可能性がありました。
この修正は、`DumpResponse` が `Content-Length` を含む、より正確で完全なHTTPレスポンスのダンプを提供できるようにすることを目的としています。
## 前提知識の解説
### `net/http/httputil` パッケージ
`net/http/httputil` は、Go言語の標準ライブラリ `net/http` パッケージを補完するユーティリティ機能を提供するパッケージです。主にHTTPリクエストやレスポンスのダンプ(内容の文字列化)、リバースプロキシの実装、接続の再利用などの機能を提供します。デバッグやネットワークトラフィックの検査において非常に有用です。
### `http.Response` 構造体
`http.Response` は、HTTPレスポンスを表すGo言語の構造体です。この構造体には、ステータスコード、プロトコルバージョン、ヘッダー、そしてレスポンスボディなどの情報が含まれます。
- `Status`: レスポンスのステータス文字列(例: "200 OK")。
- `StatusCode`: レスポンスの数値ステータスコード(例: 200)。
- `Proto`: 使用されたHTTPプロトコル(例: "HTTP/1.1")。
- `Header`: HTTPヘッダーを表す `http.Header` マップ。
- `Body`: レスポンスボディを表す `io.ReadCloser` インターフェース。
- `ContentLength`: レスポンスボディの長さ(バイト単位)。不明な場合は `-1`。
### `DumpResponse` 関数
`httputil.DumpResponse` 関数は、与えられた `http.Response` オブジェクトの内容をバイトスライスとしてダンプ(文字列化)します。
- `resp *http.Response`: ダンプするHTTPレスポンスオブジェクト。
- `body bool`: レスポンスボディを含めるかどうかを示すフラグ。`true` の場合、ボディもダンプに含まれます。
この関数は、デバッグ目的でHTTPレスポンスの生データを検査する際によく使用されます。
### `Content-Length` ヘッダー
`Content-Length` はHTTPヘッダーの一つで、HTTPメッセージのエンティティボディ(ペイロード)のバイト単位のサイズを示します。これは、受信側がメッセージの終わりを判断するために使用される重要なヘッダーです。特に、持続的接続(Persistent Connection)において、次のリクエストを送信するタイミングを決定するために不可欠です。
### `io.ReadCloser` インターフェース
`io.ReadCloser` は、Go言語の `io` パッケージで定義されているインターフェースで、`io.Reader` と `io.Closer` の両方のインターフェースを組み合わせたものです。
- `Read(p []byte) (n int, err error)`: データを読み込みます。
- `Close() error`: リソースを閉じます。
`http.Response.Body` はこの `io.ReadCloser` 型であり、レスポンスボディのストリームを読み込み、使用後に閉じる責任があることを示します。
### `ioutil.NopCloser` と `strings.NewReader`
- `strings.NewReader(s string)`: 文字列 `s` からデータを読み込む `io.Reader` を返します。これは、メモリ上の文字列をファイルのように扱う場合に便利です。
- `ioutil.NopCloser(r io.Reader)`: 与えられた `io.Reader` を `io.ReadCloser` にラップします。この `io.ReadCloser` の `Close` メソッドは何も行いません。これは、`io.ReadCloser` が期待されるが、基になるリーダーが閉じる必要がない場合(例: メモリ上のデータ)に便利です。
## 技術的詳細
このコミットの主要な変更点は、`DumpResponse` 関数が `body` 引数の値に応じて `resp.Body` の扱いを改善し、`Content-Length` ヘッダーが常に正しくダンプされるようにしたことです。
### 変更前(問題点)
変更前は、`DumpResponse` 関数内で `body` 引数が `false` の場合、または `resp.Body` が `nil` の場合、以下のように処理されていました。
```go
if !body || resp.Body == nil {
resp.Body = nil
resp.ContentLength = 0
}
このコードは、ボディをダンプしない場合に resp.Body
を nil
に、resp.ContentLength
を 0
に強制的に設定していました。しかし、http.Response.Write
メソッドは、Content-Length
ヘッダーを生成する際に resp.ContentLength
フィールドの値を参照します。したがって、この強制的な 0
設定により、本来の Content-Length
の値が失われ、ダンプ結果に反映されませんでした。
変更後(修正内容)
修正後のコードは、この問題を解決するために以下の変更を導入しました。
-
errNoBody
とfailureToReadBody
の導入:errNoBody
は、ボディを読み込むべきではない場合にRead
メソッドが返す特別なエラー値として定義されました。failureToReadBody
はio.ReadCloser
インターフェースを実装する新しい型です。この型のRead
メソッドは常に0, errNoBody
を返し、Close
メソッドは何も行いません。
-
DumpResponse
内のresp.Body
の扱い:if !body
の場合(ボディをダンプしない場合):resp.Body
はfailureToReadBody{}
に設定されます。これにより、http.Response.Write
がボディを読み込もうとした際にerrNoBody
が返され、ボディの読み込みが意図的にスキップされたことを示します。しかし、resp.ContentLength
は元の値が保持されるため、Write
メソッドは正しいContent-Length
ヘッダーを生成できます。else if resp.Body == nil
の場合(元のボディがnil
の場合):resp.Body
はemptyBody
に設定されます。emptyBody
はioutil.NopCloser(strings.NewReader(""))
で作成された空のボディであり、http.Response.Write
がボディを処理する際にエラーを発生させずに空のボディとして扱われます。- それ以外の場合(ボディをダンプする場合):
既存の
drainBody
関数を使用してボディを読み込み、元のボディを保存します。これは変更前と同じ動作です。
-
エラーハンドリングの改善:
resp.Write(&b)
の呼び出し後、返されたエラーがerrNoBody
であった場合、そのエラーはnil
に上書きされます。これは、errNoBody
が意図的なボディのスキップを示すための内部的なシグナルであり、外部にエラーとして伝播すべきではないためです。これにより、DumpResponse
はボディをダンプしない場合でもエラーを返さずに正常に完了します。
これらの変更により、DumpResponse
は body
引数の値に関わらず、http.Response.Write
が Content-Length
ヘッダーを正しく生成するために必要な resp.ContentLength
の値を保持し、かつボディの読み込みを適切に制御できるようになりました。
テストの追加
dump_test.go
に TestDumpResponse
という新しいテスト関数と dumpResTests
というテストケースのスライスが追加されました。これらのテストケースは、Content-Length
が正しくダンプされること、ボディがダンプされる場合とされない場合の両方で期待される出力が得られることを検証します。特に、Content-Length: 50
のレスポンスが body: false
の場合でも正しく出力されるテストケースが含まれており、このコミットの目的を直接検証しています。
コアとなるコードの変更箇所
src/pkg/net/http/httputil/dump.go
--- a/src/pkg/net/http/httputil/dump.go
+++ b/src/pkg/net/http/httputil/dump.go
@@ -7,6 +7,7 @@ package httputil
import (
"bufio"
"bytes"
+"errors" // 追加
"fmt"
"io"
"io/ioutil"
@@ -230,14 +231,31 @@ func DumpRequest(req *http.Request, body bool) (dump []byte, err error) {
return
}
+// errNoBody は、failureToReadBody が使用するセンチネルエラー値で、
+// ボディがないことが意図的であることを検出するために使用されます。
+var errNoBody = errors.New("sentinel error value")
+
+// failureToReadBody は io.ReadCloser で、Read 時に errNoBody を返します。
+// ボディを実際に消費したくないが、nil ではないボディが必要な場合、
+// およびダミーボディの読み込みからのエラーを区別したい場合に置き換えられます。
+type failureToReadBody struct{}
+
+func (failureToReadBody) Read([]byte) (int, error) { return 0, errNoBody }
+func (failureToReadBody) Close() error { return nil }
+
+var emptyBody = ioutil.NopCloser(strings.NewReader("")) // 追加
+
// DumpResponse is like DumpRequest but dumps a response.
func DumpResponse(resp *http.Response, body bool) (dump []byte, err error) {
var b bytes.Buffer
save := resp.Body
savecl := resp.ContentLength
-\tif !body || resp.Body == nil {\n-\t\tresp.Body = nil\n-\t\tresp.ContentLength = 0\n+\n+\tif !body { // 変更
+\t\tresp.Body = failureToReadBody{} // 変更
+\t} else if resp.Body == nil { // 変更
+\t\tresp.Body = emptyBody // 変更
\t} else {
\tsave, resp.Body, err = drainBody(resp.Body)
\tif err != nil {
@@ -245,11 +263,13 @@ func DumpResponse(resp *http.Response, body bool) (dump []byte, err error) {
\t}\n \t}\n \terr = resp.Write(&b)\n+\tif err == errNoBody { // 追加
+\t\terr = nil // 追加
+\t}\n \tresp.Body = save
resp.ContentLength = savecl
if err != nil {
-\t\treturn\n+\t\treturn nil, err // 変更
\t}\n-\tdump = b.Bytes()\n-\treturn\n+\treturn b.Bytes(), nil // 変更
}\n```
### `src/pkg/net/http/httputil/dump_test.go`
```diff
--- a/src/pkg/net/http/httputil/dump_test.go
+++ b/src/pkg/net/http/httputil/dump_test.go
@@ -11,6 +11,7 @@ import (
"io/ioutil"
"net/http"
"net/url"
+"strings" // 追加
"testing"
)
@@ -176,3 +177,82 @@ func mustNewRequest(method, url string, body io.Reader) *http.Request {
}\n return req
}\n+\n+// dumpResTests は DumpResponse のテストケースを定義します。
+var dumpResTests = []struct {
+ res *http.Response
+ body bool
+ want string
+}{
+ {
+ res: &http.Response{
+ Status: "200 OK",
+ StatusCode: 200,
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ ContentLength: 50, // Content-Length が設定されているケース
+ Header: http.Header{
+ "Foo": []string{"Bar"},
+ },
+ Body: ioutil.NopCloser(strings.NewReader("foo")), // ボディは使用されない
+ },
+ body: false, // ボディをダンプしない設定
+ want: `HTTP/1.1 200 OK
+Content-Length: 50
+Foo: Bar`, // Content-Length が正しく出力されることを期待
+ },
+
+ {
+ res: &http.Response{
+ Status: "200 OK",
+ StatusCode: 200,
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ ContentLength: 3, // Content-Length が設定されているケース
+ Body: ioutil.NopCloser(strings.NewReader("foo")),
+ },
+ body: true, // ボディをダンプする設定
+ want: `HTTP/1.1 200 OK
+Content-Length: 3
+
+foo`, // Content-Length とボディが正しく出力されることを期待
+ },
+
+ {
+ res: &http.Response{
+ Status: "200 OK",
+ StatusCode: 200,
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ ContentLength: -1, // Content-Length が不明なケース(Transfer-Encoding: chunked)
+ Body: ioutil.NopCloser(strings.NewReader("foo")),
+ TransferEncoding: []string{"chunked"},
+ },
+ body: true, // ボディをダンプする設定
+ want: `HTTP/1.1 200 OK
+Transfer-Encoding: chunked
+
+3
+foo
+0`, // Transfer-Encoding が正しく出力されることを期待
+ },
+}
+
+// TestDumpResponse は DumpResponse 関数をテストします。
+func TestDumpResponse(t *testing.T) {
+ for i, tt := range dumpResTests {
+ gotb, err := DumpResponse(tt.res, tt.body)
+ if err != nil {
+ t.Errorf("%d. DumpResponse = %v", i, err)
+ continue
+ }
+ got := string(gotb)
+ got = strings.TrimSpace(got)
+ got = strings.Replace(got, "\r", "", -1)
+
+ if got != tt.want {
+ t.Errorf("%d.\nDumpResponse got:\n%s\n\nWant:\n%s\n", i, got, tt.want)
+ }
+ }
+}
コアとなるコードの解説
src/pkg/net/http/httputil/dump.go
の変更点
-
import "errors"
の追加:errors
パッケージがインポートされ、カスタムエラーerrNoBody
を定義するために使用されます。 -
errNoBody
変数の定義:var errNoBody = errors.New("sentinel error value")
これは、DumpResponse
がボディを読み込むべきではない場合に、failureToReadBody
型のRead
メソッドが返す特別なエラー値です。このエラーは、ボディの読み込みが意図的にスキップされたことを示す「センチネルエラー」として機能します。 -
failureToReadBody
型の定義:type failureToReadBody struct{} func (failureToReadBody) Read([]byte) (int, error) { return 0, errNoBody } func (failureToReadBody) Close() error { return nil }
この新しい型は
io.ReadCloser
インターフェースを実装します。Read
メソッドは常に0
バイトを読み込み、errNoBody
を返します。これにより、http.Response.Write
がボディを読み込もうとした際に、ボディがないことを安全に伝えることができます。Close
メソッドは何も行いません。これは、このダミーボディが閉じるべきリソースを持たないためです。
-
emptyBody
変数の定義:var emptyBody = ioutil.NopCloser(strings.NewReader(""))
これは、元のresp.Body
がnil
であった場合にresp.Body
に設定される空のio.ReadCloser
です。strings.NewReader("")
で空の文字列から読み込むリーダーを作成し、それをioutil.NopCloser
でio.ReadCloser
にラップしています。これにより、http.Response.Write
がボディを処理する際に、エラーを発生させずに空のボディとして扱われます。 -
DumpResponse
関数内の条件分岐の変更:- if !body || resp.Body == nil { - resp.Body = nil - resp.ContentLength = 0 + if !body { + resp.Body = failureToReadBody{} + } else if resp.Body == nil { + resp.Body = emptyBody } else { // ... 既存の drainBody 呼び出し ... }
if !body
ブロック:body
引数がfalse
の場合(ボディをダンプしない場合)、resp.Body
はfailureToReadBody{}
に設定されます。これにより、http.Response.Write
はボディを読み込もうとしますが、errNoBody
を受け取ることで、ボディの読み込みを安全にスキップします。重要なのは、このときresp.ContentLength
は変更されないため、元のContent-Length
の値が保持され、ダンプ結果に反映される点です。else if resp.Body == nil
ブロック:body
引数がtrue
で、かつ元のresp.Body
がnil
であった場合、resp.Body
はemptyBody
に設定されます。これにより、http.Response.Write
は空のボディとして処理し、エラーを発生させません。else
ブロック:body
引数がtrue
で、かつ元のresp.Body
がnil
でない場合、既存のdrainBody
関数が呼び出され、ボディが読み込まれます。
-
エラーハンドリングの追加:
err = resp.Write(&b) + if err == errNoBody { + err = nil + }
resp.Write(&b)
の呼び出し後、返されたエラーがerrNoBody
であった場合、そのエラーはnil
に上書きされます。これは、errNoBody
がDumpResponse
の内部的なロジックでボディのスキップを示すために使用されるものであり、外部にエラーとして報告すべきではないためです。これにより、ボディをダンプしない場合でも、関数は正常に完了します。 -
戻り値の変更:
- return + return nil, err // 変更 // ... - dump = b.Bytes() - return + return b.Bytes(), nil // 変更
エラーが発生した場合の戻り値の形式が
return
からreturn nil, err
に、成功した場合の戻り値がdump = b.Bytes(); return
からreturn b.Bytes(), nil
に変更されました。これは、Goのエラーハンドリングの慣習に沿った明示的なエラーと戻り値の指定です。
src/pkg/net/http/httputil/dump_test.go
の変更点
-
import "strings"
の追加:strings
パッケージがインポートされ、strings.NewReader
を使用してテスト用のボディを作成するために使用されます。 -
dumpResTests
変数の追加:DumpResponse
関数のテストケースを構造体のスライスとして定義しています。各テストケースは、入力となるhttp.Response
、body
フラグ、そして期待されるダンプ結果 (want
文字列) を含みます。- 最初のテストケースは、
Content-Length: 50
が設定されたレスポンスでbody: false
の場合でも、Content-Length
ヘッダーが正しくダンプされることを検証します。これがこのコミットの主要な修正点を直接テストするものです。 - 2番目のテストケースは、
Content-Length: 3
が設定されたレスポンスでbody: true
の場合、Content-Length
とボディの両方が正しくダンプされることを検証します。 - 3番目のテストケースは、
Content-Length: -1
(つまりTransfer-Encoding: chunked
)のレスポンスでbody: true
の場合、Transfer-Encoding
ヘッダーとチャンク化されたボディが正しくダンプされることを検証します。
- 最初のテストケースは、
-
TestDumpResponse
関数の追加:dumpResTests
スライスをイテレートし、各テストケースに対してDumpResponse
を呼び出し、結果が期待値と一致するかを検証します。strings.TrimSpace(got)
とstrings.Replace(got, "\r", "", -1)
を使用して、ダンプ結果から余分な空白やCR文字を削除し、比較を安定させています。
これらのテストの追加により、DumpResponse
の修正が意図通りに機能し、既存の動作を壊していないことが保証されます。
関連リンク
- Go言語のコミットページ: https://github.com/golang/go/commit/8f7664123087d1800c214b7dc6fb8ea169208ad6
- Go言語のコードレビューシステム (Gerrit) での変更リスト: https://golang.org/cl/87910050
参考にした情報源リンク
- Stack Overflow の関連議論 (Go issue 5357 の背景): https://stackoverflow.com/questions/29975579/httputil-dumprequest-does-not-show-content-length-header
- Go言語
net/http/httputil
パッケージのドキュメント: https://pkg.go.dev/net/http/httputil - Go言語
net/http
パッケージのドキュメント: https://pkg.go.dev/net/http - Go言語
io
パッケージのドキュメント: https://pkg.go.dev/io - Go言語
io/ioutil
パッケージのドキュメント (Go 1.16以降はio
およびos
パッケージに移行): https://pkg.go.dev/io/ioutil (ただし、このコミット時点ではioutil
が使用されています) - HTTP/1.1 RFC 7230 (Message Syntax and Routing) - Content-Length ヘッダーに関する記述: https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2