mirror of
https://github.com/zedeus/nitter
synced 2026-09-05 14:49:32 +00:00
Add retry logic and timeout to media proxy
This commit is contained in:
parent
bfcf75af7f
commit
581c6f1714
1 changed files with 65 additions and 41 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import uri, strutils, httpclient, os, hashes, base64, re
|
import uri, strutils, httpclient, os, hashes, base64, re
|
||||||
import asynchttpserver, asyncstreams, asyncfile, asyncnet
|
import asynchttpserver, asyncstreams, asyncfile, asyncnet
|
||||||
|
import asyncdispatch
|
||||||
|
|
||||||
import jester
|
import jester
|
||||||
|
|
||||||
|
|
@ -32,47 +33,70 @@ template respond*(req: asynchttpserver.Request; headers) =
|
||||||
|
|
||||||
proc proxyMedia*(req: jester.Request; url: string): Future[HttpCode] {.async.} =
|
proc proxyMedia*(req: jester.Request; url: string): Future[HttpCode] {.async.} =
|
||||||
result = Http200
|
result = Http200
|
||||||
let
|
let request = req.getNativeReq()
|
||||||
request = req.getNativeReq()
|
|
||||||
client = newAsyncHttpClient(maxRedirects = 0)
|
|
||||||
|
|
||||||
try:
|
for attempt in 0 .. 2:
|
||||||
let res = await client.get(url)
|
let client = newAsyncHttpClient(maxRedirects = 0)
|
||||||
if res.status != "200 OK":
|
var shouldRetry = false
|
||||||
if res.status != "404 Not Found":
|
try:
|
||||||
echo "[media] Proxying failed, status: $1, url: $2" % [res.status, url]
|
let resFut = client.get(url)
|
||||||
return Http404
|
let completed = await withTimeout(resFut, 5000)
|
||||||
|
if not completed:
|
||||||
let hashed = $hash(url)
|
if attempt < 2:
|
||||||
if request.headers.getOrDefault("If-None-Match") == hashed:
|
echo "[media] Retry $1/2, timeout after 5s, url: $2" % [$(attempt + 1), url]
|
||||||
return Http304
|
shouldRetry = true
|
||||||
|
else:
|
||||||
let contentLength =
|
echo "[media] Proxying timeout after 5s, url: $1" % [url]
|
||||||
if res.headers.hasKey("content-length"):
|
return Http504
|
||||||
res.headers["content-length", 0]
|
|
||||||
else:
|
else:
|
||||||
""
|
let res = resFut.read()
|
||||||
|
if res.status != "200 OK":
|
||||||
|
if res.status == "404 Not Found":
|
||||||
|
return Http404
|
||||||
|
if attempt < 2:
|
||||||
|
echo "[media] Retry $1/2, status: $2, url: $3" % [$(attempt + 1), res.status, url]
|
||||||
|
shouldRetry = true
|
||||||
|
else:
|
||||||
|
echo "[media] Proxying failed, status: $1, url: $2" % [res.status, url]
|
||||||
|
return Http404
|
||||||
|
else:
|
||||||
|
let hashed = $hash(url)
|
||||||
|
if request.headers.getOrDefault("If-None-Match") == hashed:
|
||||||
|
return Http304
|
||||||
|
|
||||||
let headers = newHttpHeaders({
|
let contentLength =
|
||||||
"content-type": res.headers["content-type", 0],
|
if res.headers.hasKey("content-length"):
|
||||||
"content-length": contentLength,
|
res.headers["content-length", 0]
|
||||||
"cache-control": maxAge,
|
else:
|
||||||
"etag": hashed
|
""
|
||||||
})
|
|
||||||
|
|
||||||
respond(request, headers)
|
let headers = newHttpHeaders({
|
||||||
|
"content-type": res.headers["content-type", 0],
|
||||||
|
"content-length": contentLength,
|
||||||
|
"cache-control": maxAge,
|
||||||
|
"etag": hashed
|
||||||
|
})
|
||||||
|
|
||||||
var (hasValue, data) = (true, "")
|
respond(request, headers)
|
||||||
while hasValue:
|
|
||||||
(hasValue, data) = await res.bodyStream.read()
|
var (hasValue, data) = (true, "")
|
||||||
if hasValue:
|
while hasValue:
|
||||||
await request.client.send(data)
|
(hasValue, data) = await res.bodyStream.read()
|
||||||
data.setLen 0
|
if hasValue:
|
||||||
except HttpRequestError, ProtocolError, OSError:
|
await request.client.send(data)
|
||||||
echo "[media] Proxying exception, error: $1, url: $2" % [getCurrentExceptionMsg(), url]
|
data.setLen 0
|
||||||
result = Http404
|
return Http200
|
||||||
finally:
|
except CatchableError:
|
||||||
client.close()
|
if attempt < 2:
|
||||||
|
echo "[media] Retry $1/2, error: $2, url: $3" % [$(attempt + 1), getCurrentExceptionMsg(), url]
|
||||||
|
shouldRetry = true
|
||||||
|
else:
|
||||||
|
echo "[media] Proxying exception, error: $1, url: $2" % [getCurrentExceptionMsg(), url]
|
||||||
|
result = Http404
|
||||||
|
finally:
|
||||||
|
client.close()
|
||||||
|
if not shouldRetry:
|
||||||
|
break
|
||||||
|
|
||||||
template check*(code): untyped =
|
template check*(code): untyped =
|
||||||
if code != Http200:
|
if code != Http200:
|
||||||
|
|
@ -129,7 +153,7 @@ proc createMediaRouter*(cfg: Config) =
|
||||||
if getHmac(url) != request.matches[1]:
|
if getHmac(url) != request.matches[1]:
|
||||||
resp Http403, showError("Failed to verify signature", cfg)
|
resp Http403, showError("Failed to verify signature", cfg)
|
||||||
|
|
||||||
if ".mp4" in url or ".ts" in url or ".m4s" in url:
|
if ".mp4" in url or ".ts" in url or ".m4s" in url or ".aac" in url:
|
||||||
let code = await proxyMedia(request, url)
|
let code = await proxyMedia(request, url)
|
||||||
check code
|
check code
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue