Skip to content

Commit 56be9aa

Browse files
authored
Merge pull request #62 from NfNitLoop/release-v0.5.1
Release v0.5.1
2 parents 8c67a72 + 964f34d commit 56be9aa

4 files changed

Lines changed: 59 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
11
Changelog
22
=========
33

4+
Version 0.5.1
5+
=============
6+
7+
Released: July 24, 2021
8+
<https://github.com/NfNitLoop/feoblog/releases/tag/v0.5.1>
9+
10+
11+
Improvements
12+
------------
13+
14+
* Better handling of attachment uploads.
15+
For cases where it already had a copy of an attachment on the server,
16+
FeoBlog would close the HTTP connection early with a 202 status. This
17+
didn't work well in Chrome or with Deno. Now we'll try to be a bit
18+
more friendly to the client.
19+
20+
As an user, you *may* notice slightly faster uploads of duplicate files.
21+
Though I don't expect that that's a common case.
22+
423
Version 0.5.0
524
=============
625

src/server/attachments.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,16 @@ pub(crate) async fn put_file(
6464
let backend = data.backend_factory.open()?;
6565

6666
let metadata = backend.get_attachment_meta(&user_id, &signature, &file_name)?;
67+
// Drop our pooled connection while we wait for bytes, which could be slow:
68+
drop(backend);
6769

6870
let metadata = match metadata {
6971
Some(d) => d,
7072
None => {
73+
// note: NO drain() here.
74+
// see: https://stackoverflow.com/questions/14250991/is-it-acceptable-for-a-server-to-send-a-http-response-before-the-entire-request
75+
// regarding HTTP errros. This is an error condition anyway.
76+
7177
// If we don't yet have the metadata for a file (provided in its Item), then you can't upload yet.
7278
return Ok(
7379
HttpResponse::Forbidden()
@@ -78,6 +84,7 @@ pub(crate) async fn put_file(
7884
};
7985

8086
if metadata.exists {
87+
drain(body).await;
8188
return Ok(
8289
HttpResponse::Accepted()
8390
.content_type(PLAINTEXT)
@@ -119,8 +126,6 @@ pub(crate) async fn put_file(
119126

120127
// Collect the file bytes into a temp file so that we're not using the backend while we wait for the upload:
121128

122-
// Drop our pooled connection while we wait for bytes, which could be slow:
123-
drop(backend);
124129

125130
let file = tempfile().context("Error opening temp file")?;
126131

@@ -185,6 +190,22 @@ pub(crate) async fn put_file(
185190
);
186191
}
187192

193+
194+
/// If you don't wait to read all the Payload bytes, Actix-Web may close
195+
/// the connection before the client has sent them all. Then the client
196+
/// may behave poorly.
197+
///
198+
/// 1. Chrome has a long pause.
199+
/// 2. Deno fetch() dies on the next request.
200+
/// See: https://github.com/denoland/deno/issues/11513
201+
///
202+
/// SO, here we read the whole payload, even though we don't care what
203+
/// they sent. This seems ripe for DoS but it just seems to be the way
204+
/// browsers and HTTP clients work? 😢
205+
async fn drain(mut payload: Payload) {
206+
while payload.next().await.is_some() {}
207+
}
208+
188209
pub(crate) async fn head_file(
189210
data: Data<AppData>,
190211
Path((user_id, signature, file_name)): Path<(UserID, Signature, String)>,

web-client/components/pages/SyncPage.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,8 @@ async function syncAttachment({userID, signature, fileName, to, fromServers, tra
332332
try {
333333
await tracker.runSubtask(`Uploading to ${to.url}`, async (tracker) => {
334334
let blob = new Blob([buffer!])
335-
let response = await to.putAttachment(userID, signature, fileName, blob)
336-
tracker.log(`Success. ${response.status}: ${response.statusText}`)
335+
await to.putAttachment(userID, signature, fileName, blob)
336+
tracker.log(`Success.`)
337337
})
338338
} catch (_ignored) {
339339
// The subtask will have recorded the error.

web-client/ts/client.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import * as nacl from "./naclWorker/nacl"
44
import { bytesToHex, ConsoleLogger, Logger, prefetch } from "./common";
55
import { tick } from "svelte";
66

7+
// Before sending files larger than this, we should check whether they exist:
8+
const SMALL_FILE_THRESHOLD = 1024 * 128
9+
10+
711
// Encapsulates communication with the server(s).
812
export class Client {
913

@@ -96,7 +100,13 @@ export class Client {
96100
return response
97101
}
98102

99-
async putAttachment(userID: UserID, signature: Signature, fileName: string, blob: Blob): Promise<Response> {
103+
async putAttachment(userID: UserID, signature: Signature, fileName: string, blob: Blob): Promise<void> {
104+
// If the file is already in the content store, we can save some bandwidth/time:
105+
if (blob.size > SMALL_FILE_THRESHOLD) {
106+
const {exists} = await this.headAttachment(userID, signature, fileName)
107+
if (exists) return
108+
}
109+
100110
let url = this.attachmentURL(userID, signature, fileName)
101111
let response: Response
102112
try {
@@ -109,11 +119,11 @@ export class Client {
109119
}
110120

111121
} catch (e) {
112-
console.error("PUT exception:", e)
122+
const {exists} = await this.headAttachment(userID, signature, fileName)
123+
if (exists) return // Someone beat us to the upload, everything's OK.
124+
// else:
113125
throw e
114126
}
115-
116-
return response
117127
}
118128

119129
private attachmentURL(userID: UserID, signature: Signature, fileName: string) {
@@ -339,7 +349,7 @@ export type AttachmentMeta = {
339349

340350
export type GetItemOptions = {
341351
// When syncing items from one server to another, the receiving server MUST
342-
// perform the verificiation, so verifying in the client is redundant and slow.
352+
// perform the verification, so verifying in the client is redundant and slow.
343353
// Set this flag to skip it.
344354
skipSignatureCheck?: boolean
345355
}

0 commit comments

Comments
 (0)