本文へ移動
cccskills
無料GitHub で公開

shiguredo-http11

時雨堂の Sans I/O HTTP/1.1 ライブラリ shiguredo_http11 の機能・API リファレンス。リクエスト/レスポンスのエンコード・デコード、ストリーミング処理、ヘッダーパース、圧縮トレイト、RFC 準拠に関する質問時に使用。

インストール方法を見る

含まれるファイル(1)

  • SKILL.md30.4 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

shiguredo_http11

Sans I/O 設計に基づく HTTP/1.1 パーサー/シリアライザーライブラリ。

特徴

  • 依存なし: 外部依存ゼロ (core / alloc のみ)
  • no_std 完全対応: std 非依存 (alloc クレートは必要)
  • Sans I/O: I/O を完全に分離した設計 (Tokio, async-std, 同期 I/O など任意の環境で使用可能)
  • 柔軟性: HTTP/1.1 のほか RTSP/1.0, RTSP/2.0 等のテキストプロトコルにも応用可能
  • ストリーミング対応: 大容量ボディをメモリ効率的に処理
  • 圧縮トレイト: Compressor / Decompressor で任意の実装を組み込み可能 (本体は実装を含まない)
  • RFC 準拠: RFC 9110, 9111, 9112 等の最新仕様に対応
  • セキュリティ: Request Smuggling, Response Splitting 対策

バージョン情報

  • crate 名: shiguredo_http11
  • バージョン: 2026.6.0
  • Rust Edition: 2024
  • 最小 Rust バージョン: 1.93
  • ライセンス: Apache-2.0

公開 enum (全 Error 系・StatusClass / BodyKind / CompressionStatus / Authorization / AuthChallenge 等) には #[non_exhaustive] が付与されている。利用側の match には _ アームを置くこと。

コア API

構築型 (コンパイル時検査)

型説明主要 API
HeaderNameHTTP ヘッダー名 (RFC 9110 Section 5.1, field-name = token)。Eq/Hash は case-insensitivefrom_static(b"Name") (const, コンパイル時検査), new(impl AsRef<[u8]>) (Result, ランタイム検査), as_str(), as_bytes(), TryFrom<&'static str> / TryFrom<&'static [u8]> (builder 用)
HeaderNameErrorHeaderName の構築エラーEmpty { input }, InvalidByte { byte, position, input }, input(), into_input()
MethodHTTP メソッド (RFC 9110 Section 9.1, method = token)。Eq/Hash は case-sensitivefrom_static(b"METHOD") (const, コンパイル時検査), new(impl AsRef<[u8]>) (Result, ランタイム検査), as_str(), as_bytes(), TryFrom<&'static str> / TryFrom<&'static [u8]> (builder 用)
MethodErrorMethod の構築エラーEmpty { input }, InvalidByte { byte, position, input }, input(), into_input()
SchemeURI スキーム (RFC 3986 Section 3.1)。Eq/Hash は case-insensitivefrom_static(b"scheme") (const, コンパイル時検査), new(impl AsRef<[u8]>) (Result, ランタイム検査), as_str(), as_bytes()
SchemeErrorScheme の構築エラーEmpty, InvalidByte { byte, position }

Method の標準定数: Method::GET, Method::POST, Method::PUT, Method::DELETE, Method::HEAD, Method::OPTIONS, Method::CONNECT, Method::TRACE, Method::PATCH, Method::QUERY

Scheme の標準定数: Scheme::HTTP, Scheme::HTTPS, Scheme::WS, Scheme::WSS, Scheme::RTSP

from_static はバイトリテラル (b"...") を受け取る const fn であり、不正な入力はコンパイル時に panic する。ランタイムで動的に構築する場合は new() を使い Result をハンドリングする。

builder 用途では "GET" / "Host" 等の 'static str リテラルを直接渡せる (TryFrom<&'static str> による)。非 'static な &str はコンパイルエラーになるため、動的入力は new() で構築した値を渡す。

エンコード用 (送信側)

型説明主要メソッド
RequestHTTP リクエストnew(Method, uri) (Result), with_version(Method, uri, version) (Result), header(HeaderName, value) (Result, builder), add_header(HeaderName, value) (Result, mutator, チェイン可), set_header(HeaderName, value) (Result, mutator, チェイン可), body() (builder), set_body() (mutator), clear_body() (mutator), without_body() (builder), body_bytes() (getter), method(), uri(), version(), encode() (Result), encode_headers() (Result), is_keep_alive(), is_chunked()
ResponseHTTP レスポンスnew(status_code, reason_phrase) (Result), with_version(version, status_code, reason_phrase) (Result), with_status(StatusCode) (infallible), header(HeaderName, value) (Result, builder), add_header(HeaderName, value) (Result, mutator, チェイン可), set_header(HeaderName, value) (Result, mutator, チェイン可), body() (builder), set_body() (mutator), clear_body() (mutator), without_body() (builder), body_bytes() (getter), omit_body() (builder), set_omit_body() (mutator), is_body_omitted(), status_code(), reason_phrase(), encode() (Result), encode_headers() (Result), status_class(), is_keep_alive()
StatusCodeIANA 登録済み HTTP ステータスコード (const 値)OK, CREATED, NO_CONTENT, NOT_MODIFIED, BAD_REQUEST, NOT_FOUND, INTERNAL_SERVER_ERROR 等の const 定数, code(), canonical_reason(), class(), from_code(u16) (未登録コードは None)
StatusClassRFC 9110 Section 15 のクラス分類 enumInformational, Successful, Redirection, ClientError, ServerError, from_status_code(u16) (範囲外は None)
RequestEncoder<C>圧縮対応リクエストエンコーダーnew(), with_compressor(), compress_body(), finish(), reset()
ResponseEncoder<C>圧縮対応レスポンスエンコーダーnew(), with_compressor(), compress_body(), finish(), reset()

encode() / encode_headers() は意味論違反 (Host 欠落、Content-Length 不一致、Transfer-Encoding と Content-Length の競合、1xx/204/205 へのボディ等) を Result<Vec<u8>, EncodeError> で返す。構築時バリデーションを通過した値でも encode 時に検出されるため、呼び出し側は ? 等で伝播する。

Request / Response のヘッダー名引数は "Host" 等の 'static str リテラル、HeaderName 型、のいずれかを渡せる (impl TryInto<HeaderName>)。メソッド引数は "GET" 等のリテラル、Method 定数 (Method::GET) のいずれかを渡せる (impl TryInto<Method>)。値引数 (value / uri / version / reason_phrase / body) は impl Into<String> / impl Into<Vec<u8>> を受け、String や Vec<u8> をムーブで渡せる。&str / &[u8] も従来どおり利用可能。

デコード用 (受信側)

型説明主要メソッド
RequestDecoder<D>リクエストデコーダーnew(), with_limits(), with_decompressor(), with_decompressor_and_limits(), feed(), feed_unchecked(), mut_buf(), advance_buf(), available_buf(), decode(), decode_headers(), peek_body(), peek_body_decompressed(), consume_body(), progress(), remaining(), limits(), reset(), is_tunnel(), take_remaining() (CONNECT 用)
ResponseDecoder<D>レスポンスデコーダー同上 + mark_eof(), is_close_delimited(), set_request_method() (HEAD/CONNECT 判定用のリクエストメソッドを設定)
RequestHeadデコード済みリクエストヘッダーnew(Method, uri) (Result), with_version(Method, uri, version) (Result), header(HeaderName, value) (Result, builder), add_header(HeaderName, value) (Result, mutator), method() / uri() / version() / headers() (参照 getter), into_method() / into_uri() / into_version() / into_headers() / into_parts() (消費メソッド)
ResponseHeadデコード済みレスポンスヘッダーnew(status_code, reason_phrase) (Result), with_version(version, status_code, reason_phrase) (Result), header(HeaderName, value) (Result, builder), add_header(HeaderName, value) (Result, mutator), status_code() / reason_phrase() / version() / headers() / status_class() (参照 getter), into_version() / into_reason_phrase() / into_headers() / into_parts() (消費メソッド)
HttpHeadヘッダー操作トレイト (Request / Response / RequestHead / ResponseHead が実装)version(), headers(), get_header(), is_keep_alive(), is_chunked()
request_target::RequestTargetFormrequest-target 形式 (encoder/decoder で共通)Origin, Absolute, Authority, Asterisk

HttpHead トレイト

Request / Response / RequestHead / ResponseHead が実装する共通トレイト。エンコーダー側とデコーダー側でヘッダー操作の一貫性を保証する。

メソッド説明
version()HTTP バージョンを取得
headers()ヘッダーリストを取得 (&[(HeaderName, String)])
get_header(name)ヘッダーを取得 (大文字小文字を区別しない)
get_headers(name)同名ヘッダーをすべて取得
has_header(name)ヘッダーの存在確認
connection()Connection ヘッダーを取得
is_keep_alive()キープアライブ接続か判定
content_length()Content-Length を取得 (Result<Option<u64>, Error>、複数値・不正値を検出)
is_chunked()Transfer-Encoding の最後が chunked か判定

ボディ処理

型説明
BodyKind::ContentLength(u64)Content-Length による固定長
BodyKind::ChunkedTransfer-Encoding: chunked
BodyKind::CloseDelimited接続終了まで (レスポンスのみ)
BodyKind::TunnelCONNECT 2xx レスポンス後のトンネルモード (Transfer-Encoding/Content-Length は無視)
BodyKind::Noneボディなし
BodyProgress::Advanced状態機械が前進し、続けて呼び出すことで処理を進められる
BodyProgress::NeedData追加データが必要。呼び出し側はループを抜けて I/O に戻る
BodyProgress::Complete { trailers }完了 (トレーラーヘッダー含む)

エンコーダー関数

関数戻り値説明
encode_request(&Request)Result<Vec<u8>, EncodeError>リクエスト全体をエンコード
encode_response(&Response)Result<Vec<u8>, EncodeError>レスポンス全体をエンコード
encode_request_headers(&Request)Result<Vec<u8>, EncodeError>ヘッダーのみエンコード
encode_response_headers(&Response)Result<Vec<u8>, EncodeError>ヘッダーのみエンコード
encode_chunk(&[u8])Vec<u8>単一チャンクをエンコード (空入力は終端チャンク)
encode_chunks(&[&[u8]])Vec<u8>複数チャンクをエンコード (終端含む)

圧縮トレイト

型/トレイト説明
Compressor圧縮トレイト: compress(), finish(), reset()
Decompressor展開トレイト: decompress(), reset()
NoCompressionデフォルト実装 (圧縮なし)
CompressionStatusContinue { consumed, produced }, Complete { consumed, produced }, OutputFull { consumed, produced }
CompressionErrorBufferTooSmall { required, available }, InvalidData(String), Internal(String), UnexpectedEof, AlreadyFinished

CompressionStatus には consumed(), produced(), is_complete(), is_output_full() ヘルパーがある。

ライブラリ本体は圧縮実装を含まないため、利用者が noflate / flate2 (gzip), brotli, zstd などを使って実装する。サンプル (examples/) は noflate ベースで実装されている。

RequestDecoder / ResponseDecoder は Keep-Alive 接続におけるメッセージ境界 (decode() 完了時および decode_headers() Complete→StartLine 遷移時) で Decompressor::reset() を自動的に呼び出すため、前メッセージの内部状態が次メッセージに漏れない。

ヘッダーパースモジュール

モジュール主要型RFC
acceptAccept, AcceptCharset, AcceptEncoding, AcceptLanguage, QValueRFC 9110
accept_queryAcceptQuery, MediaRangeItem, AcceptQueryErrorRFC 10008, RFC 9651
authBasicAuth, DigestAuth, DigestChallenge, BearerToken, BearerChallenge, WwwAuthenticate, Authorization, AuthChallenge, ProxyAuthorization, ProxyAuthenticate, AuthErrorRFC 7617, 7616, 6750
cacheCacheControl, Age, ExpiresRFC 9111
conditionalIfMatch, IfNoneMatch, IfModifiedSince, IfUnmodifiedSince, IfRangeRFC 9110
content_dispositionContentDisposition, DispositionTypeRFC 6266
content_encodingContentEncodingRFC 9110
content_languageContentLanguageRFC 9110
content_locationContentLocationRFC 9110
content_typeContentTypeRFC 9110
cookieCookie, SetCookie, SameSiteRFC 6265
dateHttpDate (IMF-fixdate / asctime は parse、rfc850-date は parse_rfc850(input, reference_year)), DateErrorRFC 9110
digest_fieldsContentDigest, ReprDigest, WantContentDigest, WantReprDigestRFC 9530
etagEntityTag, ETagListRFC 9110
expectExpectRFC 9110
hostHost (IPv4, IPv6, IPv-future 対応)RFC 9110
multipartMultipartParser (new(boundary) -> Result, with_max_buffer_size, feed -> Result<(), MultipartError>), MultipartBuilder (with_boundary(boundary) -> Result), Part, MultipartErrorRFC 7578
rangeRange, RangeSpec, ContentRange, AcceptRangesRFC 9110
trailerTrailer (禁止フィールド検証)RFC 9110, 9112
upgradeUpgradeRFC 9110
uriUri, UriError, percent_encode(), percent_encode_path(), percent_encode_query(), percent_decode(), percent_decode_bytes(), resolve(), normalize()RFC 3986
varyVaryRFC 9110

コード例

クライアント実装

use shiguredo_http11::{Method, Request, ResponseDecoder};
use std::io::Read;

// リクエスト作成
// 構築時バリデーション (CRLF/NUL 拒否) を行い Result<_, EncodeError> を返す
let request = Request::new(Method::GET, "/")?
    .header("Host", "example.com")?
    .header("Connection", "close")?;
let bytes = request.encode()?;
// bytes をネットワークに送信...

// レスポンスデコード: 内部バッファに直接 read してコピーを排除
let mut decoder = ResponseDecoder::new();
const READ_CHUNK: usize = 8192;
loop {
    let want = decoder.available_buf().min(READ_CHUNK);
    if want == 0 {
        return Err("decoder buffer full".into());
    }
    let buf = decoder.mut_buf(want)?;
    let n = stream.read(buf)?;
    if n == 0 {
        decoder.advance_buf(0);
        decoder.mark_eof();
        break;
    }
    decoder.advance_buf(n);
    if let Some(response) = decoder.decode()? {
        println!("Status: {}", response.status_code());
        break;
    }
}

サーバー実装

use shiguredo_http11::{RequestDecoder, Response, StatusCode};
use std::io::Read;

// リクエストデコード: 内部バッファに直接 read してコピーを排除
let mut decoder = RequestDecoder::new();
const READ_CHUNK: usize = 8192;
let request = loop {
    let want = decoder.available_buf().min(READ_CHUNK);
    if want == 0 {
        return Err("decoder buffer full".into());
    }
    let buf = decoder.mut_buf(want)?;
    let n = stream.read(buf)?;
    if n == 0 {
        decoder.advance_buf(0);
        return Err("client disconnected".into());
    }
    decoder.advance_buf(n);
    if let Some(req) = decoder.decode()? {
        break req;
    }
};

// レスポンス作成
// IANA 登録済みステータスコードは `Response::with_status(StatusCode::OK)` で
// infallible に構築できる (canonical reason phrase が自動付与される)。
// カスタムバージョンや任意の reason phrase が必要な場合は
// `Response::new` / `with_version` を使う (Result<_, EncodeError> を返す)。
let response = Response::with_status(StatusCode::OK)
    .header("Content-Type", "text/plain")?
    .body(b"Hello, World!".to_vec());
let bytes = response.encode()?;
// bytes をネットワークに送信...

HEAD リクエストの処理

HEAD リクエストへのレスポンスは GET と同じヘッダーを返すがボディは送信しない (RFC 9110 Section 9.3.2)。

use shiguredo_http11::{Method, Request, Response, ResponseDecoder, StatusCode};

// サーバー側: Response::omit_body() でボディ送信を抑止
let is_head = request.method() == &Method::HEAD;
let body = b"Hello, World!";
let mut response = Response::with_status(StatusCode::OK)
    .header("Content-Type", "text/plain")?
    .header("Content-Length", &body.len().to_string())?
    .omit_body(is_head);
if !is_head {
    response = response.body(body.to_vec());
}
let bytes = response.encode()?;

// クライアント側: ResponseDecoder::set_request_method("HEAD") でボディなしを通知
let mut decoder = ResponseDecoder::new();
decoder.set_request_method("HEAD");

CONNECT トンネルの処理

CONNECT リクエストへの 2xx レスポンスはトンネルモードに切り替わり、Transfer-Encoding と Content-Length は無視される (BodyKind::Tunnel)。

use shiguredo_http11::ResponseDecoder;

let mut decoder = ResponseDecoder::new();
decoder.set_request_method("CONNECT");
// CONNECT 2xx 応答後のボディは Tunnel として扱われる。
// CONNECT 2xx を検出した時点で ResponseHead.headers からは
// Transfer-Encoding / Content-Length が除去される (RFC 9110 §9.3.6 / §7.8)。

サーバー側 (RequestDecoder) も CONNECT メソッド受信時に BodyKind::Tunnel を返し、is_tunnel() / take_remaining() でトンネル接続を引き継げる。

直接書き込み API (mut_buf / advance_buf / available_buf)

OS の read 等にデコーダーの内部バッファを直接渡せる API。

  • mut_buf(len) -> Result<&mut [u8], Error>: 内部バッファ末尾に len バイトの書き込み枠 (ゼロ初期化済み) を確保。max_buffer_size を超える場合は BufferOverflow を返し、バッファ状態は不変。
  • advance_buf(n): 直前の mut_buf で確保した枠のうち、実際に書き込まれた n バイトを確定する。n = 0 で枠全体を破棄 (EOF や read 失敗時のリセット用)。
  • available_buf() -> usize: 書き込み可能な残り容量 (max_buffer_size - 現在のバッファ長)。pending を含めて差し引いた値。

feed / feed_unchecked との使い分け

両者は入力経路の違う別の最適解として共存する。用途で使い分ける:

  • これから書き込む先のバッファが必要なケース (OS の read でソケットから受信する等) は mut_buf / advance_buf。OS が内部バッファに直接書き込めるので、スタックバッファ → 内部 Vec<u8> のコピーが発生しない。
  • 既にバイト列が &[u8] として手元にあるケース (io_uring 等の完了通知型 I/O、テスト用バイトリテラル、別経路から受け取ったバイト列の中継等) は feed / feed_unchecked。mut_buf 経由だと「ゼロ初期化 + memcpy」の二段になるが、feed は素直に 1 memcpy で済む。
use shiguredo_http11::ResponseDecoder;

let mut decoder = ResponseDecoder::new();
const READ_CHUNK: usize = 8192;

loop {
    let want = decoder.available_buf().min(READ_CHUNK);
    if want == 0 {
        return Err("decoder buffer full".into());
    }
    let buf = decoder.mut_buf(want)?;
    let n = stream.read(buf)?;
    if n == 0 {
        decoder.advance_buf(0);
        decoder.mark_eof();
        if let Some(response) = decoder.decode()? {
            return Ok(response);
        }
        return Err("Connection closed before response complete".into());
    }
    decoder.advance_buf(n);

    if let Some(response) = decoder.decode()? {
        return Ok(response);
    }
}

mut_buf 後 advance_buf を呼ばずに feed / decode_headers / peek_body / consume_body 等を呼ぶと debug ビルドで panic する (誤用検出)。

ストリーミングボディ処理

use shiguredo_http11::{RequestDecoder, BodyKind, BodyProgress};
use std::io::Read;

let mut decoder = RequestDecoder::new();
const READ_CHUNK: usize = 8192;

// ヘッダーがそろうまで mut_buf 経由で受信
let (head, body_kind) = loop {
    let want = decoder.available_buf().min(READ_CHUNK);
    let buf = decoder.mut_buf(want)?;
    let n = stream.read(buf)?;
    if n == 0 {
        decoder.advance_buf(0);
        return Err("client disconnected before headers".into());
    }
    decoder.advance_buf(n);
    if let Some(result) = decoder.decode_headers()? {
        break result;
    }
};

// ボディをストリーミングで読み取り
let mut body = Vec::new();
if let BodyKind::ContentLength(_) | BodyKind::Chunked = body_kind {
    'outer: loop {
        loop {
            if let Some(data) = decoder.peek_body() {
                body.extend_from_slice(data);
                let len = data.len();
                match decoder.consume_body(len)? {
                    BodyProgress::Complete { .. } => break 'outer,
                    // NeedData (chunked CRLF 不足) でも内側ループ継続。
                    // 直後の peek_body() は None を返すため progress 分岐に進む。
                    BodyProgress::Advanced | BodyProgress::NeedData => continue,
                }
            }
            // peek_body() が None → 状態機械を進める
            match decoder.progress()? {
                BodyProgress::Complete { .. } => break 'outer,
                BodyProgress::Advanced => continue,
                // バッファ不足: 内側ループを抜けて I/O 読み取りに戻る
                BodyProgress::NeedData => break,
            }
        }

        // 追加データを内部バッファに直接 read
        let want = decoder.available_buf().min(READ_CHUNK);
        let buf = decoder.mut_buf(want)?;
        let n = stream.read(buf)?;
        if n == 0 {
            decoder.advance_buf(0);
            return Err("client disconnected during body".into());
        }
        decoder.advance_buf(n);
    }
}

Chunked Transfer Encoding

use shiguredo_http11::{Response, StatusCode, encode_chunk};

let response = Response::with_status(StatusCode::OK)
    .header("Transfer-Encoding", "chunked")?;

// ヘッダーを送信
let headers = response.encode_headers()?;
send(&headers);

// チャンクを順次送信
send(&encode_chunk(b"Hello, "));
send(&encode_chunk(b"World!"));
send(&encode_chunk(b"")); // 終端チャンク

エンコード時のエラー処理

encode() / encode_headers() は構築時バリデーション後の意味論違反 (Host 欠落、Content-Length 不一致、Transfer-Encoding と Content-Length の競合等) を Result<Vec<u8>, EncodeError> で返す。

use shiguredo_http11::{EncodeError, Method, Request};

let request = Request::new(Method::GET, "/")?;  // Host ヘッダーなし
match request.encode() {
    Ok(bytes) => { /* 送信 */ }
    Err(EncodeError::MissingHostHeader) => {
        // Host ヘッダーがない (HTTP/1.1 必須)
    }
    Err(e) => eprintln!("encode error: {}", e),
}

rfc850-date のパース

HttpDate::parse は IMF-fixdate と asctime のみ受理し、rfc850-date を検出した場合は DateError::Rfc850Date を返す。rfc850-date の 2 桁年を解決するには parse_rfc850(input, reference_year) を使う (RFC 9110 §5.6.7 の 50 年ルール)。

use shiguredo_http11::date::{HttpDate, DateError};

let input = "Sunday, 06-Nov-94 08:49:37 GMT";
let date = match HttpDate::parse(input) {
    Ok(d) => d,
    Err(DateError::Rfc850Date) => HttpDate::parse_rfc850(input, 2026)?,
    Err(e) => return Err(e),
};

SetCookie::parse / Expires::parse / IfModifiedSince::parse / IfUnmodifiedSince::parse / IfRange::parse は内部でこのフォールバックを行うため、シグネチャに reference_year: u16 引数を取る。

圧縮トレイトの実装

use shiguredo_http11::compression::{Compressor, Decompressor, CompressionStatus, CompressionError};
use shiguredo_http11::{ResponseDecoder, ResponseEncoder};

// 展開器を組み込んだデコーダー
let decoder = ResponseDecoder::with_decompressor(MyGzipDecompressor::new());

// 圧縮器を組み込んだエンコーダー
let mut encoder = ResponseEncoder::with_compressor(MyGzipCompressor::new());

// 展開器と制限を同時に指定
let decoder = RequestDecoder::with_decompressor_and_limits(
    MyGzipDecompressor::new(),
    DecoderLimits::default(),
);

DecoderLimits

フィールドデフォルト値説明
max_buffer_size64KB最大バッファサイズ
max_headers_count100最大ヘッダー数
max_header_line_size8KB最大ヘッダー行長
max_body_size10MB最大ボディサイズ
max_chunk_line_size64B最大チャンクサイズ行長 (16 進数)
use shiguredo_http11::{RequestDecoder, DecoderLimits};

// カスタム制限
let limits = DecoderLimits {
    max_body_size: 100 * 1024 * 1024, // 100MB
    ..DecoderLimits::default()
};
let mut decoder = RequestDecoder::with_limits(limits);

// 制限なし (信頼済み入力やテスト用途のみ。OOM に注意)
let decoder = RequestDecoder::with_limits(DecoderLimits::unlimited());

エラー型

Error (デコード/ランタイムエラー)

バリアント説明
Error::InvalidData(String)不正なデータ
Error::BufferOverflow { size, limit }バッファサイズ超過
Error::TooManyHeaders { count, limit }ヘッダー数超過
Error::HeaderLineTooLong { size, limit }ヘッダー行長超過
Error::BodyTooLarge { size, limit }ボディサイズ超過
Error::ChunkLineTooLong { size, limit }チャンクサイズ行長超過
Error::Compression(CompressionError)圧縮/展開エラー

EncodeError (エンコード時のバリデーションエラー)

主なバリアント:

バリアント説明
MissingHostHeaderHTTP/1.1 リクエストに Host ヘッダーがない (RFC 9112 §3.2)
ConflictingTransferEncodingAndContentLengthTransfer-Encoding と Content-Length 同時指定 (RFC 9112 §6.2)
ForbiddenTransferEncoding { status_code }1xx/204/205 で Transfer-Encoding 指定 (RFC 9112 §6.1, RFC 9110 §15.3.6)
ForbiddenContentLength { status_code }1xx/204 で Content-Length 指定 (RFC 9110 §8.6)
ForbiddenBodyFor205205 Reset Content にボディあり (RFC 9110 §15.3.6)
ContentLengthMismatch { header_value, body_length }Content-Length とボディ長が不一致
DuplicateContentLengthContent-Length が複数で値が不一致 (RFC 9110 §8.6)
InvalidContentLengthValue { value }Content-Length が 1*DIGIT ABNF 違反
InvalidMethod { method }不正なメソッド名
InvalidRequestTarget { uri }不正な request-target
InvalidRequestTargetForm { method, uri }メソッドと request-target 形式の不整合 (RFC 9112 §3.2)
InvalidVersion { version }不正な HTTP バージョン
InvalidHeaderName { name }不正なヘッダー名
InvalidHeaderValue { name, value }不正なヘッダー値
InvalidStatusCode { code }不正なステータスコード
InvalidReasonPhrase { phrase }不正な reason-phrase
DuplicateHostHeaderHost ヘッダーが重複
InvalidHostHeader { value }不正な Host ヘッダー値
HostAuthorityMismatch { host, authority }Host と request-target authority が不一致
UserinfoInHttpUri { uri }http/https URI に userinfo (RFC 9110 §4.2.4)
NonEmptyHostWithoutAuthority { host, uri }authority のない URI で Host が非空 (RFC 9112 §3.2)
EmptyHostInHttpUri { uri }http/https URI の host が空 (RFC 9110 §4.2)

セキュリティ

  • Response Splitting 対策: ヘッダー行の CR/LF と obs-fold を拒否
  • Request Smuggling 対策: Transfer-Encoding と Content-Length の同時指定を拒否
  • Request Smuggling 対策: Content-Length の不一致検出
  • DoS 対策: DecoderLimits による各種制限
  • CONNECT の扱い: RFC 9110 §9.3.6 に準拠

RFC 準拠

RFC名称対応機能
RFC 3986URI: Generic SyntaxURI パース、パーセントエンコーディング、resolve, normalize
RFC 6265HTTP State ManagementCookie, Set-Cookie
RFC 6266Content-Dispositionファイル添付
RFC 6750Bearer TokenBearer 認証
RFC 7578multipart/form-dataフォームデータ
RFC 7616Digest AuthDigest 認証
RFC 7617Basic AuthBasic 認証
RFC 8187Encoded HTTP Header Parametersfilename* 等のエンコード
RFC 9110HTTP Semanticsメソッド、ステータス、ヘッダー、条件付きリクエスト、Range
RFC 9111HTTP CachingCache-Control, Age, Expires
RFC 9112HTTP/1.1Transfer-Encoding, Content-Length, 接続管理, request-target 形式
RFC 9530Digest FieldsContent-Digest, Repr-Digest, Want-Content-Digest, Want-Repr-Digest
RFC 9651Structured Field Values for HTTPAccept-Query の List パース
RFC 10008The HTTP QUERY MethodQUERY メソッド, Accept-Query

既知の制限事項

  • UTF-8 強制: RFC 9112 §2.2 ではメッセージをオクテット列として解析すべき (SHOULD) としているが、本実装はチャンクサイズ行・トレーラー・ヘッダー値などを Rust の String として扱うため、非 UTF-8 バイト列 (obs-text 0x80-0xFF を含む) を拒否する。
  • request-line 前の空行: RFC 9112 §2.2 では先頭の空行を少なくとも 1 行は無視すべき (SHOULD) としているが、本実装は厳格にパースする。アプリケーション層で除去すること。
  • rfc850-date の reference_year: グローバル可変状態を持たないため、rfc850-date を含む可能性があるヘッダーをパースする API (SetCookie::parse 等) は reference_year: u16 引数を要求する。アプリケーション側で現在年を渡す責務を負う。

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

このスキルの問題を報告する