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

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

このコミットは、EmacsのGoモード(go-mode.el)におけるGo Playgroundへのデータ送信に関するバグ修正です。具体的には、Go Playgroundに送信するHTTPリクエストのContent-Lengthヘッダが正しく設定されていなかった問題を修正し、UTF-8エンコーディングを明示的に適用することで、マルチバイト文字を含むコードが正しく扱われるようにしています。

コミット

commit 950c284b110edf7ccd53d6254c9b1640e325a6f8
Author: Dominik Honnef <dominik.honnef@gmail.com>
Date:   Thu Oct 10 16:49:19 2013 -0400

    misc/emacs: send correct content-length to the playground
    
    R=adonovan
    CC=golang-dev
    https://golang.org/cl/14548049

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

https://github.com/golang/go/commit/950c284b110edf7ccd53d6254c9b1640e325a6f8

元コミット内容

misc/emacs: send correct content-length to the playground

R=adonovan
CC=golang-dev
https://golang.org/cl/14548049

変更の背景

このコミットは、EmacsのGoモード(go-mode.el)からGo Playgroundにコードを送信する際に発生していた問題を解決するために行われました。以前の実装では、Emacsのバッファから取得した文字列をそのままHTTPリクエストのボディとして送信していましたが、この際にContent-Lengthヘッダがバイト数ではなく文字数に基づいて計算されてしまう可能性がありました。特に、UTF-8などのマルチバイト文字エンコーディングを使用している場合、1文字が複数バイトで表現されるため、文字数とバイト数が一致しません。

Go PlaygroundのようなWebサービスは、HTTPリクエストのContent-Lengthヘッダを信頼してリクエストボディの長さを判断します。もしContent-Lengthが実際のボディのバイト数と一致しない場合、サーバーはリクエストボディを正しく読み取ることができず、結果としてコードの実行エラーや予期せぬ動作を引き起こす可能性がありました。このコミットは、このエンコーディングに起因するContent-Lengthの不一致を解消し、Go Playgroundとの連携を安定させることを目的としています。

前提知識の解説

  • Go Playground: Go Playgroundは、Go言語のコードをブラウザ上で記述、実行、共有できるオンラインサービスです。Go言語の公式ウェブサイト(go.dev)で提供されており、Go言語の学習や簡単なコードのテストに広く利用されています。Go Playgroundは、ユーザーが入力したコードをサーバーサイドでコンパパイル・実行し、その結果をブラウザに返します。
  • EmacsのGoモード (go-mode.el): Emacsは、高度にカスタマイズ可能なテキストエディタであり、プログラミング言語ごとに「モード」と呼ばれる拡張機能を提供します。go-mode.elは、EmacsでGo言語のコードを編集するためのモードであり、シンタックスハイライト、インデント、コード補完、そしてGo Playgroundとの連携機能などを提供します。
  • HTTP Content-Length ヘッダ: HTTPプロトコルにおいて、Content-Lengthヘッダは、HTTPメッセージボディのオクテット(バイト)単位の長さを指定します。これは、サーバーがリクエストボディの終わりを正確に判断するために非常に重要です。このヘッダが正しくない場合、サーバーはボディを途中で切り捨てたり、余分なデータを読み込んだりする可能性があり、結果としてリクエストの処理に失敗します。
  • 文字エンコーディング (UTF-8): 文字エンコーディングは、文字をコンピュータが扱えるバイト列に変換する規則です。UTF-8は、Unicode文字を可変長のバイト列で表現するエンコーディング方式であり、世界中のほとんどの文字を表現できます。ASCII文字は1バイトで表現されますが、日本語の漢字やその他の特殊文字は2バイト以上で表現されます。このため、文字列の「文字数」と「バイト数」は、使用される文字エンコーディングによって異なる場合があります。
  • encode-coding-string (Emacs Lisp): Emacs Lispの関数で、文字列を指定されたエンコーディングでバイト列に変換します。この関数を使用することで、文字列のバイト数を正確に計算し、HTTPリクエストのContent-Lengthヘッダに設定することができます。

技術的詳細

このコミットの核心は、Emacs Lispのurl-request-data変数の設定方法の変更にあります。以前は、buffer-substring-no-properties関数で取得した文字列を直接url-request-dataに代入していました。この文字列はEmacs内部のエンコーディング(通常はUTF-8)で表現されていますが、HTTPリクエストとして送信される際には、そのバイト数を正確にContent-Lengthヘッダに反映させる必要があります。

問題は、EmacsのHTTPリクエスト送信ライブラリが、文字列のバイト数を自動的に正しく計算しない場合があることでした。特に、マルチバイト文字を含む文字列の場合、Content-Lengthが文字数で計算されてしまうと、実際のバイト数とずれが生じます。

このコミットでは、url-request-dataに代入する前に、encode-coding-string関数を使用して文字列を明示的に'utf-8エンコーディングでバイト列に変換しています。

(encode-coding-string
 (buffer-substring-no-properties start end)
 'utf-8)

これにより、url-request-dataには、Go Playgroundが期待するUTF-8エンコードされたバイト列が格納され、EmacsのHTTPリクエスト送信機能がこのバイト列の正確な長さをContent-Lengthヘッダとして設定できるようになります。結果として、Go Playgroundは送信されたコードを正しく解釈し、処理できるようになります。

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

変更はmisc/emacs/go-mode.elファイル内の以下の部分です。

--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -774,7 +774,10 @@ link in the kill ring.\"
   (let* ((url-request-method \"POST\")
          (url-request-extra-headers
           \'((\"Content-Type\" . \"application/x-www-form-urlencoded\")))\
-         (url-request-data (buffer-substring-no-properties start end))\
+         (url-request-data
+          (encode-coding-string
+           (buffer-substring-no-properties start end)\
+           \'utf-8))\
          (content-buf (url-retrieve\
                         \"http://play.golang.org/share\"\
                         (lambda (arg)\

コアとなるコードの解説

  • (url-request-method "POST"): HTTPリクエストのメソッドをPOSTに設定しています。Go Playgroundへのコード送信は通常POSTリクエストで行われます。
  • (url-request-extra-headers '(("Content-Type" . "application/x-www-form-urlencoded"))): HTTPリクエストの追加ヘッダを設定しています。Content-Typeapplication/x-www-form-urlencodedに設定しており、これはフォームデータをURLエンコードされた形式で送信することを示します。
  • (buffer-substring-no-properties start end): Emacsの現在のバッファから、startからendまでの範囲の文字列を取得します。この関数は、テキストのプロパティ(フォント、色など)を含まない純粋な文字列を返します。
  • (encode-coding-string ... 'utf-8): 取得した文字列をUTF-8エンコーディングでバイト列に変換します。この変換によって、マルチバイト文字が正しくバイト数としてカウントされるようになります。
  • (url-request-data ...): HTTPリクエストのボディとして送信するデータを設定します。修正前はbuffer-substring-no-propertiesの戻り値を直接設定していましたが、修正後はencode-coding-stringでUTF-8エンコードされたバイト列を設定しています。
  • (url-retrieve "http://play.golang.org/share" ...): 指定されたURL(Go Playgroundの共有エンドポイント)に対してHTTPリクエストを送信し、その結果を非同期で取得します。

この変更により、EmacsのGoモードからGo Playgroundへ送信されるコードのバイト数が正確にContent-Lengthヘッダに反映されるようになり、マルチバイト文字を含むコードでもGo Playgroundが正しく処理できるようになりました。

関連リンク

参考にした情報源リンク

  • Go Playgroundの動作に関する一般的な知識
  • HTTPプロトコルとContent-Lengthヘッダに関する一般的な知識
  • 文字エンコーディング(特にUTF-8)に関する一般的な知識
  • Emacs Lispのurl-retrieveおよび関連関数に関する一般的な知識
  • コミットメッセージと差分から読み取れる情報
  • Goのコードレビューシステム (Gerrit) のCL (Change-list) リンク: https://golang.org/cl/14548049 (現在はGoのGerritインスタンスはGitHubに移行しているため、このリンクは直接機能しない可能性がありますが、当時の変更履歴を辿る手がかりとなります。)
  • GitHubのコミットページ: https://github.com/golang/go/commit/950c284b110edf7ccd53d6254c9b1640e325a6f8