Goの標準ライブラリ、net/httpで作ったHTTPサーバに対して、netcatやtelnetでリクエストを送ると、なぜがレスポンスボディの後に「HTTP/1.1 400 Bad Request」が付いてくることに気が付きました。
$ nc localhost 8080
GET / HTTP/1.1
Host: localhost:8080
[ここまで入力したらENTERキーを押す]
HTTP/1.1 200 OK
Date: Sat, 09 May 2015 07:56:54 GMT
Content-Length: 13
Content-Type: text/plain; charset=utf-8
Hello, World
[ここでENTERキーを押す]
HTTP/1.1 400 Bad Request
一番最後の行に注目してください。なぜか最後に「Bad Request」がついてきます。ちなみに、レスポンスヘッダの「Content-Length」は13なので、最後の行はレスポンスボディではないことが分かります。
この現象を再現するには、次のコードでサーバを立てると再現できます。
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World\n")
})
http.ListenAndServe(":8080", nil)
}
このサーバは、go runコマンドで起動することができます。
$ go run main.go
この現象について調べていたら、GoのGoogleグループに「HTTP response sent but never closed and ending with "HTTP/1.1 400 Bad Request"」というスレッドを見つけました。このスレッドによれば、net/httpはHTTP 1.1であれば、リクエストヘッダーにConnection: close
が無い限り、Keep Aliveで通信を行うように作られているとのことです。これは、HTTP 1.1の仕様に従っているので、不具合ではありません。
従って、今回のケースでは、2回目のENTER押下をしていますが、それが空っぽのリクエストとしてサーバが認識した結果、Bad Requestのレスポンスが帰ってきたと理解できます。ですので、2回目のENTERの代わりに、リクエストをもう一度書くと、レスポンスを得ることができます。
$ nc localhost 8080
GET / HTTP/1.1
Host: localhost:8080
[ここまで書いてENTER押下]
HTTP/1.1 200 OK
Date: Sat, 09 May 2015 08:06:28 GMT
Content-Length: 13
Content-Type: text/plain; charset=utf-8
Hello, World
GET / HTTP/1.1
Host: localhost:8080
[更にここまで書いてENTER押下]
HTTP/1.1 200 OK
Date: Sat, 09 May 2015 08:06:31 GMT
Content-Length: 13
Content-Type: text/plain; charset=utf-8
Hello, World
[ENTER押下]
HTTP/1.1 400 Bad Request
最後の「HTTP/1.1 400 Bad Request」を受け取らないようにするには、ヘッダにConnection: close
を書きます。
$ nc localhost 8080
GET / HTTP/1.1
Host: localhost:8080
Connection: close
[ここまで書いてENTER押下]
HTTP/1.1 200 OK
Date: Sat, 09 May 2015 08:08:40 GMT
Content-Length: 13
Content-Type: text/plain; charset=utf-8
Connection: close
Hello, World
もちろん、HTTP 1.0でリクエストをすれば、Keep Aliveがデフォルトで無効の状態となり、最後の「HTTP/1.1 400 Bad Request」も受け取らなくなります。
$ nc localhost 8080
GET / HTTP/1.0
[ここまで書いてENTER押下]
HTTP/1.0 200 OK
Date: Sat, 09 May 2015 08:09:14 GMT
Content-Length: 13
Content-Type: text/plain; charset=utf-8
Hello, World