codekingpro/portable-devtools
114k
1"""2World-Level API Resilience Toolkit3====================================4Handles: IP blocks, timeouts, rate limits, SSL errors, connection drops5 6Usage:7 python api_call.py GET https://api.example.com/endpoint8 python api_call.py POST https://api.example.com/data '{"key":"value"}'9 python api_call.py GET https://api.example.com --proxy http://127.0.0.1:808010 11Features:12 - Auto Retry with Exponential Backoff13 - Random User-Agent Rotation14 - Connection Timeout Handling15 - Rate Limit Detection (429) with Auto-Wait16 - SSL Error Bypass Option17 - Proxy Support (for mitmproxy or any proxy)18 - Detailed Error Diagnosis19"""20 21import sys22import time23import random24import json25import urllib.request26import urllib.error27import urllib.parse28import ssl29import os30 31# ─────────────────────────────────────────────32# CONFIGURATION - Edit these as needed33# ─────────────────────────────────────────────34MAX_RETRIES = 535INITIAL_WAIT = 2 # seconds before first retry36TIMEOUT = 30 # request timeout in seconds37BYPASS_SSL = False # Set True if getting SSL errors (self-signed certs)38PROXY = None # e.g. "http://127.0.0.1:8080" for mitmproxy39# ─────────────────────────────────────────────40 41USER_AGENTS = [42 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/124.0 Safari/537.36",43 "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0",44 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Safari/605.1.15",45 "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/122.0 Safari/537.36",46 "python-httpx/0.27.0",47 "axios/1.7.2",48 "curl/8.7.1",49]50 51DIAGNOSABLE_ERRORS = {52 400: "BAD REQUEST - Check your payload/params format",53 401: "UNAUTHORIZED - Check your API key/token",54 403: "FORBIDDEN - IP may be blocked or permissions missing",55 404: "NOT FOUND - Check the URL/endpoint",56 408: "REQUEST TIMEOUT - Server too slow, try increasing TIMEOUT",57 429: "RATE LIMITED - Waiting before retry...",58 500: "SERVER ERROR - Their problem, will retry",59 502: "BAD GATEWAY - Proxy/CDN issue, retrying",60 503: "SERVICE UNAVAILABLE - Server overloaded, retrying",61 504: "GATEWAY TIMEOUT - Network issue, retrying",62}63 64 65def make_request(method, url, data=None, headers=None, proxy=None, bypass_ssl=False, timeout=30):66 """Core request function with all resilience features."""67 if headers is None:68 headers = {}69 70 headers["User-Agent"] = random.choice(USER_AGENTS)71 headers["Accept"] = "application/json, text/plain, */*"72 headers["Accept-Language"] = "en-US,en;q=0.9"73 headers["Connection"] = "keep-alive"74 75 # SSL context76 ctx = ssl.create_default_context()77 if bypass_ssl:78 ctx.check_hostname = False79 ctx.verify_mode = ssl.CERT_NONE80 81 # Proxy handler82 handlers = []83 if proxy:84 proxy_handler = urllib.request.ProxyHandler({"http": proxy, "https": proxy})85 handlers.append(proxy_handler)86 87 https_handler = urllib.request.HTTPSHandler(context=ctx)88 handlers.append(https_handler)89 opener = urllib.request.build_opener(*handlers)90 91 # Build request92 body = None93 if data:94 if isinstance(data, dict):95 body = json.dumps(data).encode("utf-8")96 headers["Content-Type"] = "application/json"97 else:98 body = data.encode("utf-8") if isinstance(data, str) else data99 100 req = urllib.request.Request(url, data=body, headers=headers, method=method.upper())101 102 response = opener.open(req, timeout=timeout)103 raw = response.read()104 status = response.status105 106 try:107 result = json.loads(raw)108 except Exception:109 result = raw.decode("utf-8", errors="replace")110 111 return status, result112 113 114def api_call(method, url, data=None, headers=None):115 """Resilient API call with retry, backoff, and diagnosis."""116 wait = INITIAL_WAIT117 last_error = None118 119 for attempt in range(1, MAX_RETRIES + 1):120 try:121 print(f"\n[Attempt {attempt}/{MAX_RETRIES}] {method.upper()} {url}")122 status, result = make_request(123 method, url, data=data, headers=headers,124 proxy=PROXY, bypass_ssl=BYPASS_SSL, timeout=TIMEOUT125 )126 127 if status == 429:128 retry_after = wait * 3129 print(f" ⚠{DIAGNOSABLE_ERRORS[429]}")130 print(f" Waiting {retry_after}s before next attempt...")131 time.sleep(retry_after)132 wait = min(wait * 2, 120)133 continue134 135 if 200 <= status < 300:136 print(f" ✓ SUCCESS ({status})")137 return status, result138 139 msg = DIAGNOSABLE_ERRORS.get(status, f"HTTP {status} - Unexpected error")140 print(f" ✗ {msg}")141 142 if status in (400, 401, 403, 404):143 print(" → Non-retryable error. Stopping.")144 return status, result145 146 except urllib.error.URLError as e:147 reason = str(e.reason)148 print(f" ✗ CONNECTION ERROR: {reason}")149 150 if "SSL" in reason or "certificate" in reason.lower():151 print(" DIAGNOSIS: SSL/Certificate issue.")152 print(" FIX: Set BYPASS_SSL = True in this script")153 elif "timed out" in reason.lower():154 print(" DIAGNOSIS: Connection timed out.")155 print(" FIX: Increase TIMEOUT or check network")156 elif "Name or service not known" in reason or "getaddrinfo" in reason:157 print(" DIAGNOSIS: DNS resolution failed.")158 print(" FIX: Check internet / try a different DNS (1.1.1.1)")159 else:160 print(" DIAGNOSIS: Network/connectivity issue.")161 print(" FIX: Try mitmproxy or a proxy for routing")162 163 last_error = e164 165 except TimeoutError:166 print(f" ✗ TIMEOUT after {TIMEOUT}s")167 last_error = "Timeout"168 169 except Exception as e:170 print(f" ✗ UNEXPECTED: {e}")171 last_error = e172 173 if attempt < MAX_RETRIES:174 print(f" ↻ Retrying in {wait}s... (backoff)")175 time.sleep(wait)176 wait = min(wait * 2, 60)177 178 print(f"\n✗ All {MAX_RETRIES} attempts failed. Last error: {last_error}")179 return None, None180 181 182def main():183 if len(sys.argv) < 3:184 print("Usage: python api_call.py <METHOD> <URL> [JSON_BODY] [--proxy PROXY_URL]")185 print("Examples:")186 print(" python api_call.py GET https://httpbin.org/get")187 print(" python api_call.py POST https://httpbin.org/post '{\"key\":\"value\"}'")188 print(" python api_call.py GET https://api.github.com/repos/cli/cli --proxy http://127.0.0.1:8080")189 sys.exit(1)190 191 method = sys.argv[1]192 url = sys.argv[2]193 data = None194 global PROXY195 196 args = sys.argv[3:]197 for i, arg in enumerate(args):198 if arg == "--proxy" and i + 1 < len(args):199 PROXY = args[i + 1]200 elif arg.startswith("{") or arg.startswith("["):201 try:202 data = json.loads(arg)203 except Exception:204 data = arg205 206 status, result = api_call(method, url, data=data)207 208 if result is not None:209 print("\n─── RESPONSE ─────────────────────────────")210 if isinstance(result, (dict, list)):211 print(json.dumps(result, indent=2, ensure_ascii=False))212 else:213 print(result)214 print("─────────────────────────────────────────")215 216 217if __name__ == "__main__":218 main()219
220 