少し前にはてな界隈でも話題になった、Goならわかるシステムプログラミングという本を写経しています。
この本は、Go言語の勉強のために購入したのですが、Go言語というより、HTTPプロトコルなど、比較的程レイヤーの解説および実装となっていて、Go言語の勉強というよりも、TCPとは何か、UDPとは何か、OSにおけるファイルシステムとはどういうものなのかということを学べる書籍となっています。そういう意味で、今まで何気なく使っているものより少し下のレイヤーの技術を勉強するにはもってこいの本だと思います。その中で、「6.6.1 速度改善(1)」にあるp107ページのコードがあるのですが、「6.5.1 TCPソケットを使ったHTTPサーバー」の差分になっており、差分といってもどこをどう編集すればいいか少し戸惑ったので、diffで表現して見ました。
また、差分表記ではないものは、こちらにあります。
package main import ( "bufio" "fmt" + "io" "io/ioutil" "net" "net/http" "net/http/httputil" "strings" + "time" ) func main() { listener, err := net.Listen("tcp", "localhost:8888") if err != nil { panic(err) } fmt.Println("Server is running at localhost:8888") for { conn, err := listener.Accept() if err != nil { panic(err) } - fmt.Printf("Accept %v\n", conn.RemoteAddr()) - request, err := http.ReadRequest( - bufio.NewReader(conn)) - if err != nil { - panic(err) - } - dump, err := httputil.DumpRequest(request, true) - if err != nil { - panic(err) - } - fmt.Println(string(dump)) - response := http.Response{ - StatusCode: 200, - ProtoMajor: 1, - ProtoMinor: 0, - Body: ioutil.NopCloser(strings.NewReader("Hellow World\n")), - } - response.Write(conn) - conn.Close() + + go func() { + defer conn.Close() + fmt.Printf("Accept %v\n", conn.RemoteAddr()) + + for { + conn.SetReadDeadline(time.Now().Add(5 * time.Second)) + request, err := http.ReadRequest(bufio.NewReader(conn)) + if err != nil { + neterr, ok := err.(net.Error) + if ok && neterr.Timeout() { + fmt.Println("Timeout") + break + } else if err == io.EOF { + break + } + panic(err) + } + dump, err := httputil.DumpRequest(request, true) + if err != nil { + panic(err) + } + fmt.Println(string(dump)) + content := "Hello World\n" + + response := http.Response{ + StatusCode: 200, + ProtoMajor: 1, + ProtoMinor: 1, + ContentLength: int64(len(content)), + Body: ioutil.NopCloser( + strings.NewReader(content)), + } + response.Write(conn) + } + }() } }