Byte ranges are properly advertised and served.

This means Firefox can seek video / audio served by us.
To think, you used to need Flash to do this.
main
_ 2020-10-27 22:04:08 -05:00
parent aa183de15e
commit 1e6c29fb01
2 changed files with 41 additions and 15 deletions

View File

@ -1,6 +1,7 @@
use std::{ use std::{
cmp::{min, max},
collections::*, collections::*,
convert::Infallible, convert::{Infallible, TryInto},
error::Error, error::Error,
io::SeekFrom, io::SeekFrom,
path::PathBuf, path::PathBuf,
@ -31,9 +32,11 @@ use ptth::http_serde;
fn parse_range_header (range_str: &str) -> (Option <u64>, Option <u64>) { fn parse_range_header (range_str: &str) -> (Option <u64>, Option <u64>) {
lazy_static! { lazy_static! {
static ref RE: Regex = Regex::new (r"^(\d+)-(\d+)$").expect ("Couldn't compile regex for Range header"); static ref RE: Regex = Regex::new (r"^bytes=(\d*)-(\d*)$").expect ("Couldn't compile regex for Range header");
} }
println! ("{}", range_str);
let caps = match RE.captures (range_str) { let caps = match RE.captures (range_str) {
Some (x) => x, Some (x) => x,
_ => return (None, None), _ => return (None, None),
@ -100,7 +103,7 @@ async fn main () -> Result <(), Box <dyn Error>> {
for (k, v) in parts.headers.iter () { for (k, v) in parts.headers.iter () {
let v = std::str::from_utf8 (v).unwrap (); let v = std::str::from_utf8 (v).unwrap ();
println! ("{}: {}", k, v); //println! ("{}: {}", k, v);
if k == "range" { if k == "range" {
let (start, end) = parse_range_header (v); let (start, end) = parse_range_header (v);
@ -114,7 +117,7 @@ async fn main () -> Result <(), Box <dyn Error>> {
_ => false, _ => false,
}; };
println! ("Step 6"); //println! ("Step 6");
let client = client.clone (); let client = client.clone ();
tokio::spawn (async move { tokio::spawn (async move {
let (tx, rx) = channel (2); let (tx, rx) = channel (2);
@ -129,11 +132,18 @@ async fn main () -> Result <(), Box <dyn Error>> {
path.push (&uri [1..]); path.push (&uri [1..]);
let mut f = File::open (path).await.unwrap (); let mut f = File::open (path).await.unwrap ();
if let Some (start) = range_start {
f.seek (SeekFrom::Start (start)).await.unwrap ();
}
let file_md = f.metadata ().await.unwrap (); let file_md = f.metadata ().await.unwrap ();
let file_len = file_md.len ();
let start = range_start.unwrap_or (0);
let end = range_end.unwrap_or (file_len);
let start = max (0, min (start, file_len));
let end = max (0, min (end, file_len));
f.seek (SeekFrom::Start (start)).await.unwrap ();
println! ("Serving range {}-{}", start, end);
if should_send_body { if should_send_body {
tokio::spawn (async move { tokio::spawn (async move {
@ -141,12 +151,15 @@ async fn main () -> Result <(), Box <dyn Error>> {
let mut tx = tx; let mut tx = tx;
//let mut bytes_sent = 0; //let mut bytes_sent = 0;
let mut bytes_left = end - start;
loop { loop {
let mut buffer = vec! [0u8; 65_536]; let mut buffer = vec! [0u8; 65_536];
let bytes_read = f.read (&mut buffer).await.unwrap (); let bytes_read: u64 = f.read (&mut buffer).await.unwrap ().try_into ().unwrap ();
buffer.truncate (bytes_read); let bytes_read = min (bytes_left, bytes_read);
buffer.truncate (bytes_read.try_into ().unwrap ());
if bytes_read == 0 { if bytes_read == 0 {
break; break;
@ -156,6 +169,11 @@ async fn main () -> Result <(), Box <dyn Error>> {
break; break;
} }
bytes_left -= bytes_read;
if bytes_left == 0 {
break;
}
//bytes_sent += bytes_read; //bytes_sent += bytes_read;
//println! ("Sent {} bytes", bytes_sent); //println! ("Sent {} bytes", bytes_sent);
@ -165,14 +183,21 @@ async fn main () -> Result <(), Box <dyn Error>> {
} }
let mut headers: HashMap <String, Vec <u8>> = Default::default (); let mut headers: HashMap <String, Vec <u8>> = Default::default ();
//headers.insert (String::from ("x-its-a-header"), Vec::from (&b"wow"[..])); headers.insert (String::from ("accept-ranges"), b"bytes".to_vec ());
let status_code;
if range_start.is_none () && range_end.is_none () { if range_start.is_none () && range_end.is_none () {
headers.insert (String::from ("content-length"), file_md.len ().to_string ().into_bytes ()); headers.insert (String::from ("content-length"), end.to_string ().into_bytes ());
status_code = http_serde::StatusCode::Ok;
}
else {
headers.insert (String::from ("content-range"), format! ("bytes {}-{}/{}", start, end - 1, end).into_bytes ());
status_code = http_serde::StatusCode::PartialContent;
} }
let resp_parts = http_serde::ResponseParts { let resp_parts = http_serde::ResponseParts {
status_code: http_serde::StatusCode::Ok, status_code,
headers, headers,
}; };
@ -184,7 +209,7 @@ async fn main () -> Result <(), Box <dyn Error>> {
resp_req = resp_req.body (body); resp_req = resp_req.body (body);
} }
println! ("Step 6"); //println! ("Step 6");
if let Err (e) = resp_req.send ().await { if let Err (e) = resp_req.send ().await {
println! ("Err: {:?}", e); println! ("Err: {:?}", e);
} }

View File

@ -1 +1,2 @@
- Byte range request header - Set up tokens or something we clients can't trivially
impersonate servers