パッケージ概要

HTTPクライアントとサーバーの実装を提供

Get, Head, Post, PostForm で HTTP リクエストを作れる

sample

resp, err := http.Get("<http://example.com/>")
...
resp, err := http.Post("<http://example.com/upload>", "image/jpeg", &buf)
...
resp, err := http.PostForm("<http://example.com/form>",
	url.Values{"key": {"Value"}, "id": {"123"}})

Get 実装

// URL に対して GET するよ
// リダイレクトは最大 10 まで (3xx レスポンスに対して
//
// status 2xx 以外は error として返る
// エラー型は, *url.Error
//
// resp.Body は必ず閉じる必要がある
//
// http.Get は, DefaultClient のラッパー
//
// カスタムヘッダーを利用したい場合は, NewRequest と DefaultClient.Do を使う
func Get(url string) (resp *Response, err error) {
	return DefaultClient.Get(url)
}

url.Error

古の時代に作られたからか, error の wrap 機能が自作されてた

// Error reports an error and the operation and URL that caused it.
type Error struct {
	Op  string
	URL string
	Err error
}

func (e *Error) Unwrap() error { return e.Err }
func (e *Error) Error() string { return fmt.Sprintf("%s %q: %s", e.Op, e.URL, e.Err) }

DefaultClient

HTTP Client が定義されていてそれのゼロ値

// DefaultClient is the default Client and is used by Get, Head, and Post.
var DefaultClient = &Client{}

実際の流れ

http://hustcat.github.io/assets/golang/net-http-timeout-client-1.jpg

// どういう意図なのか基本的な実装は private メソッドで行うようになってた
// (基本的にパブリックは private なメソッドを叩くだけ)
func (c *Client) do(req *Request) (retres *Response, reterr error) {
...
...
  for { // リダイレクト用のループ
...
...
    if resp, didTimeout, err = c.send(req, deadline); err != nil {
    ...
    ...
    }
    ....
    ....
    // リダイレクトするかみたいな判定があって return
...
...
func send(ireq *Request, rt RoundTripper, deadline time.Time) (resp *Response, didTimeout func() bool, err error) {
  ...
  ...
  // RoundTrip は データ伝送などの分野では、通信相手に信号やデータを発信して、応答が帰ってくるまでの過程
  resp, err = rt.RoundTrip(req) 
  ...
}
...
...
func (t *Transport) roundTrip(req *Request) (*Response, error) {
...
...
    // 接続されたHTTPプロキシへのキャッシュされた接続または, 新しく作成された接続の取得
    pconn, err := t.getConn(treq, cm)
...
...
    resp, err = pconn.roundTrip(treq)
...
}
...
...
// ターゲットにダイアルして persistConn を返す
// これがエラーを返さなければ persistentConn はリクエストを書き込む準備が出来てる
func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (pc *persistConn, err error) {
...
  ctx := req.Context() // request のコンテキストを伝搬させてる(キャンセルとかデッドラインとかが使われてた
  ...
  ...
	w := &wantConn{
    ...
    ...
		ctx:        ctx,
...
}
...
...
func (t *Transport) dialConnFor(w *wantConn) {
	defer w.afterDial()

	pc, err := t.dialConn(w.ctx, w.cm)
  ...
  ...
}
...
...
// 通常は Keep-Alive みたいな永続的な接続( keep-alive 以外でも使えるよとは書いてた
func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err error) {
    // リクエストの本文全体を読み取る処理とか応答を待つと同時にリクエストを書き込んだりする処理
}