Skip to content

Commit 3611fad

Browse files
authored
Add files via upload
1 parent 4566d4a commit 3611fad

2 files changed

Lines changed: 12 additions & 712 deletions

File tree

pywwwgetadv_udpseq_merged_v3.py

Lines changed: 3 additions & 355 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import os
2222
import re
23+
import io
2324
import sys
2425
import time
2526
import socket
@@ -2748,359 +2749,6 @@ def send_from_fileobj(fileobj, host, port=3124, proto="tcp", timeout=None,
27482749
return total
27492750

27502751

2751-
def recv_to_fileobj(fileobj, host="", port=3124, proto="tcp", timeout=None,
2752-
max_bytes=None, chunk_size=65536, backlog=1,
2753-
use_ssl=False, ssl_verify=True, ssl_ca_file=None,
2754-
ssl_certfile=None, ssl_keyfile=None,
2755-
require_auth=False, expected_user=None, expected_pass=None,
2756-
total_timeout=None, expect_scope=None,
2757-
on_progress=None, rate_limit_bps=None,
2758-
enforce_path=True, wait_seconds=None):
2759-
"""
2760-
Receive bytes into fileobj over TCP/UDP.
2761-
2762-
Path enforcement:
2763-
- UDP: expects 'PATH <...>\\n' control frame first (if enforce_path).
2764-
- TCP: reads first line 'PATH <...>\\n' before auth/payload (if enforce_path).
2765-
2766-
UDP control frames understood: PATH, LEN, HASH, DONE (+ AF1 auth blob).
2767-
2768-
wait_seconds (TCP only): overall accept window to wait for a client
2769-
(mirrors the HTTP server behavior). None = previous behavior (single accept
2770-
with 'timeout' as the accept timeout).
2771-
"""
2772-
proto = (proto or "tcp").lower()
2773-
port = int(port)
2774-
total = 0
2775-
2776-
start_ts = time.time()
2777-
def _time_left():
2778-
if total_timeout is None:
2779-
return None
2780-
left = total_timeout - (time.time() - start_ts)
2781-
return 0.0 if left <= 0 else left
2782-
2783-
def _set_effective_timeout(socklike, base_timeout):
2784-
left = _time_left()
2785-
if left == 0.0:
2786-
return False
2787-
eff = base_timeout
2788-
if left is not None:
2789-
eff = left if eff is None else min(eff, left)
2790-
if eff is not None:
2791-
try:
2792-
socklike.settimeout(eff)
2793-
except Exception:
2794-
pass
2795-
return True
2796-
2797-
if proto not in ("tcp", "udp"):
2798-
raise ValueError("proto must be 'tcp' or 'udp'")
2799-
2800-
# ---------------- UDP server ----------------
2801-
if proto == "udp":
2802-
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
2803-
authed_addr = None
2804-
expected_len = None
2805-
expected_sha = None
2806-
path_checked = (not enforce_path)
2807-
2808-
try:
2809-
sock.bind(("", port))
2810-
if timeout is None:
2811-
try: sock.settimeout(10.0)
2812-
except Exception: pass
2813-
2814-
recvd_so_far = 0
2815-
last_cb_ts = monotonic()
2816-
rl_ts = last_cb_ts
2817-
rl_bytes = 0
2818-
2819-
while True:
2820-
if _time_left() == 0.0:
2821-
if expected_len is not None and total < expected_len:
2822-
raise RuntimeError("UDP receive aborted by total_timeout before full payload received")
2823-
break
2824-
if (max_bytes is not None) and (total >= max_bytes):
2825-
break
2826-
2827-
if not _set_effective_timeout(sock, timeout):
2828-
if expected_len is not None and total < expected_len:
2829-
raise RuntimeError("UDP receive timed out before full payload received")
2830-
if expected_len is None and total > 0:
2831-
raise RuntimeError("UDP receive timed out with unknown length; partial data")
2832-
if expected_len is None and total == 0:
2833-
raise RuntimeError("UDP receive: no packets received before timeout (is the sender running?)")
2834-
break
2835-
2836-
try:
2837-
data, addr = sock.recvfrom(chunk_size)
2838-
except socket.timeout:
2839-
if expected_len is not None and total < expected_len:
2840-
raise RuntimeError("UDP receive idle-timeout before full payload received")
2841-
if expected_len is None and total > 0:
2842-
raise RuntimeError("UDP receive idle-timeout with unknown length; partial data")
2843-
if expected_len is None and total == 0:
2844-
raise RuntimeError("UDP receive: no packets received before timeout (is the sender running?)")
2845-
break
2846-
2847-
if not data:
2848-
continue
2849-
2850-
# (0) PATH first (strict)
2851-
if not path_checked and data.startswith(b"PATH "):
2852-
got_path = _unquote_path_from_wire(data[5:].strip())
2853-
if _to_text(got_path) != _to_text(expect_scope or u""):
2854-
raise RuntimeError("UDP path mismatch: got %r expected %r"
2855-
% (got_path, expect_scope))
2856-
path_checked = True
2857-
continue
2858-
if enforce_path and not path_checked:
2859-
if not data.startswith(b"PATH "):
2860-
continue # ignore until PATH arrives
2861-
2862-
# (0b) Control frames
2863-
if data.startswith(b"LEN ") and expected_len is None:
2864-
try:
2865-
parts = data.strip().split()
2866-
n = int(parts[1])
2867-
expected_len = (None if n < 0 else n)
2868-
if len(parts) >= 3:
2869-
expected_sha = parts[2].decode("ascii")
2870-
except Exception:
2871-
expected_len = None; expected_sha = None
2872-
continue
2873-
2874-
if data.startswith(b"HASH "):
2875-
try:
2876-
expected_sha = data.strip().split()[1].decode("ascii")
2877-
except Exception:
2878-
expected_sha = None
2879-
continue
2880-
2881-
if data == b"DONE\n":
2882-
# Treat DONE as end-of-transfer. If we know the expected length,
2883-
# ignore early DONE until we have all bytes (reduces truncation risk).
2884-
if expected_len is None or total_received >= expected_len:
2885-
break
2886-
else:
2887-
continue
2888-
# (1) Auth (if required)
2889-
if authed_addr is None and require_auth:
2890-
ok = False
2891-
v_ok, v_user, v_scope, _r, v_len, v_sha = verify_auth_blob_v1(
2892-
data, expected_user=expected_user, secret=expected_pass,
2893-
max_skew=600, expect_scope=expect_scope
2894-
)
2895-
if v_ok:
2896-
ok = True
2897-
if expected_len is None:
2898-
expected_len = v_len
2899-
if expected_sha is None:
2900-
expected_sha = v_sha
2901-
else:
2902-
user, pw = _parse_auth_blob_legacy(data)
2903-
ok = (user is not None and
2904-
(expected_user is None or user == _to_bytes(expected_user)) and
2905-
(expected_pass is None or pw == _to_bytes(expected_pass)))
2906-
try:
2907-
sock.sendto((_OK if ok else _NO), addr)
2908-
except Exception:
2909-
pass
2910-
if ok:
2911-
authed_addr = addr
2912-
continue
2913-
2914-
if require_auth and addr != authed_addr:
2915-
continue
2916-
2917-
# (2) Payload
2918-
fileobj.write(data)
2919-
try: fileobj.flush()
2920-
except Exception: pass
2921-
total += len(data)
2922-
recvd_so_far += len(data)
2923-
2924-
if rate_limit_bps:
2925-
sleep_s, rl_ts, rl_bytes = _pace_rate(rl_ts, rl_bytes, rate_limit_bps, len(data))
2926-
if sleep_s > 0.0:
2927-
time.sleep(min(sleep_s, 0.25))
2928-
2929-
if on_progress and (monotonic() - last_cb_ts) >= 0.1:
2930-
try: on_progress(recvd_so_far, expected_len)
2931-
except Exception: pass
2932-
last_cb_ts = monotonic()
2933-
2934-
if expected_len is not None and total >= expected_len:
2935-
break
2936-
2937-
# Post-conditions
2938-
if expected_len is not None and total != expected_len:
2939-
raise RuntimeError("UDP receive incomplete: got %d of %s bytes" % (total, expected_len))
2940-
2941-
if expected_sha:
2942-
import hashlib
2943-
try:
2944-
cur = fileobj.tell(); fileobj.seek(0)
2945-
except Exception:
2946-
cur = None
2947-
h = hashlib.sha256(); _HSZ = 1024 * 1024
2948-
while True:
2949-
blk = fileobj.read(_HSZ)
2950-
if not blk: break
2951-
h.update(_to_bytes(blk))
2952-
got = h.hexdigest()
2953-
if cur is not None:
2954-
try: fileobj.seek(cur)
2955-
except Exception: pass
2956-
if got != expected_sha:
2957-
raise RuntimeError("UDP checksum mismatch: got %s expected %s" % (got, expected_sha))
2958-
2959-
finally:
2960-
try: sock.close()
2961-
except Exception: pass
2962-
return total
2963-
2964-
# ---------------- TCP server (one-shot with optional wait window) ----------------
2965-
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2966-
try:
2967-
try: srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
2968-
except Exception: pass
2969-
srv.bind((host or "", port))
2970-
srv.listen(int(backlog) if backlog else 1)
2971-
2972-
bytes_written = 0
2973-
started = time.time()
2974-
2975-
# per-accept wait
2976-
per_accept = float(timeout) if timeout is not None else 1.0
2977-
try: srv.settimeout(per_accept)
2978-
except Exception: pass
2979-
2980-
while True:
2981-
if bytes_written > 0:
2982-
break
2983-
if wait_seconds is not None and (time.time() - started) >= wait_seconds:
2984-
break
2985-
2986-
try:
2987-
conn, _peer = srv.accept()
2988-
except socket.timeout:
2989-
continue
2990-
except Exception:
2991-
break
2992-
2993-
# TLS
2994-
if use_ssl:
2995-
if not _ssl_available():
2996-
try: conn.close()
2997-
except Exception: pass
2998-
break
2999-
if not ssl_certfile:
3000-
try: conn.close()
3001-
except Exception: pass
3002-
raise ValueError("TLS server requires ssl_certfile (and usually ssl_keyfile).")
3003-
conn = _ssl_wrap_socket(conn, server_side=True, server_hostname=None,
3004-
verify=ssl_verify, ca_file=ssl_ca_file,
3005-
certfile=ssl_certfile, keyfile=ssl_keyfile)
3006-
3007-
recvd_so_far = 0
3008-
last_cb_ts = monotonic()
3009-
rl_ts = last_cb_ts
3010-
rl_bytes = 0
3011-
3012-
try:
3013-
# (0) PATH line (if enforced)
3014-
if enforce_path:
3015-
line = _recv_line(conn, maxlen=4096, timeout=timeout)
3016-
if not line or not line.startswith(b"PATH "):
3017-
try: conn.close()
3018-
except Exception: pass
3019-
continue
3020-
got_path = _unquote_path_from_wire(line[5:].strip())
3021-
if _to_text(got_path) != _to_text(expect_scope or u""):
3022-
try: conn.close()
3023-
except Exception: pass
3024-
raise RuntimeError("TCP path mismatch: got %r expected %r"
3025-
% (got_path, expect_scope))
3026-
3027-
# (1) Auth preface
3028-
if require_auth:
3029-
if not _set_effective_timeout(conn, timeout):
3030-
try: conn.close()
3031-
except Exception: pass
3032-
continue
3033-
try:
3034-
preface = conn.recv(2048)
3035-
except socket.timeout:
3036-
try: conn.sendall(_NO)
3037-
except Exception: pass
3038-
try: conn.close()
3039-
except Exception: pass
3040-
continue
3041-
3042-
ok = False
3043-
v_ok, v_user, v_scope, _r, v_len, v_sha = verify_auth_blob_v1(
3044-
preface or b"", expected_user=expected_user, secret=expected_pass,
3045-
max_skew=600, expect_scope=expect_scope
3046-
)
3047-
if v_ok:
3048-
ok = True
3049-
else:
3050-
user, pw = _parse_auth_blob_legacy(preface or b"")
3051-
ok = (user is not None and
3052-
(expected_user is None or user == _to_bytes(expected_user)) and
3053-
(expected_pass is None or pw == _to_bytes(expected_pass)))
3054-
try: conn.sendall(_OK if ok else _NO)
3055-
except Exception: pass
3056-
if not ok:
3057-
try: conn.close()
3058-
except Exception: pass
3059-
continue
3060-
3061-
# (2) Payload loop
3062-
while True:
3063-
if _time_left() == 0.0: break
3064-
if (max_bytes is not None) and (bytes_written >= max_bytes): break
3065-
3066-
if not _set_effective_timeout(conn, timeout):
3067-
break
3068-
try:
3069-
data = conn.recv(chunk_size)
3070-
except socket.timeout:
3071-
break
3072-
if not data:
3073-
break
3074-
3075-
fileobj.write(data)
3076-
try: fileobj.flush()
3077-
except Exception: pass
3078-
total += len(data)
3079-
bytes_written += len(data)
3080-
recvd_so_far += len(data)
3081-
3082-
if rate_limit_bps:
3083-
sleep_s, rl_ts, rl_bytes = _pace_rate(rl_ts, rl_bytes, rate_limit_bps, len(data))
3084-
if sleep_s > 0.0:
3085-
time.sleep(min(sleep_s, 0.25))
3086-
3087-
if on_progress and (monotonic() - last_cb_ts) >= 0.1:
3088-
try: on_progress(recvd_so_far, max_bytes)
3089-
except Exception: pass
3090-
last_cb_ts = monotonic()
3091-
3092-
finally:
3093-
try: conn.shutdown(socket.SHUT_RD)
3094-
except Exception: pass
3095-
try: conn.close()
3096-
except Exception: pass
3097-
3098-
return total
3099-
3100-
finally:
3101-
try: srv.close()
3102-
except Exception: pass
3103-
31042752
def run_tcp_file_server(fileobj, url, on_progress=None):
31052753
"""
31062754
One-shot TCP uploader: wait for a client, authenticate (optional),
@@ -3941,7 +3589,7 @@ def recv_to_fileobj(fileobj, host="", port=0, proto="tcp", timeout=None,
39413589
require_auth=False, expected_user=None, expected_pass=None,
39423590
total_timeout=None, expect_scope=None,
39433591
on_progress=None, rate_limit_bps=None,
3944-
enforce_path=True, wait_seconds=None):
3592+
enforce_path=True, wait_seconds=None, **_kwargs):
39453593
"""
39463594
Receive bytes into fileobj over TCP/UDP.
39473595
@@ -4850,7 +4498,7 @@ def recv_via_url(fileobj, url, recv_to_fileobj_func=recv_to_fileobj):
48504498
require_auth=require_auth,
48514499
expected_user=(o["user"] if require_auth else None),
48524500
expected_pass=(o["pw"] if require_auth else None),
4853-
auth_scope=o["path"],
4501+
expect_scope=o["path"],
48544502
want_sha=o["want_sha"],
48554503
enforce_path=o["enforce_path"],
48564504
expected_path=o["path"],

0 commit comments

Comments
 (0)