第1章:Webサーバー入門

はじめに

webservは、HTTP/1.1に準拠したWebサーバーをC++で実装するプロジェクトです。本章では、Webの歴史とHTTPプロトコルの基礎を学びます。

---

1. Webの歴史

1.1 Tim Berners-Leeの発明

年表:
1989: Tim Berners-Lee、CERNでWorld Wide Webを提案
1990: 最初のWebサーバー(httpd)とブラウザ(WorldWideWeb)
1991: 外部からの初めてのアクセス
1993: NCSA Mosaic登場、Webの爆発的普及
1995: Apache HTTP Server 1.0リリース
1996: HTTP/1.0(RFC 1945)
1997: HTTP/1.1(RFC 2068)
1999: HTTP/1.1改訂(RFC 2616)
2015: HTTP/2(RFC 7540)
2022: HTTP/3(RFC 9114)

1.2 なぜWebサーバーを自作するのか

学習目標:
1. ネットワークプログラミングの基礎
   - ソケットAPI
   - TCP/IPの実践

2. プロトコルの理解
   - HTTPの内部構造
   - リクエスト/レスポンスモデル

3. 非同期プログラミング
   - I/O多重化
   - イベント駆動アーキテクチャ

4. システムプログラミング
   - プロセス管理(CGI)
   - ファイルI/O

---

2. HTTPの概要

2.1 クライアント-サーバーモデル

+--------+          リクエスト          +--------+
|        | =========================> |        |
| Client |                             | Server |
|        | <========================= |        |
+--------+         レスポンス           +--------+

1. クライアントがリクエストを送信
2. サーバーがリクエストを処理
3. サーバーがレスポンスを返す
4. (HTTP/1.1では接続を維持可能)

2.2 HTTPリクエストの構造

GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html
Accept-Language: ja
Connection: keep-alive

[リクエストボディ(POSTの場合)]

構造:
リクエストライン: メソッド SP URI SP HTTPバージョン CRLF
ヘッダー: フィールド名: フィールド値 CRLF
空行: CRLF
ボディ: (オプション)

SP = スペース
CRLF = \r\n

2.3 HTTPレスポンスの構造

HTTP/1.1 200 OK
Date: Mon, 01 Jan 2024 00:00:00 GMT
Server: webserv/1.0
Content-Type: text/html; charset=UTF-8
Content-Length: 1234
Connection: keep-alive

<!DOCTYPE html>
<html>
...
</html>

構造:
ステータスライン: HTTPバージョン SP ステータスコード SP 理由フレーズ CRLF
ヘッダー: フィールド名: フィールド値 CRLF
空行: CRLF
ボディ: コンテンツ

---

3. HTTPメソッド

3.1 必須メソッド

| メソッド | 説明 | 冪等性 | ボディ | |----------|------|--------|--------| | GET | リソースの取得 | Yes | なし | | POST | データの送信 | No | あり | | DELETE | リソースの削除 | Yes | なし |

3.2 GET

GET /users/123 HTTP/1.1
Host: api.example.com

→ リソースを取得
→ サーバーの状態を変更しない
→ キャッシュ可能

3.3 POST

POST /users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Content-Length: 45

{"name": "John", "email": "john@example.com"}

→ 新しいリソースを作成
→ サーバーの状態を変更
→ 冪等ではない(同じリクエストで複数作成される可能性)

3.4 DELETE

DELETE /users/123 HTTP/1.1
Host: api.example.com

→ リソースを削除
→ 冪等(削除済みなら何もしない)

---

4. ステータスコード

4.1 カテゴリ

1xx: 情報(Informational)
     100 Continue

2xx: 成功(Success)
     200 OK
     201 Created
     204 No Content

3xx: リダイレクト(Redirection)
     301 Moved Permanently
     302 Found
     304 Not Modified

4xx: クライアントエラー(Client Error)
     400 Bad Request
     403 Forbidden
     404 Not Found
     405 Method Not Allowed
     413 Request Entity Too Large

5xx: サーバーエラー(Server Error)
     500 Internal Server Error
     502 Bad Gateway
     503 Service Unavailable

4.2 実装で重要なステータス

enum StatusCode {
    OK = 200,
    CREATED = 201,
    NO_CONTENT = 204,
    MOVED_PERMANENTLY = 301,
    BAD_REQUEST = 400,
    FORBIDDEN = 403,
    NOT_FOUND = 404,
    METHOD_NOT_ALLOWED = 405,
    REQUEST_ENTITY_TOO_LARGE = 413,
    INTERNAL_SERVER_ERROR = 500
};

const char* getReasonPhrase(int code) {
    switch (code) {
        case 200: return "OK";
        case 201: return "Created";
        case 204: return "No Content";
        case 301: return "Moved Permanently";
        case 400: return "Bad Request";
        case 403: return "Forbidden";
        case 404: return "Not Found";
        case 405: return "Method Not Allowed";
        case 413: return "Request Entity Too Large";
        case 500: return "Internal Server Error";
        default: return "Unknown";
    }
}

---

5. プロジェクト構造

5.1 ディレクトリ構成

webserv/
├── Makefile
├── conf/
│   ├── default.conf
│   └── example.conf
├── includes/
│   ├── webserv.hpp
│   ├── Server.hpp
│   ├── Client.hpp
│   ├── Request.hpp
│   ├── Response.hpp
│   ├── Config.hpp
│   └── CGI.hpp
├── srcs/
│   ├── main.cpp
│   ├── Server.cpp
│   ├── Client.cpp
│   ├── Request.cpp
│   ├── Response.cpp
│   ├── Config.cpp
│   └── CGI.cpp
└── www/
    ├── index.html
    ├── error/
    │   ├── 404.html
    │   └── 500.html
    └── cgi-bin/
        └── hello.py

5.2 クラス設計

+----------+      +----------+      +----------+
|  Config  |----->|  Server  |----->|  Client  |
+----------+      +----------+      +----------+
                       |                  |
                       |                  v
                       |            +-----------+
                       |            |  Request  |
                       |            +-----------+
                       |                  |
                       v                  v
                 +-----------+     +-----------+
                 |    CGI    |     | Response  |
                 +-----------+     +-----------+

---

6. 設定ファイルの概要

6.1 Nginx風の設定

server {
    listen 8080;
    server_name localhost;
    root /var/www/html;
    index index.html;
    client_max_body_size 1m;

    error_page 404 /error/404.html;
    error_page 500 502 503 504 /error/50x.html;

    location / {
        autoindex on;
        allow_methods GET POST;
    }

    location /upload {
        allow_methods POST;
        upload_store /var/www/uploads;
    }

    location /cgi-bin {
        cgi_pass /usr/bin/python3;
        cgi_extension .py;
    }
}

6.2 必要な設定項目

server:
├── listen          # ポート番号
├── server_name     # サーバー名
├── root            # ドキュメントルート
├── index           # デフォルトファイル
├── client_max_body_size  # 最大ボディサイズ
├── error_page      # エラーページ
└── location        # ロケーション設定
    ├── root / alias
    ├── autoindex
    ├── allow_methods
    ├── return      # リダイレクト
    ├── cgi_pass
    └── upload_store

---

7. 実装の流れ

7.1 ステップバイステップ

Phase 1: 基盤
├── 設定ファイルのパース
├── ソケットの作成とバインド
└── 単一接続の処理

Phase 2: HTTPパース
├── リクエストラインの解析
├── ヘッダーの解析
└── ボディの解析

Phase 3: レスポンス生成
├── 静的ファイルの配信
├── エラーページ
└── ディレクトリリスト

Phase 4: I/O多重化
├── select/poll/kqueue
├── 複数クライアント対応
└── タイムアウト処理

Phase 5: 高度な機能
├── CGI
├── ファイルアップロード
└── チャンク転送

7.2 テスト方法

# curlでテスト
curl -v http://localhost:8080/
curl -X POST -d "data=test" http://localhost:8080/upload
curl -X DELETE http://localhost:8080/file.txt

# telnetで生リクエスト
telnet localhost 8080
GET / HTTP/1.1
Host: localhost
<Enter><Enter>

# siege(負荷テスト)
siege -c 100 -t 10s http://localhost:8080/

---

まとめ

本章で学んだこと:

  • Webの歴史: Tim Berners-Lee、HTTPの進化
  • HTTPの概要: リクエスト/レスポンス構造
  • HTTPメソッド: GET, POST, DELETE
  • ステータスコード: 2xx, 3xx, 4xx, 5xx
  • プロジェクト構造: クラス設計
  • 設定ファイル: Nginx風の構文

次章では、ソケットプログラミングを学びます。