🍇

HTTP GET 少し深掘り

2023/08/06に公開

少し気になったのでなるべく詳しめに調べてみた。
一番単純そうなGETメソッドのみに絞る。

HTTP GETの主なフロー

  1. tcp接続する
  2. httpリクエストする。
  3. tcp切断する。

パケットの調査

以下のような単純なサーバとクライアントを書きキャプチャして内容を確認した。

  • server
package main

import (
	"fmt"
	"net/http"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Yo!")
	})
	http.ListenAndServe("localhost:8000", nil)
}
  • client
package main

import (
	"fmt"
	"net/http"
)

func main() {
	resp, _ := http.Get("http://localhost:8000")
	defer resp.Body.Close()
	fmt.Println(resp.Body)
}

調査結果

以下の図のような通信を行っている。

  • 今回初めて知った点
    httpを送信した後も確認用のACKを送っている。
    httpプロトコル自体はテキストを送っているだけのように見える。

クライアントの実装を変えて送ってみる

決まったフォーマットのテキストをtcp上で送っているだけということがわかったので少し実装を変えて試してみる。
クライアントの方が簡単そうなのでクライアントのみ。

  • httpをそのままかく

httpのテキストを以下の様に直に書いて、tcpで送る

package main

import (
	"fmt"
	"net"
)

func main() {
	conn, err := net.Dial("tcp", "localhost:8000")
	defer conn.Close()

	if err != nil {
		fmt.Println("error: ", err)
	}

	httpGetStr := "GET / HTTP/1.1\r\nHost: localhost:8000\r\nUser-Agent: curl/7.81.0\r\nAccept: */*\r\n\r\n"
	data := []byte(httpGetStr)
	_, err = conn.Write(data)
	if err != nil {
		fmt.Println("error: ", err)
	}

	// ここから読み取り
	readdata := make([]byte, 1024)
	count, _ := conn.Read(readdata)
	fmt.Println(string(readdata[:count]))
}
  • ソケットに書いてみる。
package main

import (
	"fmt"
	"syscall"
)

func main() {
	// Create a TCP socket
	sock, _ := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0)
	defer syscall.Close(sock)

	// Create Connection
	syscall.Connect(sock, &syscall.SockaddrInet4{
		Port: 8000,
		Addr: [4]byte{127, 0, 0, 1},
	})

	httpGet := "GET / HTTP/1.1\r\nHost: localhost:8000\r\nUser-Agent: curl/7.81.0\r\nAccept: */*\r\n\r\n"
	syscall.Write(sock, []byte(httpGet))
	response := make([]byte, 1024)
	n, _ := syscall.Read(sock, response)
	fmt.Println(string(response[:n]))
}

上記の実装でもHTTP GETをいちよ送れた。
syscall.Socket初めて使ったが、syscall.SOCK_STREAMを指定するとtcp用のソケットを作ってくれるらしい。
参考
たぶんtcpの3ウェイハンドシェイクはSocket関数で抽象化されているらしい。
今回使ったコード
https://github.com/ryutaro-asada/go-net-test/tree/main/cmd/http_test

Discussion