DesignAssembler

備忘録に近い

HTTPクライアントの実装

Webを支える技術を読んでいます。

Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)

Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)

Webエンジニアなら知ってて当然の基礎の基礎が載っている本です。

この前基礎の基礎を聞かれた時ハッキリと答えられなくて悔しかったので読んでいます。

今回はhttpリクエスト周りです。

httpリクエスト投げるコード書いた

オブジェクト指向っぽく書きました。(書いたつもりです)

optionsメソッド投げて返ってきたAllowed Methodから動的にメソッドを生成すると面白そうですね。

optionsメソッドをそもそも受け付けないサーバーがあるからキツそうですけど・・・

require 'socket'

class HttpClient
  attr_accessor :uri, :port, :path

  def initialize(uri, path,  port)
    @uri = uri
    @path = path
    @port = port
    #@socket = TCPSocket.open(uri, port)
  end

  [:get, :post, :put, :delete, :head, :options].each do |method|
    define_method(method) do |content = nil, type = "text/plain"|
      @socket = TCPSocket.open(uri, port)
      #修正 : ソケットのopenを各メソッドに任せる。
      @socket << "#{method.to_s.swapcase} #{self.path} HTTP/1.1\r\n"
      @socket << "Host: #{self.uri}\r\n"
      @socket << "Content-Type: #{type}; charset=utf-8\r\n"
      unless content.nil?
        @socket << "Content-Length: #{content.length}\r\n"
        @socket << "\r\n#{content}"
      end
      @socket << "Connection: close\r\n"
      @socket << "\r\n"
      puts @socket.read
      @socket.close
    end
  end
end

HttpClient.new("example.com", "/", 80).get
HttpClient.new("example.com", "/", 80).post("こんにちは")
HttpClient.new("example.com", "/", 80).options

各ヘッダーの意味

  • Host: #{ホスト名}
    このホストにリクエスト投げるぞという意味です。

  • Content-Type: #{タイプ}; charset=utf-8
    文字コードとタイプの指定です。ここからはMIMEコード(メディアタイプ)の指定です。

  • Content-Length:

その名の通りメッセージのサイズです。

最初Content-Lengthヘッダーを付けずにexample.comにpost投げつけたら以下のメッセージが返ってきました。

HTTP/1.0 411 Length Required
Content-Type: text/html
Content-Length: 357
Connection: close
Date: Fri, 29 Apr 2016 09:52:50 GMT
Server: ECSF (pae/3788)

Content-Length付けてリクエスト送信すると正常にレスポンスが返ってきました

HTTP/1.0 200 OK
Accept-Ranges: bytes
Cache-Control: max-age=604800
Content-Type: text/html
Date: Fri, 29 Apr 2016 09:55:32 GMT
Etag: "359670651"
Expires: Fri, 06 May 2016 09:55:32 GMT
Last-Modified: Fri, 09 Aug 2013 23:54:35 GMT
Server: EOS (lax004/2816)
Content-Length: 1270
Connection: close

405 Not Allowedが返ってくると思ったんですがちゃんとpostされてますね。空を切っている感。

HTTP/1.0とHTTP/1.1の違い

最初HTTP/1.1でレスポンスが返ってくるのが遅くて困っていました。

調べると以下のヘッダーが足りなかったみたいです。

Connection: close

Connectionヘッダーについて

HTTP/1.0とHTTP/1.1で変わった点に接続の継続性があります。これを表すのがConnectionヘッダーになります。具体的には、

Connection: keep-aliveにすると繋ぎっぱなしになり、 Connection: closeにするとTCPコネクションが閉じます。

HTTP/1.0では要求ごとに接続されていたのですが、HTTP/1.1ではコネクションを閉じない限り接続が続きます。つまりデフォルトでkeep-aliveになりました。

つまり、タイムアウトになるまでTCPコネクション繋ぎっぱなしにしていたことがレスポンスが遅かった原因です。

Connection: closeを書いていないリクエストのレスポンスにはConnection: closeが含まれていません。コネクションはサーバーアプリケーションが切ったようです。

HTTP/1.1 200 OK
Allow: OPTIONS, GET, HEAD, POST
Cache-Control: max-age=604800
Date: Fri, 29 Apr 2016 11:43:16 GMT
Expires: Fri, 06 May 2016 11:43:16 GMT
Server: EOS (lax004/280C)
x-ec-custom-error: 1
Content-Length: 0

チャンクやキャッシュは次回にします。

参考

http://www.cresc.co.jp/tech/java/Servlet_Tutorial/Lesson_36.htm