From 024303592a4e8c0e9a0b0ab40948571529da7471 Mon Sep 17 00:00:00 2001 From: ed Date: Wed, 10 Jan 2024 23:59:43 +0000 Subject: [PATCH] improved logging when a client dies mid-POST; igloo irc has an absolute time limit of 2 minutes before it just disconnects mid-upload and that kinda looked like it had a buggy multipart generator instead of just being funny anticipating similar events in the future, also log the client-selected boundary value to eyeball its yoloness --- copyparty/httpcli.py | 9 +++++++++ copyparty/util.py | 24 +++++++++++++++++------- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 8c27a556..bba6ff96 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -2349,6 +2349,7 @@ class HttpCli(object): files: list[tuple[int, str, str, str, str, str]] = [] # sz, sha_hex, sha_b64, p_file, fname, abspath errmsg = "" + tabspath = "" dip = self.dip() t0 = time.time() try: @@ -2446,6 +2447,8 @@ class HttpCli(object): if not nullwrite: atomic_move(tabspath, abspath) + tabspath = "" + files.append( (sz, sha_hex, sha_b64, p_file or "(discarded)", fname, abspath) ) @@ -2491,6 +2494,12 @@ class HttpCli(object): errmsg = vol_san( list(self.asrv.vfs.all_vols.values()), unicode(ex).encode("utf-8") ).decode("utf-8") + try: + got = bos.path.getsize(tabspath) + t = "connection lost after receiving %s of the file" + self.log(t % (humansize(got),), 3) + except: + pass td = max(0.1, time.time() - t0) sz_total = sum(x[0] for x in files) diff --git a/copyparty/util.py b/copyparty/util.py index b7c93568..a9697ef3 100644 --- a/copyparty/util.py +++ b/copyparty/util.py @@ -623,9 +623,14 @@ class _Unrecv(object): while nbytes > len(ret): ret += self.recv(nbytes - len(ret)) except OSError: - t = "client only sent {} of {} expected bytes".format(len(ret), nbytes) - if len(ret) <= 16: - t += "; got {!r}".format(ret) + t = "client stopped sending data; expected at least %d more bytes" + if not ret: + t = t % (nbytes,) + else: + t += ", only got %d" + t = t % (nbytes, len(ret)) + if len(ret) <= 16: + t += "; %r" % (ret,) if raise_on_trunc: raise UnrecvEOF(5, t) @@ -1517,15 +1522,20 @@ class MultipartParser(object): return ret def parse(self) -> None: + boundary = get_boundary(self.headers) + self.log("boundary=%r" % (boundary,)) + # spec says there might be junk before the first boundary, # can't have the leading \r\n if that's not the case - self.boundary = b"--" + get_boundary(self.headers).encode("utf-8") + self.boundary = b"--" + boundary.encode("utf-8") # discard junk before the first boundary for junk in self._read_data(): - self.log( - "discarding preamble: [{}]".format(junk.decode("utf-8", "replace")) - ) + if not junk: + continue + + jtxt = junk.decode("utf-8", "replace") + self.log("discarding preamble |%d| %r" % (len(junk), jtxt)) # nice, now make it fast self.boundary = b"\r\n" + self.boundary