第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風の構文
次章では、ソケットプログラミングを学びます。