[インデックス 12389] ファイルの概要
このコミットは、Go言語のドキュメンテーションツールであるgodoc
の表示に関するバグ修正です。具体的には、godoc
が生成するHTMLページ内で、ソースコードへのリンクに余分なスラッシュ(/
)が含まれてしまい、ブラウザでのDNS解決エラーを引き起こす問題を解決しています。
コミット
commit 0eb4df0bc8e32fe68d7954055ee36e24e33dc15e
Author: Ugorji Nwoke <ugorji@gmail.com>
Date: Mon Mar 5 15:36:33 2012 -0500
cmd/godoc: remove extra / in paths
If I click on links which should send you to source code (e.g. type, function, etc),
the link is to //src/... (instead of /src/...).
This causes a DNS resolution failure on the browser.
Quick fix is to remove the leading / from package.html
(since godoc.go src links automatically add a leading / as necessary).
Fixes #3193.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5730059
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0eb4df0bc8e32fe68d7954055ee36e24e33dc15e
元コミット内容
このコミットの目的は、godoc
が生成するHTMLページ内のソースコードへのリンクが誤って二重スラッシュ(//
)で始まることによって発生する、ブラウザでのDNS解決エラーを修正することです。具体的には、型や関数などのソースコードへのリンクが//src/...
の形式になってしまい、これがブラウザで正しく解釈されず、リンク切れやエラーの原因となっていました。
修正は、lib/godoc/package.html
テンプレートファイルから、posLink_url
関数によって生成されるパスの先頭にある余分なスラッシュを削除するというものです。これは、godoc.go
がソースリンクを生成する際に、必要に応じて自動的に先頭のスラッシュを追加するため、package.html
側で明示的に追加する必要がないという前提に基づいています。
この変更は、Issue #3193を修正するものであり、Gerritの変更リストhttps://golang.org/cl/5730059
に関連しています。
変更の背景
Go言語の公式ドキュメンテーションツールであるgodoc
は、Goのソースコードから自動的にドキュメントを生成し、Webブラウザで閲覧可能な形式で提供します。このツールは、Goエコシステムにおいて非常に重要な役割を担っており、開発者がライブラリやパッケージのAPIを理解し、その実装を探索する上で不可欠です。
このコミットが行われた2012年当時、godoc
はまだ活発に開発されており、ユーザーからのフィードバックに基づいて機能改善やバグ修正が頻繁に行われていました。このバグは、ユーザーがgodoc
で表示されるドキュメントからソースコードへのリンクをクリックした際に、予期せぬエラーが発生するというものでした。具体的には、リンクのURLが//src/...
のように二重スラッシュで始まってしまうため、多くのブラウザがこれを不正なURLとして解釈し、DNS解決に失敗したり、ページが見つからないというエラーを表示したりしていました。これはユーザーエクスペリエンスを著しく損なう問題であり、早急な修正が求められていました。
この問題の根本原因は、package.html
テンプレート内でリンクを生成する際に、すでにgodoc.go
側でパスの先頭にスラッシュが追加されることを考慮せずに、テンプレート側でも明示的にスラッシュを追加していたことにありました。結果として、パスが二重にスラッシュで始まってしまうという冗長な状態になっていたのです。
前提知識の解説
Go言語のgodoc
ツール
godoc
は、Go言語のソースコードからドキュメントを生成し、Webブラウザで閲覧可能にするためのツールです。Goのパッケージ、関数、型、変数などの定義に付随するコメント(GoDocコメント)を解析し、整形されたHTMLドキュメントとして表示します。これにより、開発者はコードとドキュメントを密接に連携させ、常に最新のドキュメントを維持することができます。godoc
は、ローカルで実行して自身のプロジェクトのドキュメントを閲覧したり、pkg.go.dev
のような公式のGoパッケージサイトで公開されているドキュメントを生成したりするために利用されます。
HTMLテンプレートとパスの生成
Webアプリケーションでは、動的なコンテンツを生成するためにHTMLテンプレートがよく使用されます。Go言語では、html/template
パッケージなどがこれに該当します。テンプレートは、プレースホルダーや制御構造(条件分岐、ループなど)を含んだHTMLの骨格であり、実行時にデータが埋め込まれて最終的なHTMLが出力されます。
このコミットで関連するのは、package.html
というテンプレートファイルです。このファイルは、Goのパッケージのドキュメントページを生成するためのテンプレートとして機能します。テンプレート内では、{{posLink_url .Decl $.FSet}}
のような構文が使われており、これはposLink_url
という関数を呼び出して、ソースコードへのリンクURLを生成していることを示しています。
Webにおけるパス(URL)の指定方法には、絶対パスと相対パスがあります。
- 絶対パス: ドメインのルートからの完全なパスを指定します。例えば、
/src/pkg/foo/bar.go
のように、常にスラッシュで始まります。 - 相対パス: 現在のページのURLを基準としたパスを指定します。例えば、
../bar.go
のように、現在のディレクトリからの相対的な位置を示します。
この問題では、絶対パスの指定において、意図せず二重スラッシュが発生していました。
DNS解決
DNS(Domain Name System)は、インターネット上のドメイン名(例: google.com
)をIPアドレス(例: 172.217.160.142
)に変換するシステムです。WebブラウザがURLにアクセスする際、まずDNSを使ってドメイン名をIPアドレスに解決し、そのIPアドレスを持つサーバーに接続します。
URLのパス部分に//
のような不正な形式が含まれている場合、ブラウザはそれをプロトコル相対URL(例: //example.com/path
)として解釈しようとすることがあります。しかし、このバグのケースでは、//src/...
のようにドメイン名がない状態で二重スラッシュが続くため、ブラウザはこれを有効なURLとして認識できず、DNS解決エラーや「ページが見つかりません」といったエラーを引き起こしていました。
Gerrit
Gerritは、Gitリポジトリに対するコードレビューと変更管理のためのWebベースのツールです。Googleによって開発され、Goプロジェクトのような大規模なオープンソースプロジェクトで広く利用されています。開発者は変更をGerritにアップロードし、他の開発者からのレビューを受けてから、最終的にリポジトリにマージされます。コミットメッセージに含まれるhttps://golang.org/cl/5730059
は、この変更がGerrit上でレビューされた際の変更リスト(Change-List)のURLを示しています。
技術的詳細
このバグは、godoc
のWebサーバーがHTMLページを生成する際のパス構築ロジックの不整合に起因していました。
godoc
は、Goのソースコードを解析し、その構造(パッケージ、型、関数など)を表現するデータモデルを構築します。そして、このデータモデルをHTMLテンプレートに渡し、最終的なHTMLドキュメントを生成します。ソースコードへのリンクは、通常、godoc
が提供する内部的なパス生成ロジック(この場合はposLink_url
関数)によって生成されます。
問題の箇所は、lib/godoc/package.html
テンプレートファイル内にありました。このテンプレートは、関数や型の定義など、特定の要素のソースコードへのリンクを生成する際に、以下のようなHTML構造を使用していました。
<a href="/{{posLink_url .Decl $.FSet}}">{{$name_html}}</a>
ここで、posLink_url .Decl $.FSet
は、ソースコード内の特定の宣言(Decl
)の位置に基づいて、そのソースファイルへのパスを生成する関数呼び出しです。例えば、この関数が/src/pkg/foo/bar.go#L10
のようなパスを返すとします。
しかし、godoc.go
(godoc
の主要なロジックを実装しているファイル)の内部では、既に生成されるURLの先頭にスラッシュを追加する処理が行われていました。つまり、posLink_url
が返すパスは、既にルートからの絶対パスとして完全な形式(例: src/pkg/foo/bar.go#L10
)であるか、あるいはgodoc.go
側で先頭にスラッシュが追加されることを前提とした相対パス(例: src/pkg/foo/bar.go#L10
)であるべきでした。
このコミットの作者のコメントによると、「godoc.go
のソースリンクは必要に応じて自動的に先頭のスラッシュを追加する」とあります。これは、posLink_url
が返す値は先頭にスラッシュを含まない形式(例: src/pkg/foo/bar.go#L10
)であり、godoc.go
側でこれにスラッシュを追加して/src/pkg/foo/bar.go#L10
という完全なURLを生成する、という設計意図があったことを示唆しています。
しかし、package.html
テンプレートでは、posLink_url
の出力の前に明示的に/
を追加していました。
<a href="/{{posLink_url .Decl $.FSet}}">
この結果、posLink_url
がsrc/pkg/foo/bar.go#L10
を返した場合、最終的なhref
属性の値は//src/pkg/foo/bar.go#L10
となってしまい、二重スラッシュが発生していました。
ブラウザは、//
で始まるURLを「プロトコル相対URL」として解釈しようとします。これは、現在のページのプロトコル(http:
またはhttps:
)を使用して、そのプロトコルと二重スラッシュの後に続くドメイン名にアクセスしようとするものです。しかし、このケースでは//src/...
のようにドメイン名が続くわけではないため、ブラウザはこれを不正なURLとして扱い、DNS解決エラーやファイルが見つからないというエラーを報告していました。
修正は単純で、package.html
テンプレートから余分な先頭のスラッシュを削除することでした。
<a href="{{posLink_url .Decl $.FSet}}">
これにより、posLink_url
が返すパス(例: src/pkg/foo/bar.go#L10
)に、godoc.go
側で自動的に追加されるスラッシュが結合され、最終的に正しい形式のURL(例: /src/pkg/foo/bar.go#L10
)が生成されるようになりました。
コアとなるコードの変更箇所
変更はlib/godoc/package.html
ファイルのみです。
--- a/lib/godoc/package.html
+++ b/lib/godoc/package.html
@@ -93,7 +93,7 @@
{{range .Funcs}}
{{/* Name is a string - no need for FSet */}}
{{$name_html := html .Name}}
- <h2 id="{{$name_html}}">func <a href="/{{posLink_url .Decl $.FSet}}">{{$name_html}}</a></h2>
+ <h2 id="{{$name_html}}">func <a href="{{posLink_url .Decl $.FSet}}">{{$name_html}}</a></h2>
<pre>{{node_html .Decl $.FSet}}</pre>
{{comment_html .Doc}}
{{example_html .Name $.Examples $.FSet}}
@@ -101,7 +101,7 @@
{{range .Types}}
{{$tname := .Name}}
{{$tname_html := html .Name}}
- <h2 id="{{$tname_html}}">type <a href="/{{posLink_url .Decl $.FSet}}">{{$tname_html}}</a></h2>
+ <h2 id="{{$tname_html}}">type <a href="{{posLink_url .Decl $.FSet}}">{{$tname_html}}</a></h2>
<pre>{{node_html .Decl $.FSet}}</pre>
{{comment_html .Doc}}
@@ -119,7 +119,7 @@
{{range .Funcs}}
{{$name_html := html .Name}}
- <h3 id="{{$name_html}}">func <a href="/{{posLink_url .Decl $.FSet}}">{{$name_html}}</a></h3>
+ <h3 id="{{$name_html}}">func <a href="{{posLink_url .Decl $.FSet}}">{{$name_html}}</a></h3>
<pre>{{node_html .Decl $.FSet}}</pre>
{{comment_html .Doc}}
{{example_html .Name $.Examples $.FSet}}
@@ -127,7 +127,7 @@
{{range .Methods}}
{{$name_html := html .Name}}
- <h3 id="{{$tname_html}}.{{$name_html}}">func ({{html .Recv}}) <a href="/{{posLink_url .Decl $.FSet}}">{{$name_html}}</a></h3>
+ <h3 id="{{$tname_html}}.{{$name_html}}">func ({{html .Recv}}) <a href="{{posLink_url .Decl $.FSet}}">{{$name_html}}</a></h3>
<pre>{{node_html .Decl $.FSet}}</pre>
{{comment_html .Doc}}
{{$name := printf "%s_%s" $tname .Name}}
この差分は、package.html
内の4つの異なる箇所で、href
属性の値の先頭から余分なスラッシュ(/
)が削除されたことを示しています。
コアとなるコードの解説
変更された行はすべて、<a>
タグのhref
属性内でposLink_url
関数が呼び出されている部分です。
元のコード:
<a href="/{{posLink_url .Decl $.FSet}}">{{$name_html}}</a>
修正後のコード:
<a href="{{posLink_url .Decl $.FSet}}">{{$name_html}}</a>
この変更のポイントは、posLink_url
関数が返す値の前にあった/
が削除されたことです。
.Decl
: これは、GoのAST(Abstract Syntax Tree)における宣言(Declaration)を表すオブジェクトです。関数、型、変数などの定義が含まれます。$.FSet
: これは、ファイルセット(FileSet)を表すオブジェクトです。Goのソースコードのファイルと行番号のマッピング情報を含んでいます。
posLink_url
関数は、これらの情報(どの宣言の、どのファイルセット内の、どの位置にあるか)を基に、そのソースコードへのパスを生成します。コミットメッセージにあるように、godoc.go
のロジックが、このposLink_url
が返すパスの先頭に自動的にスラッシュを追加するようになっています。
したがって、package.html
テンプレート側でさらにスラッシュを追加してしまうと、結果的に//
という二重スラッシュが生成され、これがブラウザで不正なURLとして扱われる原因となっていました。
この修正により、package.html
はposLink_url
が返す「スラッシュなしのパス」(例: src/pkg/foo/bar.go#L10
)をそのままhref
属性に渡し、godoc.go
が最終的に正しい「スラッシュありの絶対パス」(例: /src/pkg/foo/bar.go#L10
)を生成するようになります。これにより、ブラウザはリンクを正しく解釈し、ソースコードへのナビゲーションが正常に行われるようになりました。
関連リンク
- Go Issue #3193: cmd/godoc: links to source code have extra / (Google Codeのアーカイブ)
- Gerrit Change-List 5730059: https://golang.org/cl/5730059
参考にした情報源リンク
- GoDoc: https://pkg.go.dev/cmd/godoc
- Go言語の
html/template
パッケージ: https://pkg.go.dev/html/template - Gerrit Code Review: https://www.gerritcodereview.com/
- DNS (Domain Name System) の仕組み: https://www.cloudflare.com/ja-jp/learning/dns/what-is-dns/
- URLのパスについて (絶対パスと相対パス): https://developer.mozilla.org/ja/docs/Learn/Common_questions/What_is_a_URL
- プロトコル相対URL: https://developer.mozilla.org/ja/docs/Web/HTTP/URLs_and_path_formats (「プロトコル相対URL」のセクションを参照)
- Go言語のAST (Abstract Syntax Tree): https://pkg.go.dev/go/ast
- Go言語のFileSet: https://pkg.go.dev/go/token#FileSet