cargo fmt

This commit is contained in:
xenofem 2022-04-27 00:55:36 -04:00
parent 3d5010806b
commit ba3326ef24
4 changed files with 116 additions and 74 deletions

View file

@ -1,4 +1,4 @@
use std::{fs::File, task::Waker, io::Write, path::PathBuf}; use std::{fs::File, io::Write, path::PathBuf, task::Waker};
pub trait LiveWriter: Write { pub trait LiveWriter: Write {
fn add_waker(&mut self, waker: Waker); fn add_waker(&mut self, waker: Waker);

View file

@ -3,12 +3,16 @@ mod file;
mod upload; mod upload;
mod zip; mod zip;
use std::{collections::HashMap, task::Waker, sync::{mpsc::Sender, RwLock}, path::PathBuf}; use std::{
collections::HashMap,
path::PathBuf,
sync::{mpsc::Sender, RwLock},
task::Waker,
};
use actix::Addr; use actix::Addr;
use actix_web::{ use actix_web::{
get, middleware::Logger, web, App, HttpResponse, HttpServer, get, middleware::Logger, web, App, HttpRequest, HttpResponse, HttpServer, Responder,
Responder, HttpRequest,
}; };
use actix_web_actors::ws; use actix_web_actors::ws;
use time::OffsetDateTime; use time::OffsetDateTime;
@ -31,7 +35,6 @@ impl UploadedFile {
} }
} }
pub struct DownloadableFile { pub struct DownloadableFile {
name: String, name: String,
size: usize, size: usize,
@ -46,16 +49,8 @@ fn storage_dir() -> PathBuf {
} }
#[get("/upload")] #[get("/upload")]
async fn upload_socket( async fn upload_socket(req: HttpRequest, stream: web::Payload, data: AppData) -> impl Responder {
req: HttpRequest, ws::start(upload::Uploader::new(data), &req, stream)
stream: web::Payload,
data: AppData,
) -> impl Responder {
ws::start(
upload::Uploader::new(data),
&req,
stream
)
} }
#[actix_web::main] #[actix_web::main]
@ -64,8 +59,12 @@ async fn main() -> std::io::Result<()> {
let data: AppData = web::Data::new(RwLock::new(HashMap::new())); let data: AppData = web::Data::new(RwLock::new(HashMap::new()));
let static_dir = PathBuf::from(std::env::var("STATIC_DIR").unwrap_or_else(|_| String::from("static"))); let static_dir =
let port = std::env::var("PORT").ok().and_then(|p| p.parse::<u16>().ok()).unwrap_or(8080); PathBuf::from(std::env::var("STATIC_DIR").unwrap_or_else(|_| String::from("static")));
let port = std::env::var("PORT")
.ok()
.and_then(|p| p.parse::<u16>().ok())
.unwrap_or(8080);
HttpServer::new(move || { HttpServer::new(move || {
App::new() App::new()

View file

@ -1,14 +1,14 @@
use std::{collections::HashSet, fmt::Display, io::Write}; use std::{collections::HashSet, fmt::Display, io::Write};
use actix::{Actor, StreamHandler, ActorContext, AsyncContext}; use actix::{Actor, ActorContext, AsyncContext, StreamHandler};
use actix_http::ws::{Item, CloseReason}; use actix_http::ws::{CloseReason, Item};
use actix_web_actors::ws::{self, CloseCode}; use actix_web_actors::ws::{self, CloseCode};
use log::{error, debug, info, trace}; use log::{debug, error, info, trace};
use rand::distributions::{Alphanumeric, DistString}; use rand::distributions::{Alphanumeric, DistString};
use serde::Deserialize; use serde::Deserialize;
use time::OffsetDateTime; use time::OffsetDateTime;
use crate::{UploadedFile, DownloadableFile, file::LiveWriter}; use crate::{file::LiveWriter, DownloadableFile, UploadedFile};
const FILENAME_DATE_FORMAT: &[time::format_description::FormatItem] = const FILENAME_DATE_FORMAT: &[time::format_description::FormatItem] =
time::macros::format_description!("[year]-[month]-[day]-[hour][minute][second]"); time::macros::format_description!("[year]-[month]-[day]-[hour][minute][second]");
@ -97,7 +97,7 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for Uploader {
error!("Websocket error: {:?}", e); error!("Websocket error: {:?}", e);
ctx.stop(); ctx.stop();
return; return;
}, }
}; };
match self.handle_message(msg, ctx) { match self.handle_message(msg, ctx) {
@ -116,10 +116,10 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for Uploader {
code: CloseCode::Normal, code: CloseCode::Normal,
description: None, description: None,
})); }));
// self.app_data.write().unwrap().entry( // self.app_data.write().unwrap().entry(
ctx.stop(); ctx.stop();
} }
_ => () _ => (),
} }
} }
} }
@ -129,12 +129,16 @@ fn ack(ctx: &mut <Uploader as Actor>::Context) {
} }
impl Uploader { impl Uploader {
fn handle_message(&mut self, msg: ws::Message, ctx: &mut <Self as Actor>::Context) -> Result<bool, Error>{ fn handle_message(
&mut self,
msg: ws::Message,
ctx: &mut <Self as Actor>::Context,
) -> Result<bool, Error> {
trace!("Websocket message: {:?}", msg); trace!("Websocket message: {:?}", msg);
match msg { match msg {
ws::Message::Text(text) => { ws::Message::Text(text) => {
if self.writer.is_some() { if self.writer.is_some() {
return Err(Error::UnexpectedMessageType) return Err(Error::UnexpectedMessageType);
} }
let raw_files: Vec<RawUploadedFile> = serde_json::from_slice(text.as_bytes())?; let raw_files: Vec<RawUploadedFile> = serde_json::from_slice(text.as_bytes())?;
info!("Received file list: {} files", raw_files.len()); info!("Received file list: {} files", raw_files.len());
@ -167,32 +171,42 @@ impl Uploader {
let writer = super::zip::ZipGenerator::new(files, Box::new(writer)); let writer = super::zip::ZipGenerator::new(files, Box::new(writer));
let size = writer.total_size(); let size = writer.total_size();
self.writer = Some(Box::new(writer)); self.writer = Some(Box::new(writer));
let download_filename = super::APP_NAME.to_owned() let download_filename =
+ &now.format(FILENAME_DATE_FORMAT)? super::APP_NAME.to_owned() + &now.format(FILENAME_DATE_FORMAT)? + ".zip";
+ ".zip";
let modtime = now; let modtime = now;
self.app_data.write().map_err(|_| Error::LockPoisoned)?.insert(storage_filename, DownloadableFile { self.app_data
name: download_filename, .write()
size, .map_err(|_| Error::LockPoisoned)?
modtime, .insert(
uploader: Some(ctx.address()), storage_filename,
}); DownloadableFile {
name: download_filename,
size,
modtime,
uploader: Some(ctx.address()),
},
);
} else { } else {
self.writer = Some(Box::new(writer)); self.writer = Some(Box::new(writer));
self.app_data.write().map_err(|_| Error::LockPoisoned)?.insert(storage_filename, DownloadableFile { self.app_data
name: files[0].name.clone(), .write()
size: files[0].size, .map_err(|_| Error::LockPoisoned)?
modtime: files[0].modtime, .insert(
uploader: Some(ctx.address()), storage_filename,
}); DownloadableFile {
name: files[0].name.clone(),
size: files[0].size,
modtime: files[0].modtime,
uploader: Some(ctx.address()),
},
);
} }
ack(ctx); ack(ctx);
} }
ws::Message::Binary(data) ws::Message::Binary(data)
| ws::Message::Continuation(Item::FirstBinary(data)) | ws::Message::Continuation(Item::FirstBinary(data))
| ws::Message::Continuation(Item::Continue(data)) | ws::Message::Continuation(Item::Continue(data))
| ws::Message::Continuation(Item::Last(data)) => | ws::Message::Continuation(Item::Last(data)) => {
{
if let Some(ref mut writer) = self.writer { if let Some(ref mut writer) = self.writer {
if data.len() > self.bytes_remaining { if data.len() > self.bytes_remaining {
return Err(Error::TooMuchData); return Err(Error::TooMuchData);

View file

@ -5,17 +5,20 @@ use crc32fast::Hasher;
use log::debug; use log::debug;
use time::OffsetDateTime; use time::OffsetDateTime;
use crate::UploadedFile;
use crate::file::LiveWriter; use crate::file::LiveWriter;
use crate::UploadedFile;
const SIGNATURE_SIZE: usize = 4; const SIGNATURE_SIZE: usize = 4;
const SHARED_FIELDS_SIZE: usize = 26; const SHARED_FIELDS_SIZE: usize = 26;
const EXTRA_FIELD_SIZE: usize = 41; const EXTRA_FIELD_SIZE: usize = 41;
const LOCAL_HEADER_SIZE_MINUS_FILENAME: usize = SIGNATURE_SIZE + SHARED_FIELDS_SIZE + EXTRA_FIELD_SIZE; const LOCAL_HEADER_SIZE_MINUS_FILENAME: usize =
SIGNATURE_SIZE + SHARED_FIELDS_SIZE + EXTRA_FIELD_SIZE;
const DATA_DESCRIPTOR_SIZE: usize = 24; const DATA_DESCRIPTOR_SIZE: usize = 24;
const FILE_ENTRY_SIZE_MINUS_FILENAME_AND_FILE: usize = LOCAL_HEADER_SIZE_MINUS_FILENAME + DATA_DESCRIPTOR_SIZE; const FILE_ENTRY_SIZE_MINUS_FILENAME_AND_FILE: usize =
LOCAL_HEADER_SIZE_MINUS_FILENAME + DATA_DESCRIPTOR_SIZE;
const CENTRAL_DIRECTORY_HEADER_SIZE_MINUS_FILENAME: usize = SIGNATURE_SIZE + 2 + SHARED_FIELDS_SIZE + 14 + EXTRA_FIELD_SIZE; const CENTRAL_DIRECTORY_HEADER_SIZE_MINUS_FILENAME: usize =
SIGNATURE_SIZE + 2 + SHARED_FIELDS_SIZE + 14 + EXTRA_FIELD_SIZE;
const EOCD64_RECORD_SIZE: usize = 56; const EOCD64_RECORD_SIZE: usize = 56;
const EOCD64_LOCATOR_SIZE: usize = 20; const EOCD64_LOCATOR_SIZE: usize = 20;
@ -59,7 +62,11 @@ fn fat_timestamp(time: OffsetDateTime) -> u32 {
/// Append a value to a byte vector as little-endian bytes /// Append a value to a byte vector as little-endian bytes
fn append_value(data: &mut Vec<u8>, mut value: u64, len: usize) { fn append_value(data: &mut Vec<u8>, mut value: u64, len: usize) {
data.resize_with(data.len() + len, || { let byte = value as u8; value >>= 8; byte }); data.resize_with(data.len() + len, || {
let byte = value as u8;
value >>= 8;
byte
});
} }
fn append_repeated_byte(data: &mut Vec<u8>, byte: u8, count: usize) { fn append_repeated_byte(data: &mut Vec<u8>, byte: u8, count: usize) {
@ -80,9 +87,9 @@ impl UploadedFile {
/// through "Extra field length". /// through "Extra field length".
fn shared_header_fields(&self, hash: Option<u32>) -> Vec<u8> { fn shared_header_fields(&self, hash: Option<u32>) -> Vec<u8> {
let mut fields = vec![ let mut fields = vec![
45, 0, // Minimum version required to extract: 4.5 for ZIP64 extensions 45, 0, // Minimum version required to extract: 4.5 for ZIP64 extensions
0b00001000, 0, // General purpose bit flag: size and CRC-32 in data descriptor 0b00001000, 0, // General purpose bit flag: size and CRC-32 in data descriptor
0, 0, // Compression method: none 0, 0, // Compression method: none
]; ];
append_value(&mut fields, fat_timestamp(self.modtime) as u64, 4); append_value(&mut fields, fat_timestamp(self.modtime) as u64, 4);
// Use 0s as a placeholder if the CRC-32 hash isn't known yet // Use 0s as a placeholder if the CRC-32 hash isn't known yet
@ -97,8 +104,8 @@ impl UploadedFile {
fn extra_field(&self, local_header_offset: usize) -> Vec<u8> { fn extra_field(&self, local_header_offset: usize) -> Vec<u8> {
let mut field = vec![ let mut field = vec![
0x01, 0x00, // Zip64 extended information 0x01, 0x00, // Zip64 extended information
28, 0, // 28 bytes of data 28, 0, // 28 bytes of data
]; ];
// Original size and compressed size - if this is in the local // Original size and compressed size - if this is in the local
// header, we're supposed to leave these blank and point to // header, we're supposed to leave these blank and point to
@ -111,7 +118,7 @@ impl UploadedFile {
field.append(&mut vec![ field.append(&mut vec![
0x55, 0x54, // Extended timestamp 0x55, 0x54, // Extended timestamp
5, 0, // 5 bytes of data 5, 0, // 5 bytes of data
0b00000001, // Flags: Only modification time is present 0b00000001, // Flags: Only modification time is present
]); ]);
append_value(&mut field, self.modtime.unix_timestamp() as u64, 4); append_value(&mut field, self.modtime.unix_timestamp() as u64, 4);
@ -140,11 +147,12 @@ impl UploadedFile {
]; ];
header.append(&mut self.shared_header_fields(Some(hash))); header.append(&mut self.shared_header_fields(Some(hash)));
header.append(&mut vec![ header.append(&mut vec![
0, 0, // File comment length: 0 0, 0, // File comment length: 0
0, 0, // Disk number where file starts: 0 0, 0, // Disk number where file starts: 0
0, 0, // Internal file attributes: nothing 0, 0, // Internal file attributes: nothing
0, 0, 0, 0, // External file attributes: nothing 0, 0, 0, 0, // External file attributes: nothing
0xff, 0xff, 0xff, 0xff, // Relative offset of local file header: placeholder, see ZIP64 data 0xff, 0xff, 0xff,
0xff, // Relative offset of local file header: placeholder, see ZIP64 data
]); ]);
header.append(&mut self.name.clone().into_bytes()); header.append(&mut self.name.clone().into_bytes());
header.append(&mut self.extra_field(local_header_offset)); header.append(&mut self.extra_field(local_header_offset));
@ -167,7 +175,7 @@ fn end_of_central_directory(files: &[UploadedFile]) -> Vec<u8> {
let mut eocd = vec![ let mut eocd = vec![
0x50, 0x4b, 0x06, 0x06, // EOCD64 record signature 0x50, 0x4b, 0x06, 0x06, // EOCD64 record signature
44, // Size of remaining EOCD64 record 44, // Size of remaining EOCD64 record
]; ];
append_0(&mut eocd, 7); // pad out the rest of the size field append_0(&mut eocd, 7); // pad out the rest of the size field
eocd.append(&mut vec![ eocd.append(&mut vec![
@ -175,7 +183,7 @@ fn end_of_central_directory(files: &[UploadedFile]) -> Vec<u8> {
45, 0, // Minimum version 4.5 to extract 45, 0, // Minimum version 4.5 to extract
]); ]);
append_0(&mut eocd, 8); // Two 4-byte disk numbers, both 0 append_0(&mut eocd, 8); // Two 4-byte disk numbers, both 0
// Number of central directory records, on this disk and in total // Number of central directory records, on this disk and in total
append_value(&mut eocd, files.len() as u64, 8); append_value(&mut eocd, files.len() as u64, 8);
append_value(&mut eocd, files.len() as u64, 8); append_value(&mut eocd, files.len() as u64, 8);
append_value(&mut eocd, directory_size, 8); append_value(&mut eocd, directory_size, 8);
@ -200,7 +208,7 @@ pub struct ZipGenerator<'a> {
pending_metadata: Vec<u8>, pending_metadata: Vec<u8>,
hasher: Hasher, hasher: Hasher,
hashes: Vec<u32>, hashes: Vec<u32>,
output: Box<dyn LiveWriter + 'a> output: Box<dyn LiveWriter + 'a>,
} }
impl<'a> ZipGenerator<'a> { impl<'a> ZipGenerator<'a> {
@ -225,8 +233,12 @@ impl<'a> ZipGenerator<'a> {
fn finish_file(&mut self) { fn finish_file(&mut self) {
let hash = std::mem::replace(&mut self.hasher, Hasher::new()).finalize(); let hash = std::mem::replace(&mut self.hasher, Hasher::new()).finalize();
self.hashes.push(hash); self.hashes.push(hash);
self.pending_metadata.append(&mut self.files[self.file_index].data_descriptor(hash)); self.pending_metadata
debug!("Finishing file entry in zipfile: {}, hash {}", self.files[self.file_index].name, hash); .append(&mut self.files[self.file_index].data_descriptor(hash));
debug!(
"Finishing file entry in zipfile: {}, hash {}",
self.files[self.file_index].name, hash
);
self.file_index += 1; self.file_index += 1;
self.start_new_file(); self.start_new_file();
} }
@ -234,19 +246,27 @@ impl<'a> ZipGenerator<'a> {
fn start_new_file(&mut self) { fn start_new_file(&mut self) {
let mut offset = file_entries_size(&self.files[..self.file_index]); let mut offset = file_entries_size(&self.files[..self.file_index]);
while self.file_index < self.files.len() && self.files[self.file_index].size == 0 { while self.file_index < self.files.len() && self.files[self.file_index].size == 0 {
debug!("Empty file entry in zipfile: {}", self.files[self.file_index].name); debug!(
"Empty file entry in zipfile: {}",
self.files[self.file_index].name
);
self.hashes.push(EMPTY_STRING_CRC32); self.hashes.push(EMPTY_STRING_CRC32);
let mut local_header = self.files[self.file_index].local_header(offset); let mut local_header = self.files[self.file_index].local_header(offset);
let mut data_descriptor = self.files[self.file_index].data_descriptor(EMPTY_STRING_CRC32); let mut data_descriptor =
self.files[self.file_index].data_descriptor(EMPTY_STRING_CRC32);
offset += local_header.len() + data_descriptor.len(); offset += local_header.len() + data_descriptor.len();
self.file_index += 1; self.file_index += 1;
self.pending_metadata.append(&mut local_header); self.pending_metadata.append(&mut local_header);
self.pending_metadata.append(&mut data_descriptor); self.pending_metadata.append(&mut data_descriptor);
} }
if self.file_index < self.files.len() { if self.file_index < self.files.len() {
debug!("Starting file entry in zipfile: {}", self.files[self.file_index].name); debug!(
"Starting file entry in zipfile: {}",
self.files[self.file_index].name
);
self.byte_index = 0; self.byte_index = 0;
self.pending_metadata.append(&mut self.files[self.file_index].local_header(offset)); self.pending_metadata
.append(&mut self.files[self.file_index].local_header(offset));
} else { } else {
self.finish_zipfile(); self.finish_zipfile();
} }
@ -256,12 +276,17 @@ impl<'a> ZipGenerator<'a> {
debug!("Writing zipfile central directory"); debug!("Writing zipfile central directory");
let mut offset = 0; let mut offset = 0;
for (i, file) in self.files.iter().enumerate() { for (i, file) in self.files.iter().enumerate() {
debug!("Writing central directory entry: {}, hash {}", file.name, self.hashes[i]); debug!(
self.pending_metadata.append(&mut file.central_directory_header(offset, self.hashes[i])); "Writing central directory entry: {}, hash {}",
file.name, self.hashes[i]
);
self.pending_metadata
.append(&mut file.central_directory_header(offset, self.hashes[i]));
offset += file_entry_size(file); offset += file_entry_size(file);
} }
debug!("Writing end of central directory"); debug!("Writing end of central directory");
self.pending_metadata.append(&mut end_of_central_directory(&self.files)); self.pending_metadata
.append(&mut end_of_central_directory(&self.files));
} }
} }
@ -276,8 +301,12 @@ impl<'a> Write for ZipGenerator<'a> {
while !self.pending_metadata.is_empty() { while !self.pending_metadata.is_empty() {
let result = self.output.write(self.pending_metadata.as_slice()); let result = self.output.write(self.pending_metadata.as_slice());
match result { match result {
Ok(0) | Err(_) => { return result; } Ok(0) | Err(_) => {
Ok(n) => { self.pending_metadata.drain(..n); } return result;
}
Ok(n) => {
self.pending_metadata.drain(..n);
}
} }
} }
if self.file_index >= self.files.len() { if self.file_index >= self.files.len() {