gowalk-cicd 1.0.64 → 1.0.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/action/.daemux-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.65
|
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.65
|
|
@@ -13,6 +13,11 @@ import threading
|
|
|
13
13
|
from urllib.parse import unquote, urlsplit
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
# A tunnel carries whole SDK/NDK/CMake downloads and Gradle's keep-alive pools; an
|
|
17
|
+
# idle read is not a failure, so pumps wait long and the tunnel closes cleanly.
|
|
18
|
+
IDLE_TIMEOUT = 900.0
|
|
19
|
+
|
|
20
|
+
|
|
16
21
|
class Relay:
|
|
17
22
|
def __init__(self, proxy: str):
|
|
18
23
|
self.proxy = urlsplit(proxy)
|
|
@@ -56,13 +61,14 @@ class Relay:
|
|
|
56
61
|
return reader, writer
|
|
57
62
|
|
|
58
63
|
async def _pump(self, reader, writer):
|
|
59
|
-
while data := await asyncio.wait_for(reader.read(65536), timeout=
|
|
64
|
+
while data := await asyncio.wait_for(reader.read(65536), timeout=IDLE_TIMEOUT):
|
|
60
65
|
writer.write(data)
|
|
61
66
|
await writer.drain()
|
|
62
67
|
|
|
63
68
|
async def _handle(self, reader, writer):
|
|
64
69
|
upstream = None
|
|
65
70
|
pumps = []
|
|
71
|
+
established = False
|
|
66
72
|
try:
|
|
67
73
|
request = await asyncio.wait_for(reader.readuntil(b"\r\n\r\n"), timeout=15)
|
|
68
74
|
upstream_reader, upstream = await self._upstream(request)
|
|
@@ -72,12 +78,17 @@ class Relay:
|
|
|
72
78
|
raise ConnectionError("upstream tunnel refused")
|
|
73
79
|
writer.write(b"HTTP/1.1 200 Connection established\r\n\r\n")
|
|
74
80
|
await writer.drain()
|
|
81
|
+
established = True
|
|
75
82
|
pumps = [asyncio.create_task(self._pump(reader, upstream)),
|
|
76
83
|
asyncio.create_task(self._pump(upstream_reader, writer))]
|
|
77
84
|
await asyncio.wait(pumps, return_when=asyncio.FIRST_COMPLETED)
|
|
78
85
|
except (OSError, ValueError, IndexError, asyncio.TimeoutError,
|
|
79
86
|
asyncio.IncompleteReadError, asyncio.LimitOverrunError):
|
|
80
|
-
|
|
87
|
+
# Once bytes flow, the stream is the client's protocol (usually TLS): a
|
|
88
|
+
# 502 written into it corrupts the transfer instead of failing it, so an
|
|
89
|
+
# established connection only closes.
|
|
90
|
+
if not established:
|
|
91
|
+
writer.write(b"HTTP/1.1 502 Proxy unavailable\r\nContent-Length: 0\r\nConnection: close\r\n\r\n")
|
|
81
92
|
finally:
|
|
82
93
|
for task in pumps:
|
|
83
94
|
task.cancel()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Real socket and Java proofs: authenticated upstream only, no target fallback."""
|
|
2
2
|
import base64
|
|
3
|
-
from contextlib import closing, contextmanager
|
|
3
|
+
from contextlib import closing, contextmanager, suppress
|
|
4
4
|
import http.client
|
|
5
5
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
6
6
|
import os
|
|
@@ -10,7 +10,9 @@ import socket
|
|
|
10
10
|
import subprocess
|
|
11
11
|
import tempfile
|
|
12
12
|
import threading
|
|
13
|
+
import time
|
|
13
14
|
import unittest
|
|
15
|
+
from unittest import mock
|
|
14
16
|
|
|
15
17
|
import jvm_proxy_relay as relay
|
|
16
18
|
|
|
@@ -117,5 +119,30 @@ class JvmProxyTests(unittest.TestCase):
|
|
|
117
119
|
self.assertIn("-Dorg.gradle.daemon=false", env["GRADLE_OPTS"])
|
|
118
120
|
|
|
119
121
|
|
|
122
|
+
class TunnelLifetimeTests(unittest.TestCase):
|
|
123
|
+
def test_idle_tunnel_outlives_a_short_pause_and_never_receives_a_502(self):
|
|
124
|
+
# A CMake/NDK download reads a remote zip through one keep-alive tunnel with
|
|
125
|
+
# pauses between range requests; the relay must neither time the pause out
|
|
126
|
+
# nor inject an HTTP status into the established byte stream.
|
|
127
|
+
with upstream() as (proxy, _seen), mock.patch.object(relay, "IDLE_TIMEOUT", 0.3):
|
|
128
|
+
with relay.running(proxy) as port:
|
|
129
|
+
with socket.create_connection(("127.0.0.1", port), timeout=5) as client:
|
|
130
|
+
client.sendall(b"CONNECT unresolvable.invalid:443 HTTP/1.1\r\nHost: ignored\r\n\r\n")
|
|
131
|
+
self.assertIn(b"200", client.recv(4096))
|
|
132
|
+
client.sendall(b"abcd")
|
|
133
|
+
self.assertEqual(client.recv(4), b"abcd")
|
|
134
|
+
time.sleep(0.6) # longer than the (patched) idle timeout: the pumps have given up
|
|
135
|
+
client.settimeout(2)
|
|
136
|
+
received = b""
|
|
137
|
+
with suppress(OSError):
|
|
138
|
+
while chunk := client.recv(4096):
|
|
139
|
+
received += chunk
|
|
140
|
+
self.assertNotIn(b"502", received)
|
|
141
|
+
self.assertNotIn(b"HTTP/1.1", received)
|
|
142
|
+
|
|
143
|
+
def test_idle_timeout_is_long_enough_for_sdk_downloads(self):
|
|
144
|
+
self.assertGreaterEqual(relay.IDLE_TIMEOUT, 600)
|
|
145
|
+
|
|
146
|
+
|
|
120
147
|
if __name__ == "__main__":
|
|
121
148
|
unittest.main()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.65
|