Skip to content
cccskills
FreePublished on GitHub

shiguredo-http11

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

The English summary is being prepared. Showing the original description.

See how to install

Files included (1)

  • SKILL.md30.4 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

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 引数を要求する。アプリケーション側で現在年を渡す責務を負う。

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

Report a problem with this skill