Skip to content

Commit

Permalink
fix: call destroyContext after making request (#81)
Browse files Browse the repository at this point in the history
otherwise SslContext is never cleared hence results in memory leak
after each request
  • Loading branch information
miki725 authored Nov 8, 2024
1 parent e4f0aec commit e97bbd8
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 60 deletions.
33 changes: 17 additions & 16 deletions nimutils/awsclient.nim
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ type
payload: string

AwsClient* {.inheritable.} = object
httpClient*: HttpClient
userAgent*: string
timeout*: int
credentials*: AwsCredentials
scope*: AwsScope
endpoint*: Uri
Expand Down Expand Up @@ -94,15 +95,15 @@ proc getAmzDateString*(): string =

proc newAwsClient*(creds: AwsCredentials, region,
service: string): AwsClient =
let
# TODO - use some kind of template and compile-time variable to put the correct kernel used to build the sdk in the UA?
httpclient = createHttpClient(
userAgent = "nimaws-sdk/0.3.3; " & defUserAgent.replace(" ", "-").toLower() & "; darwin/16.7.0",
)
scope = AwsScope(date: getAmzDateString(), region: region, service: service)

return AwsClient(httpClient: httpclient, credentials: creds, scope: scope,
key: "", key_expires: getTime())
let scope = AwsScope(date: getAmzDateString(), region: region, service: service)

return AwsClient(
userAgent: "nimaws-sdk/0.3.3; " & defUserAgent.replace(" ", "-").toLower() & "; darwin/16.7.0",
credentials: creds,
scope: scope,
key: "",
key_expires: getTime(),
)

proc request*(client: var AwsClient, params: Table, headers: HttpHeaders = newHttpHeaders()): Response =
var
Expand Down Expand Up @@ -148,23 +149,23 @@ proc request*(client: var AwsClient, params: Table, headers: HttpHeaders = newHt
# Add signing key caching so we can skip a step
# utilizing some operator overloading on the create_aws_authorization proc.
# if passed a key and not headers, just return the authorization string; otherwise, create the key and add to the headers
client.httpClient.headers.clear()
if client.key_expires <= getTime():
client.scope.date = getAmzDateString()
client.key = create_aws_authorization(client.credentials, req,
client.httpClient.headers.table, client.scope)
client.key = create_aws_authorization(client.credentials, req, headers.table, client.scope)
client.key_expires = getTime() + initTimeInterval(minutes = 5)
else:
let auth = create_aws_authorization(client.credentials.id, client.key, req,
client.httpClient.headers.table, client.scope)
client.httpClient.headers.add("Authorization", auth)
headers.table, client.scope)
headers.add("Authorization", auth)

return client.httpClient.safeRequest(
return safeRequest(
url,
action,
payload,
headers=headers,
retries=2,
connectRetries=2,
only2xx=true,
userAgent=client.userAgent,
timeout=client.timeout,
)
47 changes: 26 additions & 21 deletions nimutils/net.nim
Original file line number Diff line number Diff line change
Expand Up @@ -152,18 +152,18 @@ proc check*(response: Response,
raise newException(ValueError, $url & " failed with " & response.status & " " & response.body())
return response

proc safeRequest*(client: HttpClient,
url: Uri | string,
httpMethod: HttpMethod | string = HttpGet,
body = "",
headers: HttpHeaders = nil,
multipart: MultipartData = nil,
retries: int = 0,
connectRetries: int = 0,
firstRetryDelayMs: int = 0,
only2xx: bool = false,
raiseWhenAbove: int = 0,
): Response =
proc safeRequest(client: HttpClient,
url: Uri | string,
httpMethod: HttpMethod | string = HttpGet,
body = "",
headers: HttpHeaders = nil,
multipart: MultipartData = nil,
retries: int = 0,
connectRetries: int = 0,
firstRetryDelayMs: int = 0,
only2xx: bool = false,
raiseWhenAbove: int = 0,
): Response =
withRetry(connectRetries, firstRetryDelayMs):
timeoutGuard(client, url)
withRetry(retries, firstRetryDelayMs):
Expand Down Expand Up @@ -191,14 +191,14 @@ proc getSSLContext(caFile: string = "", verifyMode = CVerifyPeer): SslContext =
except:
return newContext(verifyMode = verifyMode, caFile = getCAStorePath())

proc createHttpClient*(uri: Uri = parseUri(""),
proc createHttpContext(uri: Uri = parseUri(""),
maxRedirects: int = 3,
timeout: int = 1000, # in ms - 1 second
pinnedCert: string = "",
verifyMode = CVerifyPeer,
disallowHttp: bool = false,
userAgent: string = defUserAgent,
): HttpClient =
): (SslContext, HttpClient) =
if uri.scheme == "http":
if disallowHttp:
raise newException(ValueError, "http:// URLs not allowed (only https).")
Expand All @@ -219,7 +219,7 @@ proc createHttpClient*(uri: Uri = parseUri(""),
if client == nil:
raise newException(ValueError, "Invalid HTTP configuration")

return client
return (context, client)

proc safeRequest*(url: Uri | string,
httpMethod: HttpMethod | string = HttpGet,
Expand All @@ -236,17 +236,21 @@ proc safeRequest*(url: Uri | string,
disallowHttp: bool = false,
only2xx: bool = false,
raiseWhenAbove: int = 0,
userAgent: string = defUserAgent,
): Response =
let uri = when url is string:
parseUri(url)
else:
url
let client = createHttpClient(uri = uri,
maxRedirects = maxRedirects,
timeout = timeout,
pinnedCert = pinnedCert,
verifyMode = verifyMode,
disallowHttp = disallowHttp)
let (context, client) = createHttpContext(
uri = uri,
maxRedirects = maxRedirects,
timeout = timeout,
pinnedCert = pinnedCert,
verifyMode = verifyMode,
disallowHttp = disallowHttp,
userAgent = userAgent,
)
try:
return client.safeRequest(url = uri,
httpMethod = httpMethod,
Expand All @@ -259,4 +263,5 @@ proc safeRequest*(url: Uri | string,
only2xx = only2xx,
raiseWhenAbove = raiseWhenAbove)
finally:
context.destroyContext()
client.close()
27 changes: 14 additions & 13 deletions nimutils/s3client.nim
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,7 @@ type

proc newS3Client*(creds: AwsCredentials, region: string = defaultRegion,
host: string = awsURI, timeoutMilliseconds = 1000): S3Client =
let
# TODO - use some kind of template and compile-time variable to put the correct kernel used to build the sdk in the UA?
httpclient = newHttpClient(
"nimaws-sdk/0.3.3; " & defUserAgent.replace(" ", "-").toLower() & "; darwin/16.7.0",
timeout = timeoutMilliseconds,
)
scope = AwsScope(date: getAmzDateString(), region: region, service: "s3")
let scope = AwsScope(date: getAmzDateString(), region: region, service: "s3")

var
endpoint: Uri
Expand All @@ -48,9 +42,16 @@ proc newS3Client*(creds: AwsCredentials, region: string = defaultRegion,
endpoint = parseUri(mhost)


return S3Client(httpClient: httpclient, credentials: creds, scope: scope,
endpoint: endpoint, isAWS: endpoint.hostname == "amazonaws.com", key: "",
key_expires: getTime())
return S3Client(
userAgent: "nimaws-sdk/0.3.3; " & defUserAgent.replace(" ", "-").toLower() & "; darwin/16.7.0",
timeout: timeoutMilliseconds,
credentials: creds,
scope: scope,
endpoint: endpoint,
isAWS: endpoint.hostname == "amazonaws.com",
key: "",
key_expires: getTime(),
)

proc get_object*(self: var S3Client, bucket, key: string): Response =
var
Expand All @@ -68,7 +69,7 @@ proc get_object*(self: var S3Client, bucket, key: string): Response =
## path has to be absoloute path in the form /path/to/file
## payload is binary string
proc put_object*(self: var S3Client, bucket, path: string,
payload: string): Response {.gcsafe.} =
payload: string): Response =
let params = {
"action": "PUT",
"bucket": bucket,
Expand All @@ -79,7 +80,7 @@ proc put_object*(self: var S3Client, bucket, path: string,
return self.request(params)

proc list_objects*(self: var S3Client, bucket: string): seq[
Bobject] {.gcsafe.} =
Bobject] =
let
params = {
"bucket": bucket
Expand All @@ -91,7 +92,7 @@ proc list_objects*(self: var S3Client, bucket: string): seq[
result.add(Bobject(key: c[0].innerText, modified: c[1].innerText, etag: c[
2].innerText, size: parseInt(c[3].innerText)))

proc list_buckets*(self: var S3Client): seq[Bucket] {.gcsafe.} =
proc list_buckets*(self: var S3Client): seq[Bucket] =
let
params = {
"action": "GET"
Expand Down
21 changes: 11 additions & 10 deletions nimutils/stsclient.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,7 @@ proc newStsClient*(creds: AwsCredentials,
region: string = defaultRegion,
host: string = awsURI,
timeoutMilliseconds = 1000): StsClient =
let
# TODO - use some kind of template and compile-time variable to put the correct kernel used to build the sdk in the UA?
httpclient = newHttpClient(
"nimaws-sdk/0.3.3; " & defUserAgent.replace(" ", "-").toLower() & "; darwin/16.7.0",
timeout = timeoutMilliseconds,
)
scope = AwsScope(date: getAmzDateString(), region: region, service: "sts")
let scope = AwsScope(date: getAmzDateString(), region: region, service: "sts")

var
endpoint: Uri
Expand All @@ -32,9 +26,16 @@ proc newStsClient*(creds: AwsCredentials,
mhost = awsURI
endpoint = parseUri(mhost)

return StsClient(httpClient: httpclient, credentials: creds, scope: scope,
endpoint: endpoint, isAWS: endpoint.hostname == "amazonaws.com",
key: "", key_expires: getTime())
return StsClient(
userAgent: "nimaws-sdk/0.3.3; " & defUserAgent.replace(" ", "-").toLower() & "; darwin/16.7.0",
timeout: timeoutMilliseconds,
credentials: creds,
scope: scope,
endpoint: endpoint,
isAWS: endpoint.hostname == "amazonaws.com",
key: "",
key_expires: getTime(),
)

proc getCallerIdentity*(self: var StsClient): StsCallerIdentity =
let params = {
Expand Down

0 comments on commit e97bbd8

Please sign in to comment.