概要
- できるだけ生に近い HTTP リクエスト情報を調査するために Web サーバを作る
- Ruby の標準ライブラリのみを使用する
- Web サーバを macOS Catalina で動作させて curl からの HTTP リクエストを確認する
- Web サーバを Heroku で動作させて curl からの HTTP リクエストを確認する
Web サーバのソースコード
Ruby 標準ライブラリのみを使用する。
マルチスレッドには非対応。
myserver.rb
require 'socket'
# 標準出力を同期モードに設定
$stdout.sync = true
# 接続を受け付けるポート番号を決定
# 環境変数 PORT が設定されているならそれを設定
port = 8000
port = ENV['PORT'].to_i if ENV['PORT']
# サーバー接続をオープン
server = TCPServer.open(port)
# HTTP リクエストを待ち続ける
loop do
begin
# TCPSocket オブジェクトを取得
socket = server.accept
# 受け付けた日時を出力
puts "[info]#{Time.new}"
# HTTP リクエスト開始行を出力
if not req_start_line = socket.gets
puts '[info]req_start_line is nil'
next
end
puts "#{req_start_line}"
# HTTP リクエストヘッダーを1行ずつ出力
while req_header = socket.gets.chomp
puts "#{req_header}"
break if req_header == '' # ヘッダー終了
# Content-Length ヘッダーがあれば値を変数にセット
h = req_header.split(':')
content_length = h[1].strip.to_i if h[0].strip.downcase == 'content-length'
end
# Content-Length がある場合はボディを出力
if content_length != nil
puts socket.read(content_length)
end
# HTTP レスポンスを返す
# 本文データ
body = "<html><body>Hello, world</body></html>\r\n"
# ステータス行
socket.write "HTTP/1.1 200 OK\r\n"
# ヘッダー
socket.write "Server: #{RUBY_DESCRIPTION}\r\n"
socket.write "Content-Type: text/html; charset=utf-8\r\n"
socket.write "Content-Length: #{body.bytesize}\r\n"
socket.write "Connection: close\r\n"
# 空行
socket.write "\r\n"
# 本文
socket.write body
rescue => e
puts e.full_message
ensure
# HTTP 接続を閉じる
puts '[info]close this socket'
socket.close
end
end
server.close
macOS Catalina で動かす
macOS Catalina + Ruby 2.7.0 の環境で Web サーバプログラム myserver.rb を起動する。
$ ruby myserver.rb
curl から HTTP GET リクエストする例
クライアント側のコマンドと出力結果。
$ curl -i -H 'small: sss' -H 'LARGE: LLL' http://localhost:8000/foo/?aaa=xxx
HTTP/1.1 200 OK
Server: ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-darwin19]
Content-Type: text/html; charset=utf-8
Content-Length: 40
Connection: close
<html><body>Hello, world</body></html>
サーバ側の出力結果。
[info]2020-02-04 08:03:08 +0900
GET /foo/?aaa=xxx HTTP/1.1
Host: localhost:8000
User-Agent: curl/7.68.0
Accept: */*
small: sss
LARGE: LLL
[info]close this socket
curl から HTTP POST リクエストする例
クライアント側のコマンドと出力結果。
$ curl -i -H 'Content-Type: application/json' http://localhost:8000/bar/ -d '{"foo": {"bar": ["あいうえお"]}}'
HTTP/1.1 200 OK
Server: ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-darwin19]
Content-Type: text/html; charset=utf-8
Content-Length: 40
Connection: close
<html><body>Hello, world</body></html>
サーバ側の出力結果。
[info]2020-02-04 08:03:14 +0900
POST /bar/ HTTP/1.1
Host: localhost:8000
User-Agent: curl/7.68.0
Accept: */*
Content-Type: application/json
Content-Length: 37
{"foo": {"bar": ["あいうえお"]}}
[info]close this socket
Heroku で動かす
Heroku + Ruby 2.7.0 の環境で Web サーバを起動する。
できるだけ生に近い HTTP リクエストを調査したいが、Heroku のリバースプロキシが間に入るためリクエストヘッダがいくつか増えてしまう問題がある(許容するしかない)。
Heroku にデプロイするために必要なファイル
サーバプログラム myserver.rb 以外に Gemfile, Gemfile.lock, Procfile が必要。
Gemfile
ruby '2.7.0'
Gemfile.lock
Gemfile.lock
GEM
specs:
PLATFORMS
ruby
DEPENDENCIES
RUBY VERSION
ruby 2.7.0p0
BUNDLED WITH
2.1.2
Procfile
web: ruby myserver.rb
curl から HTTP GET リクエストする例
クライアント側のコマンドと出力結果。
$ curl -i -H 'small: sss' -H 'LARGE: LLL' https://example.herokuapp.com/foo/?aaa=xxx
HTTP/1.1 200 OK
Date: Mon, 03 Feb 2020 23:03:28 GMT
Connection: keep-alive
Server: ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux]
Content-Type: text/html; charset=utf-8
Content-Length: 40
Via: 1.1 vegur
<html><body>Hello, world</body></html>
サーバ側の出力結果。
[info]2020-02-03 23:03:28 +0000
GET /foo/?aaa=xxx HTTP/1.1
Host: example.herokuapp.com
Connection: close
User-Agent: curl/7.68.0
Accept: */*
Small: sss
Large: LLL
X-Request-Id: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
X-Forwarded-For: XXX.XXX.XXX.XXX
X-Forwarded-Proto: https
X-Forwarded-Port: 443
Via: 1.1 vegur
Connect-Time: 1
X-Request-Start: 1580771008907
Total-Route-Time: 0
[info]close this socket
curl から HTTP POST リクエストする例
クライアント側のコマンドと出力結果。
$ curl -i -H 'Content-Type: application/json' https://example.herokuapp.com/bar/ -d '{"foo": {"bar": ["あいうえお"]}}'
HTTP/1.1 200 OK
Date: Mon, 03 Feb 2020 23:03:36 GMT
Connection: keep-alive
Server: ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux]
Content-Type: text/html; charset=utf-8
Content-Length: 40
Via: 1.1 vegur
<html><body>Hello, world</body></html>
サーバ側の出力結果。
[info]2020-02-03 23:03:37 +0000
POST /bar/ HTTP/1.1
Host: example.herokuapp.com
Connection: close
User-Agent: curl/7.68.0
Accept: */*
Content-Type: application/json
X-Request-Id: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
X-Forwarded-For: XXX.XXX.XXX.XXX
X-Forwarded-Proto: https
X-Forwarded-Port: 443
Via: 1.1 vegur
Connect-Time: 1
X-Request-Start: 1580771017243
Total-Route-Time: 0
Content-Length: 37
{"foo": {"bar": ["あいうえお"]}}
[info]close this socket