blob: 5f2ef1a70b85ae462803dd24bf430253cc296919 [file]
'''
'''
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import time
import datetime
Test.Summary = '''
Slice selfhealing test
'''
def to_httpdate(dt):
# string representation of a date according to RFC 1123 (HTTP/1.1).
weekday = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"][dt.weekday()]
month = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][dt.month - 1]
return "%s, %02d %s %04d %02d:%02d:%02d GMT" % (weekday, dt.day, month, dt.year, dt.hour, dt.minute, dt.second)
# Test description:
# Preload the cache with the entire asset to be range requested.
# Reload remap rule with slice plugin
# Request content through the slice plugin
Test.SkipUnless(
Condition.PluginExists('slice.so'),
Condition.PluginExists('cache_range_requests.so'),
Condition.PluginExists('xdebug.so'),
)
Test.ContinueOnFail = False
# configure origin server
server = Test.MakeOriginServer("server", lookup_key="{%uuid}")
# Define ATS and configure
ts = Test.MakeATSProcess("ts")
# default root
req_header_chk = {
"headers": "GET / HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "uuid: none\r\n" + "\r\n",
"timestamp": "1469733493.993",
"body": "",
}
res_header_chk = {
"headers": "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + "\r\n",
"timestamp": "1469733493.993",
"body": "",
}
server.addResponse("sessionlog.json", req_header_chk, res_header_chk)
# set up slice plugin with remap host into cache_range_requests
ts.Disk.remap_config.AddLines(
[
f'map http://slice/ http://127.0.0.1:{server.Variables.Port}/' +
' @plugin=slice.so @pparam=--blockbytes-test=3 @pparam=--remap-host=crr',
f'map http://crr/ http://127.0.0.1:{server.Variables.Port}/' + ' @plugin=cache_range_requests.so @pparam=--consider-ident',
f'map http://slicehdr/ http://127.0.0.1:{server.Variables.Port}/' + ' @plugin=slice.so @pparam=--blockbytes-test=3' +
' @pparam=--remap-host=crrhdr @pparam=--crr-ident-header=crr-foo',
f'map http://crrhdr/ http://127.0.0.1:{server.Variables.Port}/'
' @plugin=cache_range_requests.so @pparam=--ident-header=crr-foo',
])
ts.Disk.plugin_config.AddLine('xdebug.so --enable=x-cache')
ts.Disk.records_config.update(
{
'proxy.config.diags.debug.enabled': 1,
'proxy.config.diags.debug.tags': 'cache_range_requests|slice',
})
curl_and_args = '-s -D /dev/stdout -o /dev/stderr -x localhost:{}'.format(ts.Variables.port) + ' -H "x-debug: x-cache"'
# Test case: 2nd slice out of date (refetch and continue)
req_header_2ndold1 = {
"headers": "GET /second HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "uuid: etagold-1\r\n" + "Range: bytes=3-5\r\n"
"\r\n",
"timestamp": "1469733493.993",
"body": "",
}
res_header_2ndold1 = {
"headers":
"HTTP/1.1 206 Partial Content\r\n" + "Accept-Ranges: bytes\r\n" + "Cache-Control: max-age=5000\r\n" +
"Connection: close\r\n" + "Content-Range: bytes 3-4/5\r\n" + 'Etag: "etagold"\r\n' + "\r\n",
"timestamp": "1469733493.993",
"body": "aa"
}
server.addResponse("sessionlog.json", req_header_2ndold1, res_header_2ndold1)
req_header_2ndnew0 = {
"headers": "GET /second HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "uuid: etagnew-0\r\n" + "Range: bytes=0-2\r\n"
"\r\n",
"timestamp": "1469733493.993",
"body": "",
}
res_header_2ndnew0 = {
"headers":
"HTTP/1.1 206 Partial Content\r\n" + "Accept-Ranges: bytes\r\n" + "Cache-Control: max-age=5000\r\n" +
"Connection: close\r\n" + "Content-Range: bytes 0-2/5\r\n" + 'Etag: "etagnew"\r\n' + "\r\n",
"timestamp": "1469733493.993",
"body": "bbb"
}
server.addResponse("sessionlog.json", req_header_2ndnew0, res_header_2ndnew0)
req_header_2ndnew1 = {
"headers": "GET /second HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "uuid: etagnew-1\r\n" + "Range: bytes=3-5\r\n"
"\r\n",
"timestamp": "1469733493.993",
"body": "",
}
res_header_2ndnew1 = {
"headers":
"HTTP/1.1 206 Partial Content\r\n" + "Accept-Ranges: bytes\r\n" + "Cache-Control: max-age=5000\r\n" +
"Connection: close\r\n" + "Content-Range: bytes 3-4/5\r\n" + 'Etag: "etagnew"\r\n' + "\r\n",
"timestamp": "1469733493.993",
"body": "bb"
}
server.addResponse("sessionlog.json", req_header_2ndnew1, res_header_2ndnew1)
# 0 Test - Preload reference etagnew-0
tr = Test.AddTestRun("Preload reference etagnew-0")
ps = tr.Processes.Default
ps.StartBefore(server, ready=When.PortOpen(server.Variables.Port))
ps.StartBefore(Test.Processes.ts)
tr.MakeCurlCommand(curl_and_args + ' http://crr/second -r 0-2 -H "uuid: etagnew-0"', ts=ts)
ps.ReturnCode = 0
ps.Streams.stderr = "gold/bbb.gold"
ps.Streams.stdout.Content = Testers.ContainsExpression("etagnew", "expected etagnew")
tr.StillRunningAfter = ts
# 1 Test - Preload slice etagold-1
tr = Test.AddTestRun("Preload slice etagold-1")
ps = tr.Processes.Default
tr.MakeCurlCommand(curl_and_args + ' http://crr/second -r 3-5 -H "uuid: etagold-1"', ts=ts)
ps.ReturnCode = 0
ps.Streams.stderr = "gold/aa.gold"
ps.Streams.stdout.Content = Testers.ContainsExpression("etagold", "expected etagold")
tr.StillRunningAfter = ts
# 2 Test - Request second slice via slice plugin, with instructions to fetch new 2nd slice
tr = Test.AddTestRun("Request 2nd slice (expect refetch)")
ps = tr.Processes.Default
tr.MakeCurlCommand(curl_and_args + ' http://slice/second -r 3- -H "uuid: etagnew-1"', ts=ts)
ps.ReturnCode = 0
ps.Streams.stderr = "gold/bb.gold"
ps.Streams.stdout.Content = Testers.ContainsExpression("etagnew", "expected etagnew")
tr.StillRunningAfter = ts
# 3 Test - Request fully healed asset via slice plugin
tr = Test.AddTestRun("Request full healed slice")
ps = tr.Processes.Default
tr.MakeCurlCommand(curl_and_args + ' http://slice/second', ts=ts)
ps.ReturnCode = 0
ps.Streams.stderr.Content = Testers.ContainsExpression("bbbbb", "expected bbbbb content")
ps.Streams.stdout.Content = Testers.ContainsExpression("etagnew", "expected etagnew")
tr.StillRunningAfter = ts
# Test case: reference slice out of date (abort connection, heal reference)
req_header_refold0 = {
"headers": "GET /reference HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "uuid: etagold-0\r\n" + "Range: bytes=0-2\r\n"
"\r\n",
"timestamp": "1469733493.993",
"body": "",
}
res_header_refold0 = {
"headers":
"HTTP/1.1 206 Partial Content\r\n" + "Accept-Ranges: bytes\r\n" + "Cache-Control: max-age=5000\r\n" +
"Connection: close\r\n" + "Content-Range: bytes 0-2/5\r\n" + 'Etag: "etagold"\r\n' + "\r\n",
"timestamp": "1469733493.993",
"body": "aaa"
}
server.addResponse("sessionlog.json", req_header_refold0, res_header_refold0)
req_header_refnew0 = {
"headers": "GET /reference HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "uuid: etagnew-0\r\n" + "Range: bytes=0-2\r\n"
"\r\n",
"timestamp": "1469733493.993",
"body": "",
}
res_header_refnew0 = {
"headers":
"HTTP/1.1 206 Partial Content\r\n" + "Accept-Ranges: bytes\r\n" + "Cache-Control: max-age=5000\r\n" +
"Connection: close\r\n" + "Content-Range: bytes 0-2/5\r\n" + 'Etag: "etagnew"\r\n' + "\r\n",
"timestamp": "1469733493.993",
"body": "bbb"
}
server.addResponse("sessionlog.json", req_header_refnew0, res_header_refnew0)
req_header_refnew1 = {
"headers": "GET /reference HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "uuid: etagnew-1\r\n" + "Range: bytes=3-5\r\n"
"\r\n",
"timestamp": "1469733493.993",
"body": "",
}
res_header_refnew1 = {
"headers":
"HTTP/1.1 206 Partial Content\r\n" + "Accept-Ranges: bytes\r\n" + "Cache-Control: max-age=5000\r\n" +
"Connection: close\r\n" + "Content-Range: bytes 3-4/5\r\n" + 'Etag: "etagnew"\r\n' + "\r\n",
"timestamp": "1469733493.993",
"body": "bb"
}
server.addResponse("sessionlog.json", req_header_refnew1, res_header_refnew1)
# 4 Test - Preload reference etagold-0
tr = Test.AddTestRun("Preload reference etagold-0")
ps = tr.Processes.Default
tr.MakeCurlCommand(curl_and_args + ' http://crr/reference -r 0-2 -H "uuid: etagold-0"', ts=ts)
ps.ReturnCode = 0
ps.Streams.stderr = "gold/aaa.gold"
ps.Streams.stdout.Content = Testers.ContainsExpression("etagold", "expected etagold")
tr.StillRunningAfter = ts
# 5 Test - Preload slice 1 etagnew-1
tr = Test.AddTestRun("Preload slice etagnew-1")
ps = tr.Processes.Default
tr.MakeCurlCommand(curl_and_args + ' http://crr/reference -r 3-5 -H "uuid: etagnew-1"', ts=ts)
ps.ReturnCode = 0
ps.Streams.stderr = "gold/bb.gold"
ps.Streams.stdout.Content = Testers.ContainsExpression("etagnew", "expected etagnew")
tr.StillRunningAfter = ts
# 6 Test - Request reference slice via slice plugin, with instructions to
# fetch new 2nd slice -- this will send the old header, but abort and
# refetch it
tr = Test.AddTestRun("Request 2nd slice (expect abort)")
ps = tr.Processes.Default
tr.MakeCurlCommand(curl_and_args + ' http://slice/reference -r 3- -H "uuid: etagnew-0" -w "SENT: \'%{size_download}\'"', ts=ts)
# ps.ReturnCode = 0 # curl will fail here
ps.Streams.stdout.Content = Testers.ContainsExpression("etagold", "expected etagold")
ps.Streams.stdout.Content += Testers.ContainsExpression("SENT: '0'", "expected empty payload")
tr.StillRunningAfter = ts
# 7 Test - Request full healed asset via slice plugin
tr = Test.AddTestRun("Request full healed slice")
ps = tr.Processes.Default
tr.MakeCurlCommand(curl_and_args + ' http://slice/reference', ts=ts)
ps.ReturnCode = 0
ps.Streams.stderr.Content = Testers.ContainsExpression("bbbbb", "expected bbbbb content")
ps.Streams.stdout.Content = Testers.ContainsExpression("etagnew", "expected etagnew")
tr.StillRunningAfter = ts
# Request results in 200, not 206 (server not support range requests)
req_header_200 = {
"headers": "GET /code200 HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "uuid: code200\r\n" + "Range: bytes=3-5\r\n"
"\r\n",
"timestamp": "1469733493.993",
"body": "",
}
res_header_200 = {
"headers": "HTTP/1.1 200 OK\r\n" + "Cache-Control: max-age=5000\r\n" + "Connection: close\r\n" + 'Etag: "etag"\r\n' + "\r\n",
"timestamp": "1469733493.993",
"body": "ccccc"
}
server.addResponse("sessionlog.json", req_header_200, res_header_200)
# 8 test - Request through slice but get a 200 back
tr = Test.AddTestRun("Request gets a 200")
ps = tr.Processes.Default
tr.MakeCurlCommand(curl_and_args + ' http://slice/code200 -r 3-5 -H "uuid: code200"', ts=ts)
ps.ReturnCode = 0
ps.Streams.stderr.Content = Testers.ContainsExpression("ccccc", "expected full ccccc content")
ps.Streams.stdout.Content = Testers.ContainsExpression("200 OK", "expected 200")
tr.StillRunningAfter = ts
# Test for asset gone
# Preload
req_header_assetgone0 = {
"headers": "GET /assetgone HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "uuid: assetgone-0\r\n" + "Range: bytes=0-2\r\n"
"\r\n",
"timestamp": "1469733493.993",
"body": "",
}
res_header_assetgone0 = {
"headers":
"HTTP/1.1 206 Partial Content\r\n" + "Accept-Ranges: bytes\r\n" + "Cache-Control: max-age=5000\r\n" +
"Connection: close\r\n" + "Content-Range: bytes 0-2/5\r\n" + 'Etag: "etag"\r\n' + "\r\n",
"timestamp": "1469733493.993",
"body": "aaa"
}
server.addResponse("sessionlog.json", req_header_assetgone0, res_header_assetgone0)
# 9 test - Preload reference slice
tr = Test.AddTestRun("Preload reference assetgone-0")
ps = tr.Processes.Default
tr.MakeCurlCommand(curl_and_args + ' http://slice/assetgone -r 0-2 -H "uuid: assetgone-0"', ts=ts)
ps.ReturnCode = 0
ps.Streams.stderr = "gold/aaa.gold"
ps.Streams.stdout.Content = Testers.ContainsExpression("etag", "expected etag")
tr.StillRunningAfter = ts
# 10 test - Fetch full asset, 2nd slice should trigger 404 response
tr = Test.AddTestRun("Fetch full asset")
ps = tr.Processes.Default
tr.MakeCurlCommand(curl_and_args + ' http://slice/assetgone', ts=ts)
# ps.ReturnCode = 0 # curl will return non zero
ps.Streams.stderr = "gold/aaa.gold"
ps.Streams.stdout.Content = Testers.ContainsExpression("etag", "expected etag")
ps.Streams.stdout.Content += Testers.ContainsExpression("Content-Length: 5", "expected header of content-length 5")
tr.StillRunningAfter = ts
# 11 test - Fetch full asset again, full blown 404
tr = Test.AddTestRun("Fetch full asset, 404")
ps = tr.Processes.Default
tr.MakeCurlCommand(curl_and_args + ' http://slice/assetgone', ts=ts)
# ps.ReturnCode = 0 # curl will return non zero
ps.Streams.stdout.Content = Testers.ContainsExpression("404 Not Found", "Expected 404")
tr.StillRunningAfter = ts
## custom headers
# Test case: 2nd slice out of date (refetch and continue)
req_header_custom_2ndold1 = {
"headers":
"GET /second-custom HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "uuid: etagold-custom-1\r\n" + "Range: bytes=3-5\r\n"
"\r\n",
"timestamp": "1469733493.993",
"body": "",
}
res_header_custom_2ndold1 = {
"headers":
"HTTP/1.1 206 Partial Content\r\n" + "Accept-Ranges: bytes\r\n" + "Cache-Control: max-age=5000\r\n" +
"Connection: close\r\n" + "Content-Range: bytes 3-4/5\r\n" + 'Etag: "etagold-custom"\r\n' + "\r\n",
"timestamp": "1469733493.993",
"body": "aa"
}
server.addResponse("sessionlog.json", req_header_custom_2ndold1, res_header_custom_2ndold1)
req_header_custom_2ndnew0 = {
"headers":
"GET /second-custom HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "uuid: etagnew-custom-0\r\n" + "Range: bytes=0-2\r\n"
"\r\n",
"timestamp": "1469733493.993",
"body": "",
}
res_header_custom_2ndnew0 = {
"headers":
"HTTP/1.1 206 Partial Content\r\n" + "Accept-Ranges: bytes\r\n" + "Cache-Control: max-age=5000\r\n" +
"Connection: close\r\n" + "Content-Range: bytes 0-2/5\r\n" + 'Etag: "etagnew-custom"\r\n' + "\r\n",
"timestamp": "1469733493.993",
"body": "bbb"
}
server.addResponse("sessionlog.json", req_header_custom_2ndnew0, res_header_custom_2ndnew0)
req_header_custom_2ndnew1 = {
"headers":
"GET /second-custom HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "uuid: etagnew-custom-1\r\n" + "Range: bytes=3-5\r\n"
"\r\n",
"timestamp": "1469733493.993",
"body": "",
}
res_header_custom_2ndnew1 = {
"headers":
"HTTP/1.1 206 Partial Content\r\n" + "Accept-Ranges: bytes\r\n" + "Cache-Control: max-age=5000\r\n" +
"Connection: close\r\n" + "Content-Range: bytes 3-4/5\r\n" + 'Etag: "etagnew-custom"\r\n' + "\r\n",
"timestamp": "1469733493.993",
"body": "bb"
}
server.addResponse("sessionlog.json", req_header_custom_2ndnew1, res_header_custom_2ndnew1)
edt = datetime.datetime.fromtimestamp(time.time() + 100)
edate = to_httpdate(edt)
# 12 Test - Preload reference etagold-1
tr = Test.AddTestRun("Preload slice etagold-custom-1")
ps = tr.Processes.Default
tr.MakeCurlCommand(curl_and_args + f' http://crrhdr/second-custom -r 3-5 -H "uuid: etagold-custom-1" -H "crr-foo: {edate}"', ts=ts)
ps.ReturnCode = 0
ps.Streams.stderr = "gold/aa.gold"
ps.Streams.stdout.Content = Testers.ContainsExpression("etagold-custom", "expected etagold-custom")
tr.StillRunningAfter = ts
# 13 Test - Request second slice via slice plugin, with instructions to fetch new 2nd slice
tr = Test.AddTestRun("Request 2nd slice (expect refetch)")
ps = tr.Processes.Default
tr.MakeCurlCommand(curl_and_args + ' http://slicehdr/second-custom -r 3- -H "uuid: etagnew-custom-1"', ts=ts)
ps.ReturnCode = 0
ps.Streams.stderr = "gold/bb.gold"
ps.Streams.stdout.Content = Testers.ContainsExpression("etagnew-custom", "expected etagnew-custom")
tr.StillRunningAfter = ts
# Over riding the built in ERROR check since we expect to see logSliceErrors
ts.Disk.diags_log.Content = Testers.ContainsExpression("logSliceError", "logSliceErrors generated")