mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2025-01-13 16:03:22 -05:00
test and fix If-Match handling
Seeking in Chrome 55 wasn't working. It apparently sends If-Match requests with the correct etag, which Moonfire NVR was incorrectly responding to with "Precondition failed" responses. Fix and test that. I hadn't noticed the problem in earlier versions of Chrome. I think they were using If-Range instead, which is already tested and working.
This commit is contained in:
parent
8df0eae567
commit
632358b039
@ -124,7 +124,9 @@ fn none_match(etag: Option<&header::EntityTag>, req: &Request) -> bool {
|
|||||||
/// Returns true if `req` has no `If-Match` header or one which matches `etag`.
|
/// Returns true if `req` has no `If-Match` header or one which matches `etag`.
|
||||||
fn any_match(etag: Option<&header::EntityTag>, req: &Request) -> bool {
|
fn any_match(etag: Option<&header::EntityTag>, req: &Request) -> bool {
|
||||||
match req.headers.get::<header::IfMatch>() {
|
match req.headers.get::<header::IfMatch>() {
|
||||||
Some(&header::IfMatch::Any) => true,
|
// The absent header and "If-Match: *" cases differ only when there is no entity to serve.
|
||||||
|
// We always have an entity to serve, so consider them identical.
|
||||||
|
None | Some(&header::IfMatch::Any) => true,
|
||||||
Some(&header::IfMatch::Items(ref items)) => {
|
Some(&header::IfMatch::Items(ref items)) => {
|
||||||
if let Some(some_etag) = etag {
|
if let Some(some_etag) = etag {
|
||||||
for item in items {
|
for item in items {
|
||||||
@ -135,7 +137,6 @@ fn any_match(etag: Option<&header::EntityTag>, req: &Request) -> bool {
|
|||||||
}
|
}
|
||||||
false
|
false
|
||||||
},
|
},
|
||||||
None => false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +174,7 @@ pub fn serve(rsrc: &Resource, req: &Request, mut res: Response<Fresh>) -> Result
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if any_match(etag, req) {
|
if !any_match(etag, req) {
|
||||||
*res.status_mut() = hyper::status::StatusCode::PreconditionFailed;
|
*res.status_mut() = hyper::status::StatusCode::PreconditionFailed;
|
||||||
res.send(b"Precondition failed")?;
|
res.send(b"Precondition failed")?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -438,6 +439,27 @@ mod tests {
|
|||||||
resp.read_to_end(&mut buf).unwrap();
|
resp.read_to_end(&mut buf).unwrap();
|
||||||
assert_eq!(b"01234", &buf[..]);
|
assert_eq!(b"01234", &buf[..]);
|
||||||
|
|
||||||
|
// If-Match any should still send the full body.
|
||||||
|
let mut resp = client.get(&*SERVER)
|
||||||
|
.header(header::IfMatch::Any)
|
||||||
|
.send()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(hyper::status::StatusCode::Ok, resp.status);
|
||||||
|
assert_eq!(Some(&header::ContentType(mime!(Application/OctetStream))),
|
||||||
|
resp.headers.get::<header::ContentType>());
|
||||||
|
assert_eq!(None, resp.headers.get::<header::ContentRange>());
|
||||||
|
buf.clear();
|
||||||
|
resp.read_to_end(&mut buf).unwrap();
|
||||||
|
assert_eq!(b"01234", &buf[..]);
|
||||||
|
|
||||||
|
// If-Match by etag doesn't match (as this request has no etag).
|
||||||
|
let resp =
|
||||||
|
client.get(&*SERVER)
|
||||||
|
.header(header::IfMatch::Items(vec![EntityTag::strong("foo".to_owned())]))
|
||||||
|
.send()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(hyper::status::StatusCode::PreconditionFailed, resp.status);
|
||||||
|
|
||||||
// If-None-Match any.
|
// If-None-Match any.
|
||||||
let mut resp = client.get(&*SERVER)
|
let mut resp = client.get(&*SERVER)
|
||||||
.header(header::IfNoneMatch::Any)
|
.header(header::IfNoneMatch::Any)
|
||||||
@ -571,6 +593,41 @@ mod tests {
|
|||||||
let client = hyper::Client::new();
|
let client = hyper::Client::new();
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
|
|
||||||
|
// If-Match any should still send the full body.
|
||||||
|
let mut resp = client.get(&*SERVER)
|
||||||
|
.header(header::IfMatch::Any)
|
||||||
|
.send()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(hyper::status::StatusCode::Ok, resp.status);
|
||||||
|
assert_eq!(Some(&header::ContentType(mime!(Application/OctetStream))),
|
||||||
|
resp.headers.get::<header::ContentType>());
|
||||||
|
assert_eq!(None, resp.headers.get::<header::ContentRange>());
|
||||||
|
buf.clear();
|
||||||
|
resp.read_to_end(&mut buf).unwrap();
|
||||||
|
assert_eq!(b"01234", &buf[..]);
|
||||||
|
|
||||||
|
// If-Match by matching etag should send the full body.
|
||||||
|
let mut resp =
|
||||||
|
client.get(&*SERVER)
|
||||||
|
.header(header::IfMatch::Items(vec![EntityTag::strong("foo".to_owned())]))
|
||||||
|
.send()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(hyper::status::StatusCode::Ok, resp.status);
|
||||||
|
assert_eq!(Some(&header::ContentType(mime!(Application/OctetStream))),
|
||||||
|
resp.headers.get::<header::ContentType>());
|
||||||
|
assert_eq!(None, resp.headers.get::<header::ContentRange>());
|
||||||
|
buf.clear();
|
||||||
|
resp.read_to_end(&mut buf).unwrap();
|
||||||
|
assert_eq!(b"01234", &buf[..]);
|
||||||
|
|
||||||
|
// If-Match by etag which doesn't match.
|
||||||
|
let resp =
|
||||||
|
client.get(&*SERVER)
|
||||||
|
.header(header::IfMatch::Items(vec![EntityTag::strong("bar".to_owned())]))
|
||||||
|
.send()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(hyper::status::StatusCode::PreconditionFailed, resp.status);
|
||||||
|
|
||||||
// If-None-Match by etag which matches.
|
// If-None-Match by etag which matches.
|
||||||
let mut resp =
|
let mut resp =
|
||||||
client.get(&*SERVER)
|
client.get(&*SERVER)
|
||||||
@ -640,6 +697,27 @@ mod tests {
|
|||||||
let client = hyper::Client::new();
|
let client = hyper::Client::new();
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
|
|
||||||
|
// If-Match any should still send the full body.
|
||||||
|
let mut resp = client.get(&*SERVER)
|
||||||
|
.header(header::IfMatch::Any)
|
||||||
|
.send()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(hyper::status::StatusCode::Ok, resp.status);
|
||||||
|
assert_eq!(Some(&header::ContentType(mime!(Application/OctetStream))),
|
||||||
|
resp.headers.get::<header::ContentType>());
|
||||||
|
assert_eq!(None, resp.headers.get::<header::ContentRange>());
|
||||||
|
buf.clear();
|
||||||
|
resp.read_to_end(&mut buf).unwrap();
|
||||||
|
assert_eq!(b"01234", &buf[..]);
|
||||||
|
|
||||||
|
// If-Match by etag doesn't match because matches use the strong comparison function.
|
||||||
|
let resp =
|
||||||
|
client.get(&*SERVER)
|
||||||
|
.header(header::IfMatch::Items(vec![EntityTag::weak("foo".to_owned())]))
|
||||||
|
.send()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(hyper::status::StatusCode::PreconditionFailed, resp.status);
|
||||||
|
|
||||||
// If-None-Match by identical weak etag is sufficient.
|
// If-None-Match by identical weak etag is sufficient.
|
||||||
let mut resp =
|
let mut resp =
|
||||||
client.get(&*SERVER)
|
client.get(&*SERVER)
|
||||||
|
Loading…
Reference in New Issue
Block a user