KDOC 636: LimitReaderで境界値付きで読み込む

この文書のステータス

  • 作成
    • 2026-09-27 貴島
  • レビュー
    • 2026-09-27 貴島

概要

ある大きさ以下のデータをネットワークから受け取るプログラムがあるとする。DOS 攻撃対策に、サイズを制限して読み込む必要がある。

前提。

io.Copy に生データを渡すと全部コピーするため目的を達成できない。長さを知るためには全部読み込む必要があって、不可能。HTTPヘッダも信用してはいけない。

流れ。

  1. LimitReader でデータを限定して読み取り、 io.Copy に渡す
  2. 続きの1バイトを読み込んで存在確認する。読み込めれば超過で、エラーとする。LimitReader はサイズ超過時に特にエラーは出さないので、それだけでは続きがあったうえで強制的に切り出したのか、既定サイズより下できちんと取り出せたのか判定できないため
// A LimitedReader reads from R but limits the amount of
// data returned to just N bytes. Each call to Read
// updates N to reflect the new amount remaining.
// Read returns EOF when N <= 0 or when the underlying R returns EOF.
type LimitedReader struct {
	R Reader // underlying reader
	N int64  // max bytes remaining
}

実際のコード。

func CopyResponseBody(dst io.Writer, src io.Reader, maxSize int64) error {
	if _, err := io.Copy(dst, io.LimitReader(src, maxSize)); err != nil {
		return err
	}

	var extra [1]byte
	_, err := io.ReadFull(src, extra[:])
	if err == nil {
		return fmt.Errorf("response body too large: exceeded %d bytes", maxSize)
	}
	if errors.Is(err, io.EOF) {
		return nil
	}
	return err
}

関連