This commit is contained in:
parent
f3537a66b2
commit
97a0932e67
@ -4,7 +4,7 @@ sqlalchemy>=2.0.23
|
||||
psycopg2-binary>=2.9.9
|
||||
pydantic[email]>=2.5.0
|
||||
httpx>=0.25.2
|
||||
certifi>=2024.0.0
|
||||
certifi>=2023.7.22
|
||||
python-dotenv>=1.0.0
|
||||
python-multipart>=0.0.7
|
||||
openpyxl>=3.1.2
|
||||
|
||||
185
backend/test_ssl_connection.py
Normal file
185
backend/test_ssl_connection.py
Normal file
@ -0,0 +1,185 @@
|
||||
"""
|
||||
SSL/TLS Connection Test for Meta WhatsApp API
|
||||
==============================================
|
||||
|
||||
This script tests SSL/TLS connectivity to Meta's API to diagnose
|
||||
handshake failures.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import ssl
|
||||
import certifi
|
||||
import httpx
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
|
||||
|
||||
async def test_ssl_methods():
|
||||
"""Test different SSL configuration methods"""
|
||||
|
||||
test_url = "https://graph.facebook.com/v20.0/me"
|
||||
token = os.getenv("WHATSAPP_ACCESS_TOKEN", "")
|
||||
|
||||
if not token:
|
||||
print("❌ WHATSAPP_ACCESS_TOKEN not set in .env")
|
||||
return
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
print("="*80)
|
||||
print("Testing SSL/TLS Connection to Meta's API")
|
||||
print("="*80)
|
||||
print(f"\nTest URL: {test_url}")
|
||||
print(f"Token: ***{token[-8:]}")
|
||||
print()
|
||||
|
||||
# Method 1: Using certifi
|
||||
print("Method 1: Using certifi CA bundle")
|
||||
print("-" * 80)
|
||||
try:
|
||||
async with httpx.AsyncClient(
|
||||
verify=certifi.where(),
|
||||
timeout=10.0
|
||||
) as client:
|
||||
response = await client.get(test_url, headers=headers)
|
||||
print(f"✅ SUCCESS! Status: {response.status_code}")
|
||||
print(f" Response: {response.text[:200]}")
|
||||
except Exception as e:
|
||||
print(f"❌ FAILED: {e}")
|
||||
print()
|
||||
|
||||
# Method 2: Using system certificates
|
||||
print("Method 2: Using system certificates (verify=True)")
|
||||
print("-" * 80)
|
||||
try:
|
||||
async with httpx.AsyncClient(
|
||||
verify=True,
|
||||
timeout=10.0
|
||||
) as client:
|
||||
response = await client.get(test_url, headers=headers)
|
||||
print(f"✅ SUCCESS! Status: {response.status_code}")
|
||||
print(f" Response: {response.text[:200]}")
|
||||
except Exception as e:
|
||||
print(f"❌ FAILED: {e}")
|
||||
print()
|
||||
|
||||
# Method 3: Custom SSL context with TLS 1.2+
|
||||
print("Method 3: Custom SSL context (TLS 1.2+)")
|
||||
print("-" * 80)
|
||||
try:
|
||||
ssl_context = ssl.create_default_context(cafile=certifi.where())
|
||||
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
|
||||
ssl_context.check_hostname = True
|
||||
|
||||
async with httpx.AsyncClient(
|
||||
verify=ssl_context,
|
||||
timeout=10.0
|
||||
) as client:
|
||||
response = await client.get(test_url, headers=headers)
|
||||
print(f"✅ SUCCESS! Status: {response.status_code}")
|
||||
print(f" Response: {response.text[:200]}")
|
||||
except Exception as e:
|
||||
print(f"❌ FAILED: {e}")
|
||||
print()
|
||||
|
||||
# Method 4: Custom SSL context with relaxed settings
|
||||
print("Method 4: Custom SSL context (relaxed cipher suite)")
|
||||
print("-" * 80)
|
||||
try:
|
||||
ssl_context = ssl.create_default_context(cafile=certifi.where())
|
||||
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
|
||||
ssl_context.check_hostname = True
|
||||
ssl_context.set_ciphers('DEFAULT:@SECLEVEL=1')
|
||||
|
||||
async with httpx.AsyncClient(
|
||||
verify=ssl_context,
|
||||
timeout=10.0
|
||||
) as client:
|
||||
response = await client.get(test_url, headers=headers)
|
||||
print(f"✅ SUCCESS! Status: {response.status_code}")
|
||||
print(f" Response: {response.text[:200]}")
|
||||
except Exception as e:
|
||||
print(f"❌ FAILED: {e}")
|
||||
print()
|
||||
|
||||
# Method 5: No verification (INSECURE - for testing only)
|
||||
print("Method 5: No SSL verification (INSECURE - testing only)")
|
||||
print("-" * 80)
|
||||
try:
|
||||
async with httpx.AsyncClient(
|
||||
verify=False,
|
||||
timeout=10.0
|
||||
) as client:
|
||||
response = await client.get(test_url, headers=headers)
|
||||
print(f"✅ SUCCESS! Status: {response.status_code}")
|
||||
print(f" Response: {response.text[:200]}")
|
||||
print(" ⚠️ WARNING: This method is INSECURE. Do not use in production!")
|
||||
except Exception as e:
|
||||
print(f"❌ FAILED: {e}")
|
||||
print()
|
||||
|
||||
# System info
|
||||
print("="*80)
|
||||
print("System Information")
|
||||
print("="*80)
|
||||
print(f"Python SSL version: {ssl.OPENSSL_VERSION}")
|
||||
print(f"Certifi CA bundle location: {certifi.where()}")
|
||||
print(f"httpx version: {httpx.__version__}")
|
||||
|
||||
# Check if CA bundle exists
|
||||
import os.path
|
||||
ca_bundle = certifi.where()
|
||||
if os.path.exists(ca_bundle):
|
||||
file_size = os.path.getsize(ca_bundle)
|
||||
print(f"✅ CA bundle exists ({file_size:,} bytes)")
|
||||
else:
|
||||
print(f"❌ CA bundle not found at {ca_bundle}")
|
||||
|
||||
|
||||
async def main():
|
||||
print("\n" + "="*80)
|
||||
print("WhatsApp Cloud API - SSL/TLS Connectivity Test")
|
||||
print("="*80)
|
||||
print("""
|
||||
This script tests different SSL/TLS configuration methods to identify
|
||||
which one works with Meta's API on your system.
|
||||
|
||||
It will try:
|
||||
1. Certifi CA bundle
|
||||
2. System certificates
|
||||
3. Custom SSL context with TLS 1.2+
|
||||
4. Relaxed cipher suites
|
||||
5. No verification (insecure, for comparison)
|
||||
""")
|
||||
|
||||
await test_ssl_methods()
|
||||
|
||||
print("\n" + "="*80)
|
||||
print("Recommendation")
|
||||
print("="*80)
|
||||
print("""
|
||||
Look at the results above and use the first method that succeeded.
|
||||
|
||||
If only Method 5 (no verification) worked:
|
||||
- Your system certificates may be outdated
|
||||
- Update certifi: pip install --upgrade certifi
|
||||
- Update OpenSSL on your system
|
||||
|
||||
If Methods 1-4 all failed:
|
||||
- Check your firewall/proxy settings
|
||||
- Ensure you can reach graph.facebook.com
|
||||
- Verify WHATSAPP_ACCESS_TOKEN is correct
|
||||
""")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@ -17,18 +17,25 @@ logger = logging.getLogger(__name__)
|
||||
async def create_http_client() -> httpx.AsyncClient:
|
||||
"""
|
||||
Create an httpx client with proper certificate verification.
|
||||
Uses certifi for CA bundle and explicit TLS 1.2+ negotiation.
|
||||
"""
|
||||
import ssl
|
||||
# Create a default SSL context that prefers TLS 1.2 and higher
|
||||
ssl_context = ssl.create_default_context(cafile=certifi.where())
|
||||
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
|
||||
ssl_context.check_hostname = True
|
||||
Uses certifi CA bundle for Meta's WhatsApp Cloud API.
|
||||
|
||||
Falls back to system certificates if certifi fails.
|
||||
"""
|
||||
try:
|
||||
# Try using certifi's CA bundle first (most reliable)
|
||||
return httpx.AsyncClient(
|
||||
verify=ssl_context,
|
||||
verify=certifi.where(),
|
||||
timeout=httpx.Timeout(30.0, connect=10.0),
|
||||
http2=False, # Disable HTTP/2 to avoid compatibility issues
|
||||
http2=False, # Disable HTTP/2 for better compatibility
|
||||
limits=httpx.Limits(max_keepalive_connections=5, max_connections=10)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"[WhatsApp] Certifi SSL setup failed, using system certs: {e}")
|
||||
# Fallback: use system default certificates
|
||||
return httpx.AsyncClient(
|
||||
verify=True, # Use system certificates
|
||||
timeout=httpx.Timeout(30.0, connect=10.0),
|
||||
http2=False,
|
||||
limits=httpx.Limits(max_keepalive_connections=5, max_connections=10)
|
||||
)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user