Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 12964] ファイルの概要

このコミットは、Go言語の標準ライブラリ mime パッケージにおける ParseMediaType 関数のエラーハンドリングを改善するものです。具体的には、メディアタイプ文字列のパースに失敗した場合に、関数がゼロ値(空文字列とnilマップ)を返すように変更し、エラー発生時の挙動をより明確にしています。

コミット

commit 7f7a70f225b5f08b601ce3bef091887d79a34f06
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Wed Apr 25 12:01:01 2012 -0700

    mime: make ParseMediaType return zero results on error
    
    Fixes #3562
    
    R=golang-dev, adg, rsc
    CC=golang-dev
    https://golang.org/cl/6119051

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/7f7a70f225b5f08b601ce3bef091887d79a34f06

元コミット内容

mime: make ParseMediaType return zero results on error

Fixes #3562

R=golang-dev, adg, rsc
CC=golang-dev
https://golang.org/cl/6119051

変更の背景

この変更は、Go言語のIssue #3562に対応するものです。ParseMediaType 関数は、HTTPのContent-Typeヘッダなどで使用されるメディアタイプ文字列(例: text/html; charset=utf-8)をパースし、メディアタイプ本体とパラメータのマップを返します。しかし、パース中にエラーが発生した場合、これまでの実装では、エラーが返されるにもかかわらず、mediatype(文字列)や params(マップ)に中途半端な値が設定されてしまう可能性がありました。

プログラミングにおける良いプラクティスとして、関数がエラーを返す場合、その戻り値は「ゼロ値」であるべき、という考え方があります。これは、エラーが発生した際には、有効な結果が生成されなかったことを明確にし、呼び出し元がエラーの有無だけで結果の有効性を判断できるようにするためです。中途半端な値が返されると、呼び出し元がその値を誤って使用してしまうリスクがあり、予期せぬバグにつながる可能性があります。

このコミットは、この問題を解決し、ParseMediaType がエラーを返した際には、mediatype に空文字列 ""paramsnil を返すように修正することで、より堅牢で予測可能なAPI挙動を実現しています。

前提知識の解説

MIMEタイプ (Media Type / Content-Type)

MIMEタイプ(Multipurpose Internet Mail Extensions)は、インターネット上でやり取りされるデータの種類を識別するための標準的な方法です。HTTPのContent-Typeヘッダや電子メールの添付ファイルなどで広く利用されています。

MIMEタイプは通常、type/subtype の形式で表現されます(例: text/html, image/jpeg, application/json)。さらに、セミコロンで区切られたパラメータを持つこともあります(例: text/html; charset=utf-8, application/json; boundary=something)。

Go言語のエラーハンドリング

Go言語では、エラーは多値戻り値の最後の要素として返されるのが一般的です。慣習として、関数がエラーを返す場合、他の戻り値は「ゼロ値」であるべきとされています。例えば、func (T, error) のようなシグネチャを持つ関数でエラーが発生した場合、T のゼロ値(数値型なら0、文字列型なら""、ポインタやマップ、スライスならnil)が返されるべきです。これにより、呼び出し元はエラーの有無だけを確認すればよく、エラーが発生したにもかかわらず中途半端な値が返されることによる混乱を防ぎます。

mime パッケージ

Go言語の標準ライブラリ mime パッケージは、MIMEタイプ文字列のパースや生成、エンコーディング/デコーディングなどの機能を提供します。ParseMediaType 関数はその中でも中心的な役割を担い、MIMEタイプ文字列を構造化されたデータ(メディアタイプ本体とパラメータマップ)に分解します。

技術的詳細

このコミットの技術的な変更点は非常にシンプルですが、Go言語のエラーハンドリングの慣習に則った重要な改善です。

ParseMediaType 関数の元の実装では、checkMediaTypeDisposition 関数がエラーを返した場合、単に return していました。この return は、mediatypeparams にそれまでの処理で設定された可能性のある中途半端な値をそのまま返してしまうことを意味します。

変更後、エラーが発生した場合に return "", nil, err と明示的に記述することで、mediatype には空文字列 ""params には nil が設定され、エラー情報と共にゼロ値が返されるようになります。これにより、呼び出し元はエラーが返された場合に、戻り値の mediatypeparams を安全に無視できるようになります。

また、テストコード (src/pkg/mime/mediatype_test.go) も更新され、エラーが発生した場合に ParseMediaType が非nilのパラメータや非空のメディアタイプ文字列を返さないことを確認するテストケースが追加されています。これにより、将来的に同様の回帰バグが発生するのを防ぎます。

コアとなるコードの変更箇所

src/pkg/mime/mediatype.go

--- a/src/pkg/mime/mediatype.go
+++ b/src/pkg/mime/mediatype.go
@@ -99,7 +99,7 @@ func ParseMediaType(v string) (mediatype string, params map[string]string, err e
 
  	err = checkMediaTypeDisposition(mediatype)
  	if err != nil {
- 		return
+ 		return "", nil, err
  	}
 
  	params = make(map[string]string)

src/pkg/mime/mediatype_test.go

--- a/src/pkg/mime/mediatype_test.go
+++ b/src/pkg/mime/mediatype_test.go
@@ -244,13 +244,33 @@ func TestParseMediaType(t *testing.T) {
 	}\n}\n\n+type badMediaTypeTest {\n+\tin  string\n+\terr string\n+}\n+\n+var badMediaTypeTests = []badMediaTypeTest{\n+\t{\"bogus ;=========\", \"mime: invalid media parameter\"},\n+\t{\"bogus/<script>alert</script>\", \"mime: expected token after slash\"},\n+\t{\"bogus/bogus<script>alert</script>\", \"mime: unexpected content after media subtype\"},\n+}\n+\n func TestParseMediaTypeBogus(t *testing.T) {\n-\tmt, params, err := ParseMediaType(\"bogus ;=========\")\n-\tif err == nil {\n-\t\tt.Fatalf(\"expected an error parsing invalid media type; got type %q, params %#v\", mt, params)\n-\t}\n-\tif err.Error() != \"mime: invalid media parameter\" {\n-\t\tt.Errorf(\"expected invalid media parameter; got error %q\", err)\n+\tfor _, tt := range badMediaTypeTests {\n+\t\tmt, params, err := ParseMediaType(tt.in)\n+\t\tif err == nil {\n+\t\t\tt.Errorf(\"ParseMediaType(%q) = nil error; want parse error\", tt.in)\n+\t\t\tcontinue\n+\t\t}\n+\t\tif err.Error() != tt.err {\n+\t\t\tt.Errorf(\"ParseMediaType(%q) = err %q; want %q\", tt.in, err.Error(), tt.err)\n+\t\t}\n+\t\tif params != nil {\n+\t\t\tt.Errorf(\"ParseMediaType(%q): got non-nil params on error\", tt.in)\n+\t\t}\n+\t\tif mt != \"\" {\n+\t\t\tt.Errorf(\"ParseMediaType(%q): got non-empty media type string on error\", tt.in)\n+\t\t}\n \t}\n }\n \n```

## コアとなるコードの解説

### `src/pkg/mime/mediatype.go` の変更

`ParseMediaType` 関数内で、`checkMediaTypeDisposition(mediatype)` がエラーを返した場合の処理が変更されています。

- **変更前**:
  ```go
  if err != nil {
      return
  }

この return は、ParseMediaType の戻り値である mediatype, params, err のうち、err にはエラーが設定されますが、mediatypeparams にはそれまでの処理で設定された値(中途半端な値や、関数の冒頭で宣言された際のゼロ値)がそのまま返されていました。

  • 変更後:
    if err != nil {
        return "", nil, err
    }
    
    エラーが発生した場合に、mediatype には空文字列 ""params には nil を明示的に設定して返しています。これにより、エラーが返された際には、有効なメディアタイプやパラメータが取得できなかったことが明確になり、呼び出し元はこれらの値を安全に無視できます。これはGo言語におけるエラーハンドリングのベストプラクティスに沿った変更です。

src/pkg/mime/mediatype_test.go の変更

テストファイルでは、TestParseMediaTypeBogus 関数が大幅に修正されています。

  • 変更前: 単一の不正なメディアタイプ文字列 \"bogus ;=========\" に対してのみテストを行っていました。エラーが返されることを確認するのみで、mt (mediatype) や params がどのような値になるかは確認していませんでした。

  • 変更後: badMediaTypeTest という構造体と badMediaTypeTests というスライスが導入され、複数の不正なメディアタイプ文字列とその期待されるエラーメッセージを定義しています。 ループ処理でこれらのテストケースを一つずつ実行し、以下の点を厳密に検証しています。

    1. ParseMediaType がエラーを返すこと。
    2. 返されたエラーメッセージが期待されるものと一致すること。
    3. 最も重要な点として、エラー発生時に paramsnil であること。
    4. エラー発生時に mt (mediatype) が空文字列 "" であること。

これらのテストの追加により、ParseMediaType がエラーを返した際に、不完全な結果を返さないことが保証されるようになりました。

関連リンク

参考にした情報源リンク