参考URL
Working with HTTP cache
https://www.brianstorti.com/working-with-http-cache/
はじめに
httpのレスポンスのヘッダに last-modified
や Etag
等のキャッシュに関する情報が載っていることがあります.
Webサーバ側がせっかくキャッシュ情報を設定しているんだから,クライアント側も実装しよう.
基本
以降,Ruby 2.4.3(Windows10 64bit),2.5.1(Windows Subsystem for Linux)で説明します.
次のコードは,httpsでdocs.ruby-lang.org
から /
をダウンロードします.
httpのヘッダやステータスコードも出力させています.
require 'net/https'
https = Net::HTTP.new("docs.ruby-lang.org", 443)
https.use_ssl = true
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
https.start do |http|
response = http.get("/")
p response.code
puts response.each.map{|k,v| k+"\t => "+v }.to_a
puts response.body[0..200] if response.body
end
結果はこんな感じでした.
"200"
server => nginx/1.10.3
content-type => text/html
last-modified => Thu, 27 Dec 2018 00:19:32 GMT
etag => W/"5c241a94-394"
cache-control => public, max-age=43200, s-maxage=172800, stale-while-revalidate=86400, stale-if-error=604800
via => 1.1 varnish, 1.1 varnish
fastly-debug-digest => 33272ba139e924bc977ca4803fe9bfe699d76ef3818ac8b578bbfe42b3afa6cc
content-length => 579
accept-ranges => bytes
date => Sat, 02 Mar 2019 01:49:34 GMT
age => 102622
connection => keep-alive
x-served-by => cache-nrt6147-NRT, cache-itm18822-ITM
x-cache => HIT, HIT
x-cache-hits => 1, 3
x-timer => S1551491375.978827,VS0,VE5
vary => Accept-Encoding
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<!-- Global Site Tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-620926-3"></script
キャッシュ
last-modified を使う
ヘッダに含まれているlast-modified
は,「最後にそのページが更新された時間」です.
last-modified
の値が前回アクセスした時と同じならば,
更新が無かったとみなすことができるので,bodyを取得する必要はありません.
require 'net/https'
require 'time'
@cache = {}
def get(path)
https = Net::HTTP.new("docs.ruby-lang.org", 443)
https.use_ssl = true
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
https.start do |http|
# ヘッダのみを取得
response = http.head(path)
# そのパスのキャッシュが無い OR キャッシュが古いならば
if @cache[path].nil? || Time.parse(@cache[path][:last_modified]) < Time.parse(response["last-modified"])
STDERR.puts "get"
# bodyも含めて取得
response = http.get(path)
# 取得結果を記憶
@cache[path] = {
last_modified: response["last-modified"],
body: response.body
}
@cache[path][:body]
else
STDERR.puts "cached"
# 記憶済みの取得結果を返す
@cache[path][:body]
end
end
end
puts get("/")[0..50]
sleep 1
puts get("/")[0..50]
sleep 1
puts get("/")[0..50]
If-Modified-Sinceを使う
クライアント側で更新時刻を比較するのではなく,サーバ側で比較してもらう方法です.
If-Modified-Since
ヘッダを付けて,クエリを投げます.
更新がなかった場合,304が返ってきます.
require 'net/https'
require 'time'
@cache = {}
def get(path)
https = Net::HTTP.new("docs.ruby-lang.org", 443)
https.use_ssl = true
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
https.start do |http|
# 最後に取得した日付
since = @cache[path] ? @cache[path][:last_modified] : ""
# サーバから取得する
response = http.get(path, {"If-Modified-Since"=>since})
if (response.code == "200") # OK(変更がある)
STDERR.puts "get"
# 取得結果を記憶
@cache[path] = {
last_modified: response["last-modified"],
body: response.body
}
@cache[path][:body]
elsif (response.code == "304") # Not Modified
STDERR.puts "cached"
# 記憶済みの取得結果を返す
@cache[path][:body]
else
"🤔"
end
end
end
puts get("/")[0..50]
sleep 1
puts get("/")[0..50]
sleep 1
puts get("/")[0..50]
Etag
ヘッダにEtag
が記載されている場合があります.
ウェブページのハッシュ値のような識別子で,前回のクエリとEtag値が同じならば,
ページに変化がない,とみなすことが出来ます.
If-Modified-Since
同様に,If-None-Match
ヘッダを付けてクエリを投げることで,
サーバ側でEtag値の比較をしてくれるはずです.
require 'net/https'
require 'time'
@cache = {}
def get(path)
https = Net::HTTP.new("docs.ruby-lang.org", 443)
https.use_ssl = true
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
https.start do |http|
# 最後に取得した日付
etag = @cache[path] ? @cache[path][:etag] : ""
# サーバから取得する
response = http.get(path, {"If-None-Match"=>etag})
if (response.code == "200") # OK(変更がある)
STDERR.puts "get"
# 取得結果を記憶
@cache[path] = {
etag: response["Etag"],
body: response.body
}
@cache[path][:body]
elsif (response.code == "304") # Not Modified
STDERR.puts "cached"
# 記憶済みの取得結果を返す
@cache[path][:body]
else
"🍣"
end
end
end
puts get("/")[0..50]
sleep 1
puts get("/")[0..50]
sleep 1
puts get("/")[0..50]
ETagには「強いETag値」「弱いETag値」の2種類があるようです.
詳細は日本語版Wikipediaに記載されています.
https://ja.wikipedia.org/wiki/HTTP_ETag