44~~~~~~~~~~~~~
55 This module provides a class which abstracts the Nightfall REST API.
66"""
7+ import time
78from datetime import datetime , timedelta
89import hmac
910import hashlib
1011import json
1112import logging
1213import os
14+ from functools import wraps
1315from typing import List , Tuple , Optional
1416
1517import requests
18+ from requests .adapters import HTTPAdapter
19+ from urllib3 import Retry
1620
1721from nightfall .detection_rules import DetectionRule
1822from nightfall .exceptions import NightfallUserError , NightfallSystemError
@@ -43,13 +47,16 @@ def __init__(self, key: Optional[str] = None, signing_secret: Optional[str] = No
4347 raise NightfallUserError ("need an API key either in constructor or in NIGHTFALL_API_KEY environment var" ,
4448 40001 )
4549
46- self ._headers = {
50+ self .signing_secret = signing_secret
51+ self .logger = logging .getLogger (__name__ )
52+ self .session = requests .Session ()
53+ retries = Retry (total = 5 , allowed_methods = Retry .DEFAULT_ALLOWED_METHODS | {"PATCH" , "POST" })
54+ self .session .mount ('https://' , HTTPAdapter (max_retries = retries ))
55+ self .session .headers = {
4756 "Content-Type" : "application/json" ,
4857 "User-Agent" : "nightfall-python-sdk/1.1.1" ,
4958 'Authorization' : f'Bearer { self .key } ' ,
5059 }
51- self .signing_secret = signing_secret
52- self .logger = logging .getLogger (__name__ )
5360
5461 def scan_text (self , texts : List [str ], detection_rules : Optional [List [DetectionRule ]] = None ,
5562 detection_rule_uuids : Optional [List [str ]] = None , context_bytes : Optional [int ] = None ) -> \
@@ -97,11 +104,7 @@ def scan_text(self, texts: List[str], detection_rules: Optional[List[DetectionRu
97104 return findings , parsed_response .get ("redactedPayload" )
98105
99106 def _scan_text_v3 (self , data : dict ):
100- response = requests .post (
101- url = self .TEXT_SCAN_ENDPOINT_V3 ,
102- headers = self ._headers ,
103- data = json .dumps (data )
104- )
107+ response = self .session .post (url = self .TEXT_SCAN_ENDPOINT_V3 , data = json .dumps (data ))
105108
106109 self .logger .debug (f"HTTP Request URL: { response .request .url } " )
107110 self .logger .debug (f"HTTP Request Body: { response .request .body } " )
@@ -160,11 +163,7 @@ def _file_scan_initialize(self, location: str):
160163 data = {
161164 "fileSizeBytes" : os .path .getsize (location )
162165 }
163- response = requests .post (
164- url = self .FILE_SCAN_INITIALIZE_ENDPOINT ,
165- headers = self ._headers ,
166- data = json .dumps (data )
167- )
166+ response = self .session .post (url = self .FILE_SCAN_INITIALIZE_ENDPOINT , data = json .dumps (data ))
168167
169168 return response
170169
@@ -180,7 +179,7 @@ def read_chunks(fp, chunk_size):
180179 ix = ix + 1
181180
182181 def upload_chunk (id , data , headers ):
183- response = requests .patch (
182+ response = self . session .patch (
184183 url = self .FILE_SCAN_UPLOAD_ENDPOINT .format (id ),
185184 data = data ,
186185 headers = headers
@@ -189,18 +188,14 @@ def upload_chunk(id, data, headers):
189188
190189 with open (location , 'rb' ) as fp :
191190 for ix , piece in read_chunks (fp , chunk_size ):
192- headers = self ._headers
193- headers ["X-UPLOAD-OFFSET" ] = str (ix * chunk_size )
191+ headers = {"X-UPLOAD-OFFSET" : str (ix * chunk_size )}
194192 response = upload_chunk (session_id , piece , headers )
195193 _validate_response (response , 204 )
196194
197195 return True
198196
199197 def _file_scan_finalize (self , session_id : str ):
200- response = requests .post (
201- url = self .FILE_SCAN_COMPLETE_ENDPOINT .format (session_id ),
202- headers = self ._headers
203- )
198+ response = self .session .post (url = self .FILE_SCAN_COMPLETE_ENDPOINT .format (session_id ))
204199 return response
205200
206201 def _file_scan_scan (self , session_id : str , detection_rules : Optional [List [DetectionRule ]] = None ,
@@ -215,11 +210,7 @@ def _file_scan_scan(self, session_id: str, detection_rules: Optional[List[Detect
215210 if detection_rules :
216211 data ["policy" ]["detectionRules" ] = [d .as_dict () for d in detection_rules ]
217212
218- response = requests .post (
219- url = self .FILE_SCAN_SCAN_ENDPOINT .format (session_id ),
220- headers = self ._headers ,
221- data = json .dumps (data )
222- )
213+ response = self .session .post (url = self .FILE_SCAN_SCAN_ENDPOINT .format (session_id ), data = json .dumps (data ))
223214 return response
224215
225216 def validate_webhook (self , request_signature : str , request_timestamp : str , request_data : str ) -> bool :
0 commit comments