[8] 事前条件の指定がある[[要求]]のことを、[DFN[[RUBYB[条件付き要求]@en[conditional request]]]]といいます。
[[鯖]]は事前条件が満たされる場合には[[要求メソッド]]で指定された処理を[[対象資源]]に対して実行しますが、
事前条件が満たされなければ処理は実行せずにエラーを返します。

[9] [[条件付き要求]]は、[[キャッシュ]]より新しい場合のみ[[資源]]を取得したい時や、
他者の編集との衝突に注意しつつ[[鯖]]にある[[資源]]を更新したい時などに使われます。

* 仕様書

[REFS[
- [3] '''[CITE@en[RFC 7232 - Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests]] ([TIME[2014-09-11 10:02:44 +09:00]] 版) <https://tools.ietf.org/html/rfc7232>'''
-- [10] [CITE@en[RFC 7232 - Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests]] ([TIME[2014-09-11 10:02:44 +09:00]] 版) <https://tools.ietf.org/html/rfc7232#section-3>
-- [12] [CITE@en[RFC 7232 - Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests]] ([TIME[2014-09-11 10:02:44 +09:00]] 版) <https://tools.ietf.org/html/rfc7232#section-3.1>
-- [21] [CITE@en[RFC 7232 - Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests]] ([TIME[2014-09-11 10:02:44 +09:00]] 版) <https://tools.ietf.org/html/rfc7232#section-3.2>
-- [14] [CITE@en[RFC 7232 - Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests]] ([TIME[2014-09-11 10:02:44 +09:00]] 版) <https://tools.ietf.org/html/rfc7232#section-3.3>
-- [25] [CITE@en[RFC 7232 - Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests]] ([TIME[2014-09-11 10:02:44 +09:00]] 版) <https://tools.ietf.org/html/rfc7232#section-3.4>
-- [79] [CITE@en[RFC 7232 - Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests]] ([TIME[2014-09-11 10:02:44 +09:00]] 版) <https://tools.ietf.org/html/rfc7232#section-4.1>
-- [18] [CITE@en[RFC 7232 - Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests]] ([TIME[2014-09-11 10:02:44 +09:00]] 版) <https://tools.ietf.org/html/rfc7232#section-5>
-- [29] [CITE@en[RFC 7232 - Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests]] ([TIME[2014-09-11 10:02:44 +09:00]] 版) <https://tools.ietf.org/html/rfc7232#section-6>
- [1] [CITE@en[RFC 7231 - Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content]] ([TIME[2014-06-07 01:55:45 +09:00]] 版) <https://tools.ietf.org/html/rfc7231#section-5.2>
- [28] [CITE@en[RFC 7233 - Hypertext Transfer Protocol (HTTP/1.1): Range Requests]] ([TIME[2014-09-11 09:57:55 +09:00]] 版) <https://tools.ietf.org/html/rfc7233#section-3.2>
- [35] [CITE@en[RFC 7234 - Hypertext Transfer Protocol (HTTP/1.1): Caching]] ([TIME[2014-09-11 10:19:59 +09:00]] 版) <https://tools.ietf.org/html/rfc7234#section-4.3.2>
- [73] [CITE@en[RFC 7234 - Hypertext Transfer Protocol (HTTP/1.1): Caching]] ([TIME[2014-09-11 10:19:59 +09:00]] 版) <https://tools.ietf.org/html/rfc7234#section-4.3.3>
- [83] [CITE@en[RFC 7234 - Hypertext Transfer Protocol (HTTP/1.1): Caching]] ([TIME[2014-09-11 10:19:59 +09:00]] 版) <https://tools.ietf.org/html/rfc7234#section-4.3.4>
- [100] [CITE@en[RFC 4918 - HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)]] ([TIME[2014-09-21 17:04:59 +09:00]] 版) <http://tools.ietf.org/html/rfc4918#section-12.1>
]REFS]

* 意味

[4] [DFN[[RUBYB[[[条件付き要求]]]@en[conditional request]]]]は、
[[対象資源]]に[[要求メソッド]]の[[意味]]を適用する前に確認されるべき事前条件を示す[[ヘッダー]]をいくつか含む[[要求]]です
[SRC[>>3 1.]]。

[FIG(list)[
- [5] 条件付き [CODE(HTTP)@en[[[GET]]]] [[要求]]は、
[[HTTPキャッシュ]]の更新のための最も効率的な仕組みです [SRC[>>3 1.]]。
- [6] [CODE(HTTP)@en[[[PUT]]]] や [CODE(HTTP)@en[[[DELETE]]]]
のような状態を変更する[[要求メソッド]]では、並列に動作する別の[[クライアント]]の更新を誤って上書きしてしまう
「[[lost update]]」問題を防ぐために[[条件付き要求]]を使えます [SRC[>>3 1.]]。
- [47] [[キャッシュ検証]]の手段としても使われます。
]FIG]

[7] 事前条件として記述される資源のメタデータのことを、[[検証子]]といいます [SRC[>>3 2.]]。

* 条件付きヘッダー

[2] 次の[[要求ヘッダー]]は[DFN[[RUBYB[[[条件付き]]]@en[conditional]]]]に分類されています [SRC[>>1]]。
[FIG(list short)[
- [CODE(HTTP)@en[[[If-Match:]]]]
- [CODE(HTTP)@en[[[If-None-Match:]]]]
- [CODE(HTTP)@en[[[If-Modified-Since:]]]]
- [CODE(HTTP)@en[[[If-Unmodified-Since:]]]]
- [CODE(HTTP)@en[[[If-Range:]]]]
]FIG]

;; [11] これらは [[RFC 7232]] では[DFN[[RUBYB[事前条件]@en[precondition]]]]
[SRC[>>10]] ヘッダーと呼ばれています。

;; [109] [CODE[If-Range:]] は [CODE[Range:]] とは別物で、
[CODE[Range:]] は[[範囲要求]]用なので[[条件付き]]ではありません。

[99] [[RFC 4918]] は >>2 に加えて次の[[ヘッダー]]も[RUBYB[条件付き]@en[conditional]]に分類しています [SRC[>>100]]。
[[RFC 723x]] はなぜかこれらに言及していませんが、分類名や[[ヘッダー]]の性格から、
[[RFC 723x]] における「条件付き」と [[RFC 4918]] の「条件付き」
は同じ分類と考えても良さそうです。

[FIG(short list)[
- [CODE(HTTP)@en[[[If:]]]]
- [CODE(HTTP)@en[[[Overwrite:]]]]
]FIG]

[NOTE[
[105] 一覧の [[JSON]] データがあります。

[REFS[
- [106] [CITE@en[data-web-defs/headers.json at master · manakai/data-web-defs]] ([TIME[2017-08-29 17:45:16 +09:00]]) <https://github.com/manakai/data-web-defs/blob/master/data/headers.json>
-- [107] [CITE@en[data-web-defs/headers.txt at master · manakai/data-web-defs]] ([TIME[2014-09-03 10:02:34 +09:00]] 版) <https://github.com/manakai/data-web-defs/blob/master/doc/headers.txt>
]REFS]

[108] [[ヘッダー名]]の一覧を取得するには:

[PRE(code)[
$ curl -fL https://raw.githubusercontent.com/manakai/data-web-defs/master/data/headers.json | ./jq '.headers | map(select(.http.conditional)) | map (.name)'
]PRE]

]NOTE]


* 文脈

[67] [[条件付き要求]]は、次の場面で使われます。

[FIG(list)[
- [95] [[キャッシュ]]よりも新しい[[資源]]の[[表現]]が存在する場合のみ結果を返させるために[[条件付き要求]]を使うことができます。
- [68] [[Webページ]]の[[更新チェック]]を行う[[利用者エージェント]]の中には、
[[条件付き要求]]を使うものもあります。
- [93] [[範囲要求]]において、[[要求]]の対象となる[[資源]]の[[表現]]が同一のものであることを確かめるために[[条件付き要求]]を使うことができます。
- [96] [[資源]]を編集する[[要求]]において、対象となる[[資源]]の[[表現]]が同一のものであることを確かめるために[[条件付き要求]]を使うことができます。
- [97] [CODE(HTTP)@en[[[Meter]]]] の利用回数計測結果の報告において報告対象を明確にするため[[条件付き要求]]を使います。
]FIG]

* クライアントの処理モデル

[37] [[クライアント]]が[[生成]]する各[[ヘッダー]]の構文的要件については、
各[[ヘッダー]]の項を参照。

[38] 鯖の処理モデルの項の通り、[[鯖]]は[[条件付き要求]]に対応する必要はないので、
[[クライアント]]は条件が無視されて通常の[[応答]]が返された場合にも対処できる必要があります。

[69] [[検証]]のために[[キャッシュ]]が[[条件付き要求]]を行う場合は、
次のように処理します。

[FIG(steps)[
= [70] [[要求]]を[[転送]]します。
=- [49] [[転送]]する[[要求]]においては元の [CODE(HTTP)@en[[[If-None-Match:]]]]
の値が[[実体値]]の場合、それ(ら)に加えて、[[蓄積]]されている[[応答]]の[[実体タグ]]を
([[新鮮]]なものも[[腐敗]]しているものも) 加えて構いません。
ただし[[蓄積]]されている[[応答]]が一部分しか含んでいない場合にあっては、
[[要求]]がその一部分に含まれないならそうしては[['''なりません''']]。 [SRC[>>35]]
= [71] [[応答]]が [CODE(HTTP)[[[304]]]] だった場合、
== [72] [[蓄積]]された[[応答]]を更新します [SRC[>>73]]。
== [54] 更新した後の[[蓄積]]された[[応答]]を再利用します [SRC[>>35, >>73]]。
= [74] [[応答]]が [CODE(HTTP)[[[5xx]]]] だった場合、
以前[[蓄積]]された[[応答]]を再利用して構いません [SRC[>>73]]。
= [75] それ以外の場合 [SRC[>>73]]、
== [76] [[蓄積]]された[[応答]]を新しいもので置換して構いません。
== [77] 新しい[[応答]]を再利用します。
]FIG]

;; [[キャッシュ再利用]]も参照。

[78] [[キャッシュ]]が [CODE(HTTP)[[[304]]]] [[応答]]を受信した場合
([[検証]]以外も含みます。) には、次のように処理します。

[FIG(steps)[
= [84] 新しい[[応答]]が[[強い検証子]]を含む場合は、
== [85] 同じ[[強い検証子]]を持つ[[蓄積された応答]]すべてを (あれば) 選択します [SRC[>>83]]。
= [86] そうでなく新しい[[応答]]が[[弱い検証子]]を含む場合で、
同じ[[弱い検証子]]を持つ[[蓄積された応答]]がある場合は、
== [87] そのうち最新のもののみを選択します [SRC[>>83]]。
= [88] そうでない場合で、[[蓄積された応答]]が1つだけあり、
[[検証子]]が含まれない場合には、
== [89] その[[蓄積された応答]]を選択します [SRC[>>83]]。
= [90] 選択した[[蓄積された応答]]があれば、それぞれについて、
== [91] [[警告符号]] [CODE(HTTP)[[[1xx]]]] の [CODE(HTTP)@en[[[Warning:]]]]
[[ヘッダー]]を[[蓄積された応答]]から削除します [SRC[>>83]]。
== [92] [CODE(HTTP)[[[304]]]] [[応答]]に含まれる[CODE(HTTP)@en[[[Warning:]]]]
以外の[[ヘッダー]]について、
[[蓄積された応答]]の対応する[[ヘッダー]]を置き換えます [SRC[>>83]]。
= [80] [[外向き]]の[[クライアント]]が[[条件付き要求]]を作成した場合、
例えば[[キャッシュ]]を持つ[[利用者エージェント]]が[[共有串]]に[[条件付きGET]]
を送信した場合には、[[串]]は [CODE(HTTP)[[[304]]]] 
[[応答]]をその[[クライアント]]に[[転送]]する[['''べきです''']]
[SRC[>>79]]。
= [82] そうでない場合は、更新した[[蓄積された応答]]を[[転送]]します。
]FIG]

;; [81] なぜ >>80 が [['''SHOULD''']] でしかないのかは不明です。

;; [94] 元の[[蓄積された応答]]に含まれない[[ヘッダー]]が [CODE(HTTP)[[[304]]]]
[[応答]]に含まれる場合の処理は明記されていませんが、
[[蓄積された応答]]に追加するべきと思われます。

;; [98] [[範囲要求]]の場合の処理モデル ([[応答]]の結合方法) と似ています。

* 鯖の処理モデル

[45] [[受信者]]である[[キャッシュ]]や[[起源鯖]]は、
[[要求]]を次のように処理しなければ[['''なりません''']] [SRC[>>18, >>29]]。

[FIG(steps)[
= [22] [[要求]]をチェックし、必要があればエラーの[[応答]]を返したり、
[[リダイレクト]]の[[応答]]を返したりします。その場合ここで終わります。 [SRC[>>18]]
= [24] [[要求メソッド]]が[[選択された表現]]の選択や編集に関わるものである場合 (>>27) 以外は、
その[[メソッド]]の規定に従い処理し、ここで終わります [SRC[>>18]]。
= [39] [[選択された表現]]を決定します。存在しないこともあります。
== [62] [[起源鯖]]においては、実装・[[対象資源]]依存の方法で決定します。
== [65] [[キャッシュ]]においては、[[キャッシュ項目]]として[[蓄積された応答]]を使って決定します。詳細は[[キャッシュ項目]]を参照。
その過程で[[応答]]を返した場合は、ここで終わります。
= [46] [[受信者]]が[[対象資源]]の[[起源鯖]]である場合 [SRC[>>18, >>29, >>35]]、
== [52] [[要求]]に [CODE(HTTP)@en[[[If-Match:]]]] [[ヘッダー]]がある場合 [SRC[>>12, >>29]]、
その評価結果が[[偽]]なら、
=== [50] >>19 の場合は [CODE(HTTP)[[[2xx]]]] を返して終わっても構いません。
=== [51] そうしないなら、 [CODE(HTTP)[[[412]]]] を返して終わります。
== [53] [[要求]]に [CODE(HTTP)@en[[[If-Match:]]]] [[ヘッダー]]がなく、
[CODE(HTTP)@en[[[If-Unmodified-Since:]]]] [[ヘッダー]]がある場合、 [SRC[>>29, >>25]]
その評価結果が[[偽]]なら、
=== [56] >>19 の場合は [CODE(HTTP)[[[2xx]]]] を返して終わっても構いません。
=== [57] そうしないなら、 [CODE(HTTP)[[[412]]]] を返して終わります。
= [26] [[受信者]]が[[対象資源]]の[[起源鯖]]である場合や[[キャッシュ]]として動作する場合 [SRC[>>18, >>29, >>35]]、
== [48] [[要求]]に [CODE(HTTP)@en[[[If-None-Match:]]]] [[ヘッダー]]がある場合 [SRC[>>21, >>29]]、
評価結果が[[偽]]なら、
=== [59] [[要求メソッド]]が [CODE(HTTP)@en[[[GET]]]] か [CODE(HTTP)@en[[[HEAD]]]] なら、
[CODE(HTTP)[[[304]]]] を返して終わります。
=== [60] それ以外なら、 [CODE(HTTP)[[[412]]]] を返して終わります。
== [61] [[要求]]に [CODE(HTTP)@en[[[If-None-Match:]]]] [[ヘッダー]]がなく [SRC[>>29, >>14]]、
[CODE(HTTP)@en[[[If-Modified-Since:]]]] [[ヘッダー]]がある場合、
その評価結果が[[偽]]なら、
=== [64] [[要求メソッド]]が [CODE(HTTP)@en[[[GET]]]] か [CODE(HTTP)@en[[[HEAD]]]] なら、
[CODE(HTTP)[[[304]]]] を返して終わります [SRC[>>29, >>14]]。
== [55] [[要求]]に [CODE(HTTP)@en[[[If-Range:]]]] [[ヘッダー]]があり、
その評価結果が[[真]]なら、 [SRC[>>28, >>29]]
=== [66] [[要求メソッド]]が [CODE(HTTP)@en[[[GET]]]] なら、
[CODE(HTTP)@en[[[Range:]]]] [[ヘッダー]]があればそれを処理して 
[CODE(HTTP)[[[206]]]] を返して終わって構いません。 ([[範囲要求]]を参照。)
= [63] 事前条件は一致したので、[[要求]]の他の指定に従って処理し、結果を返します。
== [31] [[要求メソッド]]が [CODE(HTTP)@en[[[GET]]]] なら、
[CODE(HTTP)@en[[[If-Range:]]]] [[ヘッダー]]がなく
[CODE(HTTP)@en[[[Range:]]]] [[ヘッダー]]があればそれを処理して 
[CODE(HTTP)[[[206]]]] を返して終わって構いません。 [SRC[>>28]] ([[範囲要求]]を参照。)
== [33] それ以外なら、 [CODE(HTTP)[[[2xx]]]] を返します。
]FIG]

;; [13] 条件を満たすかどうかの判定については、各[[ヘッダー]]の項を参照してください。

;; [36] >>26 は >>35 では[[キャッシュ]]は [['''SHOULD''']] としていますが、
これは一般に[[条件付き要求]]が [['''MAY''']] なのに対し[[キャッシュ]]では [['''SHOULD''']]
なのだと行間から推測されます。

;; [23] >>64 は >>14 ではなぜか [['''SHOULD''']] とされており、 >>29 の
[['''MUST''']] と矛盾します。両方の要件を満たすには、 [['''MUST''']]
と解釈するしかありません。

;; [34] >>66 [CODE(HTTP)@en[[[Range:]]]] の処理は、なぜか [['''MUST''']]
ではなく [['''SHOULD''']] となっています [SRC[>>28]]。 [CODE(HTTP)[[[206]]]]
を実装しないことが認められているので [['''MUST''']] なのかもしれませんが、
詳細不明です。

;; [58] >>54 は >>35 によると [CODE(HTTP)[[[200]]]] [[応答]]を返すことになっていますが、
[[範囲要求]]なら [CODE(HTTP)[[[[206]]]] を返しても良いのでしょうか?

[27] 事前条件は[[選択された表現]]の選択や編集に関わる[[要求メソッド]]以外では無視する 
[SRC[>>18]] とされています。無視するべきメソッドとして [CODE(HTTP)@en[[[CONNECT]]]]、
[CODE(HTTP)@en[[[OPTIONS]]]]、[CODE(HTTP)@en[[[TRACE]]]] が挙げられています [SRC[>>18]] が、
完全なリストはなぜか提供されていません。

[40] [[ヘッダー]]の優先順位の根拠は次のように説明されています [SRC[>>29]]。
[FIG(list)[
- [42] 「[[lost update]]」の条件は[[キャッシュ]]の[[検証]]よりも厳密な要件である
- [43] [[検証]]された[[キャッシュ]]は[[部分応答]]よりも効率的である
- [44] [[実体タグ]]は日時の[[検証子]]よりも正確である
]FIG]

[FIG(corollary)[
[20] [CODE(HTTP)@en[[[If-Match:]]]] や [CODE(HTTP)@en[[[If-Unmodified-Since:]]]]
は[[起源鯖]]のみが処理し、[[キャッシュ]]は無視することになっています [SRC[>>12, >>25]] が、
[CODE(HTTP)@en[[[If-None-Match:]]]] や [CODE(HTTP)@en[[[If-Modified-Since:]]]]
は[[起源鯖]]だけでなく[[キャッシュ]]も処理することになっています。
]FIG]

[41] [CODE(HTTP)@en[[[If-Match:]]]] や [CODE(HTTP)@en[[[If-None-Match:]]]]
や [CODE(HTTP)@en[[[If-Modified-Since:]]]] や [CODE(HTTP)@en[[[If-Unmodified-Since:]]]]
を実装していない場合どう動作するべきか仕様上明確ではありませんが、
実際の[[鯖]]はこれらを無視するようです。 ([CODE(HTTP)[[[412]]]]
や [CODE(HTTP)[[[304]]]] ではありません。)

[16] [[受信者]]は、 [CODE(HTTP)@en[[[If-Modified-Since:]]]] や
[CODE(HTTP)@en[[[If-Unmodified-Since:]]]] が妥当な
[CODE(ABNF)@en[[[HTTP-date]]]] でないとき、これを無視しなければ[['''なりません''']] 
[SRC[>>14, >>25]]。

;; [32] [CODE(HTTP)@en[[[If-Match:]]]] や [CODE(HTTP)@en[[[If-None-Match:]]]]
や [CODE(HTTP)@en[[[If-Range:]]]] が構文的に正しくない時にどう処理するべきかは、
なぜか規定がありません。

;; [15] 無視ということは、条件の真偽を判断する以前に存在しないものと扱うのだと思われます。

[30] [CODE(HTTP)@en[[[If-Range:]]]] [[ヘッダー]]は、
[CODE(HTTP)@en[[[Range:]]]] [[ヘッダー]]が含まれていなければ無視しなければ[['''なりません''']]
[SRC[>>29]]。

;; [17] 同じ[[ヘッダー]]が複数ある時にどう処理するべきかはなぜか規定がありません。

[19] >>50、>>56 で [CODE(HTTP)[[[2xx]]]] を返せるのは、状態の変更が[[要求]]されており、
最終的な状態が既に[[対象資源]]の現在の状態に反映されていると[[起源鯖]]が確認できる場合です。
すなわち、以前の[[応答]]が失われたか互換性のある変更が他の[[利用者エージェント]]により行われたかによって[[利用者エージェント]]の[[要求]]が既に成功していたことに気づいていないような場合です。
ただしそのような場合には、同じ[[利用者エージェント]]が直前の[[要求]]を繰り返していると確認できる場合を除き、
[[検証子ヘッダー]]を送っては[['''なりません''']] [SRC[>>12, >>25]]。

[103] [[サーバー]]は、[[条件付要求]]のみを受け付けることにできます。
[[条件付要求]]でない[[要求]]を受け取ったら、 [CODE[[[428]]]]
[[応答]]を返すことができます。

* メモ

[413] [CITE@en[Add cache mode concept and elaborate a bit on caching. · 99377cd · whatwg/fetch]]
( ([TIME[2014-09-23 05:09:46 +09:00]] 版))
<https://github.com/whatwg/fetch/commit/99377cd334e7e4e7737c2b2fd521c74a6cf7e01a>

[101] [CITE@en[Close #141: fix 304 handling · whatwg/fetch@1a2f6b4]]
([TIME[2015-11-01 19:39:15 +09:00]] 版)
<https://github.com/whatwg/fetch/commit/1a2f6b49d616cfd74536c1a8dbed8b5c1215d548>

[102] [CITE@en[Merge conditionals in 304 handling · whatwg/fetch@bb9313f]]
([TIME[2015-11-12 11:29:43 +09:00]] 版)
<https://github.com/whatwg/fetch/commit/bb9313fc04b91c10639a3d5f5454cb2a6f224b45>

[104] [CITE@en-US[Understanding Conditional Requests and Refresh | IEInternals]]
( ([TIME[2016-06-06 11:15:40 +09:00]]))
<https://blogs.msdn.microsoft.com/ieinternals/2010/07/08/understanding-conditional-requests-and-refresh/>