upload: content-length mandatory

Massimo Melina committed May 29, 2024 at 19:38 UTC 118c737dd2be842187fcb89608f4495f7f5e981f
4 files changed +17 -9
src/middlewares.ts
+1 -1
@@ -47,7 +47,7 @@ export const headRequests: Koa.Middleware = async (ctx, next) => {
47 }
48
49 let proxyDetected: undefined | Koa.Context
50 -export const someSecurity: Koa.Middleware = async (ctx, next) => {
50 +export const someSecurity: Koa.Middleware = (ctx, next) => {
51 ctx.request.ip = normalizeIp(ctx.ip)
52 // don't allow sessions to change ip
53 const ss = ctx.session
src/selfCheck.ts
+1 -1
@@ -9,7 +9,7 @@ import { httpString } from './util-http'
9 let selfChecking = false
10
11 const CHECK_URL = SPECIAL_URI + 'self-check'
12 -export const selfCheckMiddleware: Middleware = async (ctx, next) => {
12 +export const selfCheckMiddleware: Middleware = (ctx, next) => {
13 if (!selfChecking || !ctx.url.startsWith(CHECK_URL))
14 return next()
15 ctx.body = 'HFS'
src/upload.ts
+10 -3
@@ -1,6 +1,7 @@
1 import { getNodeByName, hasPermission, statusCodeForMissingPerm, VfsNode } from './vfs'
2 import Koa from 'koa'
3 -import { HTTP_CONFLICT, HTTP_FOOL, HTTP_PAYLOAD_TOO_LARGE, HTTP_RANGE_NOT_SATISFIABLE, HTTP_SERVER_ERROR } from './const'
3 +import { HTTP_CONFLICT, HTTP_FOOL, HTTP_PAYLOAD_TOO_LARGE, HTTP_RANGE_NOT_SATISFIABLE, HTTP_SERVER_ERROR,
4 + HTTP_BAD_REQUEST } from './const'
5 import { basename, dirname, extname, join } from 'path'
6 import fs from 'fs'
7 import { Callback, dirTraversal, escapeHTML, loadFileAttr, pendingPromise, storeFileAttr, try_ } from './misc'
@@ -52,7 +53,11 @@ export function uploadWriter(base: VfsNode, path: string, ctx: Koa.Context) {
53 const dir = dirname(fullPath)
54 const min = minAvailableMb.get() * (1 << 20)
55 const reqSize = Number(ctx.headers["content-length"])
55 - if (reqSize)
56 + if (isNaN(reqSize)) {
57 + if (min)
58 + return fail(HTTP_BAD_REQUEST, 'content-length mandatory')
59 + }
60 + else
61 try {
62 if (!Object.hasOwn(cache, dir)) {
63 cache[dir] = getDiskSpaceSync(dir)
@@ -191,9 +196,11 @@ export function uploadWriter(base: VfsNode, path: string, ctx: Koa.Context) {
196 delete waitingToBeDeleted[path]
197 }
198
194 - function fail(status?: number) {
199 + function fail(status?: number, msg?: string) {
200 if (status)
201 ctx.status = status
202 + if (msg)
203 + ctx.body = msg
204 notifyClient(ctx, 'upload.status', { [path]: ctx.status }) // allow browsers to detect failure while still sending body
205 }
206 }
tests/test.ts
+5 -4
@@ -1,12 +1,11 @@
1 import { srpClientSequence } from '../src/srp'
2 -import { createReadStream } from 'fs'
2 +import { createReadStream, statSync } from 'fs'
3 import { dirname, join } from 'path'
4 import _ from 'lodash'
5 import { findDefined, randomId, tryJson, wait } from '../src/cross'
6 import { httpStream, stream2string, XRequestOptions } from '../src/util-http'
7 import { ThrottledStream, ThrottleGroup } from '../src/ThrottledStream'
8 import { rm, writeFile } from 'fs/promises'
9 -import { Readable } from 'stream'
9 /*
10 import { PORT, srv } from '../src'
11
@@ -154,10 +153,12 @@ function login(usr: string, pwd=password) {
153 reqApi(cmd, params, (x,res)=> res.statusCode < 400)())
154 }
155
157 -function reqUpload(dest: string, tester: Tester, body?: Readable | string) {
156 +function reqUpload(dest: string, tester: Tester, body?: string) {
157 + const fn = join(__dirname, 'page/gpl.png')
158 return req(dest, tester, {
159 method: 'PUT',
160 - body: body ?? createReadStream(join(__dirname, 'page/gpl.png'))
160 + headers: { 'content-length': body?.length ?? statSync(fn).size },
161 + body: body ?? createReadStream(fn)
162 })
163 }
164