ホーム › ネットワーク › N-07: HTTP(TCP ソケット低レベル)
N-07: HTTP リクエスト(TCP ソケット低レベル実装)
TCP ソケットで HTTP リクエストを手作りし、HTTP プロトコルの仕組みを理解するサンプルです。 リクエスト行・ヘッダー・空行・ボディという構造、レスポンスのステータス行解析など HTTP/1.1 の基礎を学べます。
⚠️ このページは「仕組みを理解する」ための低レベル実装です
実務で HTTP 通信するときは N-01(HttpClient、Java 11〜) または N-02(HttpURLConnection、Java 8〜) を使ってください。 ソケットで手作りすると HTTPS(TLS)対応・リダイレクト追跡・チャンク転送エンコーディングなど 多くの考慮事項があり、実用的な実装には膨大なコードが必要です。
HTTP/1.1 の構造
HTTP はテキストベースのプロトコルです。リクエストとレスポンスはどちらも 「開始行」「ヘッダー」「空行」「ボディ」という共通の構造を持っています。
| 部分 | リクエスト | レスポンス |
|---|---|---|
| 開始行 | GET /path HTTP/1.1 | HTTP/1.1 200 OK |
| ヘッダー | Host: example.com User-Agent: ... | Content-Type: text/html Content-Length: 1234 |
| 空行 | (CRLF のみの行) | (CRLF のみの行) |
| ボディ | POST の場合に送信データ(GET は省略) | <html>...</html> |
代表的なステータスコード
| コード | 意味 |
|---|---|
| 200 OK | リクエスト成功 |
| 301 Moved Permanently | 恒久的なリダイレクト |
| 400 Bad Request | リクエストの形式が不正 |
| 401 Unauthorized | 認証が必要 |
| 403 Forbidden | アクセス拒否 |
| 404 Not Found | リソースが存在しない |
| 500 Internal Server Error | サーバー内部エラー |
サンプルコード
TCP ソケットで HTTP GET リクエストを手作りし、レスポンスを解析するサンプルです。 Java 8 版ではクラスを使ったレスポンス表現、Java 17 版では record による不変なレスポンスクラスと ヘッダーの Key-Value 解析、Java 21 版では sealed interface と pattern matching switch を使った ステータスコードの型安全な分類処理を実装しています。example.com への実際のリクエストを試みますが、 ネットワーク環境によっては接続できない場合があります。
よくあるミス・注意点
⚠️ HTTP/1.1 の Host ヘッダーは必須
HTTP/1.1 では Host ヘッダーが必須です。 1 台のサーバーで複数のドメインをホストする「バーチャルホスト」に対応するために必要です。 Host ヘッダーを省略すると、サーバーによっては 400 Bad Request が返ります。
// NG: Host ヘッダーなし(HTTP/1.1 ではエラーになる) "GET / HTTP/1.1\r\n\r\n" // OK: Host ヘッダーを必ず含める "GET / HTTP/1.1\r\n" + "Host: example.com\r\n" + "\r\n"
📌 HTTPS(443 ポート)はソケット直結では通信できない
HTTPS は TLS(Transport Layer Security)で暗号化された HTTP です。 通常の Socket では平文通信しかできないため、 HTTPS に接続するには SSLSocketFactory を使ったSSLSocket が必要です。 実務では HttpClient や HttpURLConnection が TLS を自動処理してくれます。
// NG: 通常の Socket で HTTPS に接続しようとするとエラー
Socket socket = new Socket("example.com", 443); // TLS ハンドシェイクができない
// HTTPS の場合は SSLSocket を使う
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) factory.createSocket("example.com", 443);
socket.startHandshake();📌 チャンク転送エンコーディング(Transfer-Encoding: chunked)の扱い
HTTP/1.1 ではレスポンスボディが Transfer-Encoding: chunked で 送られてくる場合があります。この場合、ボディは「16 進数のサイズ + CRLF + データ + CRLF」が繰り返され、 サイズが 0 の行で終わります。単純に readLine() で読むと チャンクサイズの行が混入してしまいます。HttpClient はこれを自動処理します。
📌 Java バージョンごとの違い
Java 8 ではクラスを使ったレスポンス表現になります。 Java 17 では record で不変なレスポンスを簡潔に定義でき、 テキストブロックでリクエスト構造の説明も読みやすく書けます。 Java 21 では sealed interface と pattern matching switch でステータスコードの分類を 型安全かつ網羅的に処理できます。
テストする観点
- ステータス行の解析が正しいか("HTTP/1.1 200 OK" → statusCode=200, statusMessage="OK")
- ヘッダーの Key-Value 解析が正しいか(コロン区切り・前後のスペースのトリム)
- ヘッダーとボディの区切り(空行)を正しく検出できるか
- ステータスコードの分類(2xx/3xx/4xx/5xx)が正しいか(境界値: 200・299・300・399・400・499・500・599)
- 存在しないホストへの接続で
UnknownHostExceptionが発生するか - 接続タイムアウト(
socket.setSoTimeout())を設定した場合にSocketTimeoutExceptionが発生するか