Skip to content

Commit e634ba0

Browse files
authored
[release/6.x] Handle both inclusive-end and exclusive-end content-range headers, via the content-length header (#7646)
1 parent 91f1cc0 commit e634ba0

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1111

1212
### Fixed
1313

14+
- Snapshot fetching requests now handle either inclusive-end or exclusive-end `content-range` headers, for compatibility with 7.x nodes.
1415
- Primaries now indicate all of their chunk-ending transactions in the transaction header, for better interop with 7.x nodes. Upgrades must create a snapshot from the network after it has upgraded to this version, and provide that to joining 7.x nodes.
1516

1617
## [6.0.20]

src/snapshots/fetch.h

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,65 @@ namespace snapshots
115115
}
116116
}
117117

118+
{
119+
// Use content-length to determine whether the sender used an exclusive or
120+
// inclusive range end
121+
auto length_it = headers.find(ccf::http::headers::CONTENT_LENGTH);
122+
if (length_it == headers.end())
123+
{
124+
throw std::runtime_error(
125+
"Response is missing expected content-length header");
126+
}
127+
128+
size_t content_length = 0;
129+
130+
{
131+
const auto& length_s = length_it->second;
132+
133+
const auto [p, ec] = std::from_chars(
134+
length_s.data(), length_s.data() + length_s.size(), content_length);
135+
136+
if (ec != std::errc())
137+
{
138+
throw std::runtime_error(fmt::format(
139+
"Could not parse length from content-length header: {}",
140+
length_it->second));
141+
}
142+
}
143+
144+
const auto range_length =
145+
parsed_values.range_end - parsed_values.range_start;
146+
if (range_length == content_length)
147+
{
148+
LOG_DEBUG_FMT(
149+
"Server sent an exclusive-end content-range header. "
150+
"content-length={}, content-range={}. This is expected for 6.x to "
151+
"6.x nodes",
152+
length_it->second,
153+
it->second);
154+
}
155+
else if (range_length + 1 == content_length)
156+
{
157+
LOG_INFO_FMT(
158+
"Server sent an inclusive-end content-range header. "
159+
"content-length={}, content-range={}. Adjusting this to local "
160+
"exclusive-end representation. This should be a temporary "
161+
"mismatch, between 6.x and 7.x nodes in a mixed network",
162+
length_it->second,
163+
it->second);
164+
parsed_values.range_end += 1;
165+
}
166+
else
167+
{
168+
throw std::runtime_error(fmt::format(
169+
"content-range ({}, {} bytes) and content-length ({}) headers do not "
170+
"agree",
171+
it->second,
172+
range_length,
173+
length_it->second));
174+
}
175+
}
176+
118177
return parsed_values;
119178
}
120179

0 commit comments

Comments
 (0)