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

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

このコミットは、Go言語のhttpパッケージ内のいくつかの識別子(定数および関数)の可視性(visibility)を変更するものです。具体的には、外部に公開されていた(エクスポートされていた)識別子を、パッケージ内部でのみ利用可能な(アンエクスポートされた)識別子へと変更しています。これは、Go言語における識別子の命名規則を利用した典型的なリファクタリングであり、パッケージのAPIをより明確にし、内部実装の詳細を隠蔽することを目的としています。

コミット

  • コミットハッシュ: 5d41f55accddb6a8f0cd672dc27dae9e0779fdfe
  • 作者: Russ Cox rsc@golang.org
  • コミット日時: Fri Jan 16 11:06:42 2009 -0800

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

https://github.com/golang/go/commit/5d41f55accddb6a8f0cd672dc27dae9e0779fdfe

元コミット内容

    casify http

    R=r
    DELTA=33  (0 added, 0 deleted, 33 changed)
    OCL=22947
    CL=22949

変更の背景

このコミットの背景には、Go言語の設計思想における「可視性(Visibility)」の概念があります。Go言語では、識別子(変数、関数、型など)の最初の文字が大文字であるか小文字であるかによって、その識別子がパッケージ外部からアクセス可能(エクスポートされる)か、それともパッケージ内部でのみアクセス可能(アンエクスポートされる)かが決定されます。

httpパッケージは、HTTPプロトコルを扱うための重要な標準ライブラリです。初期の段階では、パッケージ内部で利用されるヘルパー関数や定数も、意図せずエクスポートされている場合がありました。これらの識別子がエクスポートされていると、パッケージの外部から直接アクセスできてしまい、将来的な内部実装の変更が困難になったり、ユーザーが内部の詳細に依存してしまうリスクがありました。

このコミットの目的は、httpパッケージのAPIを整理し、外部に公開する必要のない内部的なヘルパー関数や定数をアンエクスポートすることです。これにより、パッケージのインターフェースがよりクリーンになり、内部実装の変更が容易になり、利用者が誤って内部の詳細に依存することを防ぎます。コミットメッセージの "casify http" は、Go言語の命名規則における「大文字/小文字の区別」を利用して可視性を変更する、という意図を簡潔に表現しています。

前提知識の解説

Go言語におけるパッケージと可視性

Go言語では、コードは「パッケージ」という単位で整理されます。パッケージは、関連する機能や型をまとめるための基本的な構造です。Go言語の可視性ルールは非常にシンプルで、以下の原則に基づいています。

  1. エクスポートされた識別子 (Exported Identifiers):

    • 識別子の最初の文字が大文字である場合、その識別子はパッケージの外部からアクセス可能です。
    • これは、他のパッケージからimportして利用できることを意味します。
    • 例: func MyFunction(), type MyType, const MyConstant
  2. アンエクスポートされた識別子 (Unexported Identifiers):

    • 識別子の最初の文字が小文字である場合、その識別子はパッケージ内部でのみアクセス可能です。
    • 他のパッケージからは直接アクセスできません。
    • これらは通常、パッケージの内部実装の詳細を隠蔽するために使用されます。
    • 例: func myFunction(), type myType, const myConstant

このルールは、Go言語のAPI設計において非常に重要です。外部に公開すべきものだけをエクスポートし、内部的な実装はアンエクスポートすることで、パッケージの安定性と保守性を高めることができます。

HTTPプロトコルと関連概念

このコミットはHTTPプロトコルを扱うhttpパッケージに関連するため、HTTPの基本的な概念も理解しておくと良いでしょう。

  • HTTPリクエスト/レスポンス: クライアントとサーバー間で情報をやり取りするためのメッセージ形式。リクエストにはメソッド(GET, POSTなど)、URL、ヘッダー、ボディが含まれます。
  • ヘッダー: HTTPメッセージのメタデータ。Content-Type, User-Agent, Hostなど、様々な情報が含まれます。
  • URLエンコーディング: URLに使用できない文字(スペースなど)を%xx形式で表現する仕組み。

技術的詳細

このコミットの技術的な詳細は、主にGo言語の命名規則に基づいた識別子のリネームに集約されます。

  1. 定数のリネーム:

    • src/lib/http/request.goにおいて、HTTPリクエストの解析に関連する定数MaxLineLength, MaxValueLength, MaxHeaderLinesが、それぞれ_MaxLineLength, _MaxValueLength, _MaxHeaderLinesへとリネームされました。
    • プレフィックスにアンダースコア_が付与されていますが、Go言語の命名規則では、単に最初の文字を小文字にするだけでアンエクスポートされます。このアンダースコアは、おそらく内部的な定数であることをより明確にするための慣習的なものです。
  2. 関数のリネーム:

    • src/lib/http/request.go内のReadLineBytes, ReadLine, ReadKeyValue, ParseHTTPVersionといった関数が、それぞれreadLineBytes, readLine, readKeyValue, parseHTTPVersionへとリネームされました。
    • src/lib/http/server.go内のServeConnection関数がserveConnectionへとリネームされました。
    • src/lib/http/url.go内のIsHex, UnHex, GetScheme, Split関数が、それぞれishex, unhex, getscheme, splitへとリネームされました。

これらのリネームにより、これらの定数や関数はhttpパッケージの外部からは直接呼び出せなくなりました。これらはパッケージ内部で利用されるヘルパー的な役割を持つため、外部に公開する必要がないと判断されたものです。

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

このコミットによって変更されたファイルは以下の3つです。

  • src/lib/http/request.go
  • src/lib/http/server.go
  • src/lib/http/url.go

変更内容は、主に識別子の命名規則の変更(大文字から小文字への変更)と、それに伴う参照箇所の修正です。追加や削除されたコード行はなく、既存のコードのリファクタリングに特化しています。

コアとなるコードの解説

src/lib/http/request.go

このファイルでは、HTTPリクエストの解析に関連する定数と関数が変更されています。

  • 定数:

    --- a/src/lib/http/request.go
    +++ b/src/lib/http/request.go
    @@ -14,9 +14,9 @@ import (
     )
    
     const (
    -	MaxLineLength = 1024;	// assumed < bufio.DefaultBufSize
    -	MaxValueLength = 1024;
    -	MaxHeaderLines = 1024;
    +	_MaxLineLength = 1024;	// assumed < bufio.DefaultBufSize
    +	_MaxValueLength = 1024;
    +	_MaxHeaderLines = 1024;
     )
    

    MaxLineLength, MaxValueLength, MaxHeaderLinesといった定数が、_MaxLineLength, _MaxValueLength, _MaxHeaderLinesにリネームされました。これにより、これらの定数はパッケージ内部でのみ利用可能になります。

  • 関数:

    --- a/src/lib/http/request.go
    +++ b/src/lib/http/request.go
    @@ -46,14 +46,14 @@ export type Request struct {
     }
    
     // Read a line of bytes (up to \n) from b.
    -// Give up if the line exceeds MaxLineLength.
    +// Give up if the line exceeds _MaxLineLength.
     // The returned bytes are a pointer into storage in
     // the bufio, so they are only valid until the next bufio read.
    -func ReadLineBytes(b *bufio.BufRead) (p []byte, err *os.Error) {
    +func readLineBytes(b *bufio.BufRead) (p []byte, err *os.Error) {
     	if p, err = b.ReadLineSlice('\n'); err != nil {
     		return nil, err
     	}
    -	if len(p) >= MaxLineLength {
    +	if len(p) >= _MaxLineLength {
     		return nil, LineTooLong
     	}
    
    @@ -67,9 +67,9 @@ func ReadLineBytes(b *bufio.BufRead) (p []byte, err *os.Error) {
     	return p[0:i], nil
     }
    
    -// ReadLineByte, but convert the bytes into a string.
    -func ReadLine(b *bufio.BufRead) (s string, err *os.Error) {
    -	p, e := ReadLineBytes(b);
    +// readLineBytes, but convert the bytes into a string.
    +func readLine(b *bufio.BufRead) (s string, err *os.Error) {
    +	p, e := readLineBytes(b);
     	if e != nil {
     		return "", e
     	}
    @@ -80,8 +80,8 @@ func ReadLine(b *bufio.BufRead) (s string, err *os.Error) {
     // A key/value has the form Key: Value\r\n
    
     // and the Value can continue on multiple lines if each continuation line
     // starts with a space.
    -func ReadKeyValue(b *bufio.BufRead) (key, value string, err *os.Error) {
    -	line, e := ReadLineBytes(b);
    +func readKeyValue(b *bufio.BufRead) (key, value string, err *os.Error) {
    +	line, e := readLineBytes(b);
     	if e != nil {
     		return "", "", e
     	}
    @@ -127,12 +127,12 @@ func ReadKeyValue(b *bufio.BufRead) (key, value string, err *os.Error) {
     			b.UnreadByte();
    
     			// Read the rest of the line and add to value.
    -			if line, e = ReadLineBytes(b); e != nil {
    +			if line, e = readLineBytes(b); e != nil {
     				return "", "", e
     			}
     			value += " " + string(line);
    
    -			if len(value) >= MaxValueLength {
    +			if len(value) >= _MaxValueLength {
     				return "", "", ValueTooLong
     			}
     		}
    @@ -163,7 +163,7 @@ func atoi(s string, i int) (n, i1 int, ok bool) {
     }
    
     // Parse HTTP version: "HTTP/1.2" -> (1, 2, true).
    -func ParseHTTPVersion(vers string) (int, int, bool) {
    +func parseHTTPVersion(vers string) (int, int, bool) {
     	if vers[0:5] != "HTTP/" {
     		return 0, 0, false
     	}
    @@ -185,7 +185,7 @@ export func ReadRequest(b *bufio.BufRead) (req *Request, err *os.Error) {
    
     	// First line: GET /index.html HTTP/1.0
     	var s string;
    -	if s, err = ReadLine(b); err != nil {
    +	if s, err = readLine(b); err != nil {
     		return nil, err
     	}
    
    @@ -195,7 +195,7 @@ export func ReadRequest(b *bufio.BufRead) (req *Request, err *os.Error) {
     	}
     	req.method, req.rawurl, req.proto = f[0], f[1], f[2];
     	var ok bool;
    -	if req.pmajor, req.pminor, ok = ParseHTTPVersion(req.proto); !ok {
    +	if req.pmajor, req.pminor, ok = parseHTTPVersion(req.proto); !ok {
     		return nil, BadHTTPVersion
     	}
    
    @@ -208,13 +208,13 @@ export func ReadRequest(b *bufio.BufRead) (req *Request, err *os.Error) {
     	req.header = make(map[string] string);
     	for {
     		var key, value string;
    -		if key, value, err = ReadKeyValue(b); err != nil {
    +		if key, value, err = readKeyValue(b); err != nil {
     			return nil, err
     		}
     		if key == "" {
     			break
     		}
    -		if nheader++; nheader >= MaxHeaderLines {
    +		if nheader++; nheader >= _MaxHeaderLines {
     			return nil, HeaderTooLong
     		}
    

    ReadLineBytes, ReadLine, ReadKeyValue, ParseHTTPVersionといった関数が小文字始まりにリネームされ、それに伴い内部での呼び出し箇所も修正されています。これらの関数はHTTPリクエストのパース処理の内部的なヘルパーであり、外部に公開する必要がないため、アンエクスポートされました。

src/lib/http/server.go

このファイルでは、HTTPサーバーの接続処理に関連する関数が変更されています。

--- a/src/lib/http/server.go
+++ b/src/lib/http/server.go
@@ -17,7 +17,7 @@ import (
 )

 // Serve a new connection.
-func ServeConnection(fd net.Conn, raddr string, f *(*Conn, *Request)) {
+func serveConnection(fd net.Conn, raddr string, f *(*Conn, *Request)) {
 	c, err := NewConn(fd);
 	if err != nil {
 		return
@@ -48,7 +48,7 @@ export func Serve(l net.Listener, f *(*Conn, *Request)) *os.Error {
 		if e != nil {
 			return e
 		}
-		go ServeConnection(rw, raddr, f)
+		go serveConnection(rw, raddr, f)
 	}
 	panic("not reached")
 }

ServeConnection関数がserveConnectionにリネームされ、Serve関数からの呼び出しも修正されました。この関数は個々の接続を処理する内部的なルーチンであり、外部から直接呼び出されることを意図していません。

src/lib/http/url.go

このファイルでは、URLの解析に関連する関数が変更されています。

--- a/src/lib/http/url.go
+++ b/src/lib/http/url.go
@@ -16,7 +16,7 @@ export var (
 	BadURL = os.NewError("bad url syntax")
 )

-func IsHex(c byte) bool {
+func ishex(c byte) bool {
 	switch {
 	case '0' <= c && c <= '9':
 		return true;
@@ -28,7 +28,7 @@ func IsHex(c byte) bool {
 	return false
 }

-func UnHex(c byte) byte {
+func unhex(c byte) byte {
 	switch {
 	case '0' <= c && c <= '9':
 		return c - '0';
@@ -47,7 +47,7 @@ export func URLUnescape(s string) (string, *os.Error) {
 	for i := 0; i < len(s); {
 		if s[i] == '%' {
 			n++;
-			if !IsHex(s[i+1]) || !IsHex(s[i+2]) {
+			if !ishex(s[i+1]) || !ishex(s[i+2]) {
 				return "", BadURL;
 			}
 			i += 3
@@ -64,7 +64,7 @@ export func URLUnescape(s string) (string, *os.Error) {
 	j := 0;
 	for i := 0; i < len(s); {
 		if s[i] == '%' {
-			t[j] = UnHex(s[i+1]) << 4 | UnHex(s[i+2]);
+			t[j] = unhex(s[i+1]) << 4 | unhex(s[i+2]);
 			j++;
 			i += 3;
 		} else {
@@ -91,7 +91,7 @@ export type URL struct {
 // Maybe rawurl is of the form scheme:path.
 // (Scheme must be [a-zA-Z][a-zA-Z0-9+-.]*)
 // If so, return scheme, path; else return "", rawurl.
-func GetScheme(rawurl string) (scheme, path string, err *os.Error) {
+func getscheme(rawurl string) (scheme, path string, err *os.Error) {
 	for i := 0; i < len(rawurl); i++ {
 		c := rawurl[i];
 		switch {
@@ -114,7 +114,7 @@ func GetScheme(rawurl string) (scheme, path string, err *os.Error) {
 // Maybe s is of the form t c u.
 // If so, return t, c u (or t, u if cutc == true).
 // If not, return s, "".
-func Split(s string, c byte, cutc bool) (string, string) {
+func split(s string, c byte, cutc bool) (string, string) {
 	for i := 0; i < len(s); i++ {
 		if s[i] == c {
 			if cutc {
@@ -134,9 +134,9 @@ export func ParseURL(rawurl string) (url *URL, err *os.Error) {
 	url = new(URL);
 	url.raw = rawurl;

-	// Split off possible leading "http:", "mailto:", etc.
+	// split off possible leading "http:", "mailto:", etc.
 	var path string;
-	if url.scheme, path, err = GetScheme(rawurl); err != nil {
+	if url.scheme, path, err = getscheme(rawurl); err != nil {
 		return nil, err
 	}
 	url.rawpath = path;
@@ -144,14 +144,14 @@ export func ParseURL(rawurl string) (url *URL, err *os.Error) {
 	// RFC 2396: a relative URI (no scheme) has a ?query,
 	// but absolute URIs only have query if path begins with /
 	if url.scheme == "" || len(path) > 0 && path[0] == '/' {
-		path, url.query = Split(path, '?', true);
+		path, url.query = split(path, '?', true);
 		if url.query, err = URLUnescape(url.query); err != nil {
 			return nil, err
 		}
 	}

 	// Maybe path is //authority/path
 	if len(path) > 2 && path[0:2] == "//" {
-		url.authority, path = Split(path[2:len(path)], '/', false);
+		url.authority, path = split(path[2:len(path)], '/', false);
 	}

-	// If there's no @, Split's default is wrong.  Check explicitly.
+	// If there's no @, split's default is wrong.  Check explicitly.
 	if strings.index(url.authority, "@") < 0 {
 		url.host = url.authority;
 	} else {
-		url.userinfo, url.host = Split(url.authority, '@', true);
+		url.userinfo, url.host = split(url.authority, '@', true);
 	}

 	// What's left is the path.
@@ -174,7 +174,7 @@ export func ParseURLReference(rawurlref string) (url *URL, err *os.Error) {
 // A URL reference is a URL with #frag potentially added.  Parse it.\n
 export func ParseURLReference(rawurlref string) (url *URL, err *os.Error) {
 	// Cut off #frag.
-	rawurl, frag := Split(rawurlref, '#', true);
+	rawurl, frag := split(rawurlref, '#', true);
 	if url, err = ParseURL(rawurl); err != nil {
 		return nil, err
 	}

IsHex, UnHex, GetScheme, Splitといった関数が小文字始まりにリネームされ、それに伴い内部での呼び出し箇所も修正されています。これらの関数はURLのパースやエンコーディング/デコーディングの内部的なヘルパーであり、外部に公開する必要がないため、アンエクスポートされました。

全体として、このコミットはGo言語の可視性ルールを厳密に適用し、httpパッケージの内部実装と公開APIを明確に分離するための重要なリファクタリングです。これにより、パッケージの保守性が向上し、将来的な変更が容易になります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (Effective Go, Packages)
  • Go言語のソースコード (httpパッケージ)
  • Gitのコミットログと差分表示