Skip to content

Commit

Permalink
app: improve upload error handling
Browse files Browse the repository at this point in the history
Use a lock to guarantee thread safety when parsing data.  If
parsing results an error, save the error and raise it.

Signed-off-by:  Eric Callahan <[email protected]>
  • Loading branch information
Arksine committed Oct 30, 2023
1 parent a71c5c1 commit 4f32f47
Showing 1 changed file with 20 additions and 4 deletions.
24 changes: 20 additions & 4 deletions moonraker/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import tornado.iostream
import tornado.httputil
import tornado.web
from asyncio import Lock
from inspect import isclass
from tornado.escape import url_unescape, url_escape
from tornado.routing import Rule, PathMatches, AnyMatches
Expand All @@ -29,7 +30,7 @@
WebSocket,
BridgeSocket
)
from streaming_form_data import StreamingFormDataParser
from streaming_form_data import StreamingFormDataParser, ParseFailedException
from streaming_form_data.targets import FileTarget, ValueTarget, SHA256Target

# Annotation imports
Expand Down Expand Up @@ -924,6 +925,8 @@ def initialize(self,
self.file_manager: FileManager = self.server.lookup_component(
'file_manager')
self.max_upload_size = max_upload_size
self.parse_lock = Lock()
self.parse_error: Optional[ParseFailedException] = None

def prepare(self) -> None:
super(FileUploadHandler, self).prepare()
Expand All @@ -949,11 +952,24 @@ def prepare(self) -> None:
self._parser.register(name, target)

async def data_received(self, chunk: bytes) -> None:
if self.request.method == "POST":
evt_loop = self.server.get_event_loop()
await evt_loop.run_in_thread(self._parser.data_received, chunk)
if self.request.method == "POST" and self.parse_error is None:
async with self.parse_lock:
evt_loop = self.server.get_event_loop()
try:
await evt_loop.run_in_thread(self._parser.data_received, chunk)
except ParseFailedException as err:
self.parse_error = err

async def post(self) -> None:
if self.parse_error is not None:
self._file.on_finish()
try:
os.remove(self._file.filename)
except Exception:
pass
raise self.server.error(
"File Upload Parsing Failed", 500
) from self.parse_error
form_args = {}
chk_target = self._targets.pop('checksum')
calc_chksum = self._sha256_target.value.lower()
Expand Down

0 comments on commit 4f32f47

Please sign in to comment.