お願いの中に、「残り1席」と書かれていた。
server側の記録には、「残り0席」とある。
上映室の扉が閉まるまで、あと二十秒。
第11話で最後の席が取られた直後、別の端末から新しい予約requestが届いた。
POST /reservations
{
"seatId": 12,
"displayedRemaining": 1
}
requestを送った端末では、古い青い1がまだ光っていた。
サーバー応答室のcurrent ledgerでは、12番席に一枚の予約札がついている。
イトの前には、成功responseを書くための青い札と、満席を伝える赤い札が一枚ずつ置かれた。
イト
お願いに1って書いてある。これを返せば、いちばん速いよ
マコト
その1は、どこで見えた数字だろう
ピコ
clientが送った値と、serverがいま持つ状態を、同じ札にしないで見よう
残り十八秒。
requestは、clientの意図を運んでくる
browserやappのように、requestを送る側をclientと呼ぶ。
requestには、何をしたいかを示すmethod、対象を示すrequest target、必要に応じてheaderやcontentが入る。
今回のrequestが伝えている中心は、短い。
12番席を予約したい
displayedRemaining: 1は、そのclientの画面に見えていた値だ。
席が現在も空いているという保証ではない。
RFC 9110のclient/serverとmessageでは、clientがrequestを送り、serverがrequestを解釈し、status codeなどを含むresponseを返すという役割が定められている。
ただし、HTTPは上映室の予約ruleを決めない。
「最後の一席を誰へ確定するか」「どの記録をcurrent stateとして扱うか」は、このフィクションの予約serviceが決めたapplicationの仕事だ。
この練習用clientがdisplayedRemainingを送る設計も、すべての予約APIに共通する形ではない。
ユイが、requestの二行へ別々の札を置いた。
seatId: 12予約したい対象
displayedRemaining: 1clientで見えていた値
ユイ
requestは、相手が望む成功結果そのものではないんですね
ピコ
うん。何を頼まれたかを読み、server側のruleで処理して、結果をresponseにする
残り十五秒。
serverは、箱の名前ではなく役割
サーバー応答室の中央で、予約を処理するprogramがrequestを受け取った。
このconnectionでは、そのprogramがserverだ。
serverは、requestをserviceするためにconnectionを受け、responseを送るprogramの役割を指す。
大きな黒い機械だけをserverと呼ぶわけではない。
一台のmachineで動くことも、いくつもの役割に分かれることもある。HTTPは、同じinterfaceの奥でserviceをどう実装したかまでは決めない。
さらに、clientとserverは永久に固定された名前ではない。
RFC 9110では、同じprogramが、あるconnectionではclient、別のconnectionではserverとして働く場合がある。
今回の予約programもそうだった。
- browserから予約requestを受けるときはserver
- current seatをledger serviceへ尋ねるときはclient
- ledgerの返事を使い、browserへfinal responseを返すときはserver
イトは、応答室の奥に一つの巨大な真実の箱があると思っていた。
実際には、役割から役割へrequestとresponseがつながっている。
「どこにある箱か」だけでは、いま何をしているかは分からない。
三つの返事札
残り十二秒。
ピコが、三枚の札を机へ並べた。
- 古い1を写す
- ledgerを全部返す
- 12番席だけを確かめる
一枚目なら、requestのdisplayedRemaining: 1をresponseへ写し、予約成功にできる。速い。けれど、server側のcurrent stateを一度も見ない。
二枚目なら、予約者名を含むledgerをすべてclientへ返し、client側で空席を考えてもらえる。しかし、12番席が空いているかという問いに、全員の記録は必要ない。
三枚目なら、serverが12番席とcurrent remainingだけをledger serviceへ尋ね、予約ruleを適用できる。結果を待つ時間がかかり、clientが望む「成功」にならない可能性もある。
待っているclientの輪が、黄色から橙へ変わった。
「早く返して」と光っている。
青い成功札を入れれば、一瞬で輪は消える。
でも、その先には一つしかない12番席がある。
ピコ
どの返事にする?
マコト
clientを喜ばせる答えと、処理した結果は、同じとは限らない
イトはrequestの古い1へ、小さな札を重ねた。
clientで見えた値
「これは、席の計算から外す」
青い成功札を棚へ戻し、12番席だけをledger serviceへ送った。
current stateからresponseを組み立てる
残り八秒。
ledger serviceから、短い返事が戻った。
{
"seatId": 12,
"reserved": true,
"remaining": 0
}
予約programは、browserから見るとserverだった。
ledgerへ尋ねる瞬間にはclientになり、返ってきたcurrent stateを読んだ。
イトは、予約ruleを確認する。
すでにreservedの席へ、二枚目の予約札をつけない
そしてfinal responseを組み立てた。
409 Conflict
{
"reserved": false,
"remaining": 0,
"reason": "already_taken"
}
responseにはstatus codeがあり、必要に応じてheaderやcontentが入る。status codeは、requestを処理した結果をclientが解釈する手がかりになる。
RFC 9110のstatus codeはresponse resultの意味を定める。今回の409とbodyは、この予約serviceが「current stateとの衝突」を伝える一つの設計例であり、すべてのserviceが同じ形を返すとは限らない。
残り四秒。
イトが、赤い0のresponseをclientへ返した。
望まれなかったnoが、席を一つに保った
待っていたclientの青い1が消えた。
残り0席
予約buttonは「満席」へ変わる。
clientが望んだ予約は、成立しなかった。
けれど、12番席にある予約札は一枚のままだ。
二人を同じ椅子へ案内する二枚目の札は作られなかった。

イトは、古い1の札を捨てなかった。
request封筒の内側へ戻し、「clientで見えていた値」として残した。
間違った数字ではない。
ただ、serverが予約結果を決めるための現在値ではなかった。
serverは、clientの言うことを何でも否定する係でも、いつも正しい答えが眠る箱でもない。
requestの意図とtargetを読み、applicationのruleに沿って必要な処理をし、その結果をstatusとcontentにして返す役割だ。
今回イトが信じたのは、「serverと呼ばれるものなら正しい」という名前ではなかった。
12番席というtarget、current ledger、二重予約を作らないruleの三つだった。
pageそのものを求めるrequest
上映室の扉が閉まった。
最後のresponseは、その前にclientへ届いた。
応答室が静かになった瞬間、別の入口で新しい封筒が光った。
GET /screenings/finale
新しく来た端末は、残席dataだけではなく、上映案内のpageそのものを求めている。
HTML。
CSS。
JavaScript。
画像。
browserが組み立てる材料を、どの受付が返すのか。
イトは、応答室の隣にある明るい窓口を見た。
そこには、Web serverの札がかかっている。
次回:Webサーバーはどんな係?
予約serverは、current seatを確かめて、残席0のresponseを返した。
けれど、serverという役割は広い。
pageを求めるrequestには、どの材料を、どんな順番で返すのか。
次回、ピコルート第13話。
Webページの材料を返す受付へ進む。
画面のむこう側をもっと知る
物語で見たclient、server、request、responseの役割を図で整理するなら、サーバーとはで確かめられます。

