|
|
@@ -1,7 +1,8 @@
|
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
|
-import httpclient, asyncdispatch, options, times, strutils, uri
|
|
|
-import packedjson, zippy
|
|
|
+import httpclient, asyncdispatch, options, sequtils, strutils, uri
|
|
|
+import jsony, packedjson, zippy
|
|
|
import types, tokens, consts, parserutils, http_pool
|
|
|
+from experimental/types/common import Errors, ErrorObj
|
|
|
|
|
|
const
|
|
|
rlRemaining = "x-rate-limit-remaining"
|
|
|
@@ -40,7 +41,14 @@ proc genHeaders*(token: Token = nil): HttpHeaders =
|
|
|
"DNT": "1"
|
|
|
})
|
|
|
|
|
|
-proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} =
|
|
|
+template updateToken() =
|
|
|
+ if api != Api.search and resp.headers.hasKey(rlRemaining):
|
|
|
+ let
|
|
|
+ remaining = parseInt(resp.headers[rlRemaining])
|
|
|
+ reset = parseInt(resp.headers[rlReset])
|
|
|
+ token.setRateLimit(api, remaining, reset)
|
|
|
+
|
|
|
+template fetchImpl(result, fetchBody) {.dirty.} =
|
|
|
once:
|
|
|
pool = HttpPool()
|
|
|
|
|
|
@@ -48,37 +56,21 @@ proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} =
|
|
|
if token.tok.len == 0:
|
|
|
raise rateLimitError()
|
|
|
|
|
|
- let headers = genHeaders(token)
|
|
|
try:
|
|
|
var resp: AsyncResponse
|
|
|
- var body = pool.use(headers):
|
|
|
+ result = pool.use(genHeaders(token)):
|
|
|
resp = await c.get($url)
|
|
|
await resp.body
|
|
|
|
|
|
- if body.len > 0:
|
|
|
+ if result.len > 0:
|
|
|
if resp.headers.getOrDefault("content-encoding") == "gzip":
|
|
|
- body = uncompress(body, dfGzip)
|
|
|
+ result = uncompress(result, dfGzip)
|
|
|
else:
|
|
|
- echo "non-gzip body, url: ", url, ", body: ", body
|
|
|
-
|
|
|
- if body.startsWith('{') or body.startsWith('['):
|
|
|
- result = parseJson(body)
|
|
|
- else:
|
|
|
- echo resp.status, ": ", body
|
|
|
- result = newJNull()
|
|
|
+ echo "non-gzip body, url: ", url, ", body: ", result
|
|
|
|
|
|
- if api != Api.search and resp.headers.hasKey(rlRemaining):
|
|
|
- let
|
|
|
- remaining = parseInt(resp.headers[rlRemaining])
|
|
|
- reset = parseInt(resp.headers[rlReset])
|
|
|
- token.setRateLimit(api, remaining, reset)
|
|
|
+ fetchBody
|
|
|
|
|
|
- if result.getError notin {invalidToken, forbidden, badToken}:
|
|
|
- release(token, used=true)
|
|
|
- else:
|
|
|
- echo "fetch error: ", result.getError
|
|
|
- release(token, invalid=true)
|
|
|
- raise rateLimitError()
|
|
|
+ release(token, used=true)
|
|
|
|
|
|
if resp.status == $Http400:
|
|
|
raise newException(InternalError, $url)
|
|
|
@@ -89,3 +81,35 @@ proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} =
|
|
|
if "length" notin e.msg and "descriptor" notin e.msg:
|
|
|
release(token, invalid=true)
|
|
|
raise rateLimitError()
|
|
|
+
|
|
|
+proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} =
|
|
|
+ var body: string
|
|
|
+ fetchImpl body:
|
|
|
+ if body.startsWith('{') or body.startsWith('['):
|
|
|
+ result = parseJson(body)
|
|
|
+ else:
|
|
|
+ echo resp.status, ": ", body
|
|
|
+ result = newJNull()
|
|
|
+
|
|
|
+ updateToken()
|
|
|
+
|
|
|
+ let error = result.getError
|
|
|
+ if error in {invalidToken, forbidden, badToken}:
|
|
|
+ echo "fetch error: ", result.getError
|
|
|
+ release(token, invalid=true)
|
|
|
+ raise rateLimitError()
|
|
|
+
|
|
|
+proc fetchRaw*(url: Uri; api: Api): Future[string] {.async.} =
|
|
|
+ fetchImpl result:
|
|
|
+ if not (result.startsWith('{') or result.startsWith('[')):
|
|
|
+ echo resp.status, ": ", result
|
|
|
+ result.setLen(0)
|
|
|
+
|
|
|
+ updateToken()
|
|
|
+
|
|
|
+ if result.startsWith("{\"errors"):
|
|
|
+ let errors = result.fromJson(Errors).errors
|
|
|
+ if errors.anyIt(it.code in {invalidToken, forbidden, badToken}):
|
|
|
+ echo "fetch error: ", errors
|
|
|
+ release(token, invalid=true)
|
|
|
+ raise rateLimitError()
|