This commit is contained in:
parent
f3537a66b2
commit
97a0932e67
@ -4,7 +4,7 @@ sqlalchemy>=2.0.23
|
|||||||
psycopg2-binary>=2.9.9
|
psycopg2-binary>=2.9.9
|
||||||
pydantic[email]>=2.5.0
|
pydantic[email]>=2.5.0
|
||||||
httpx>=0.25.2
|
httpx>=0.25.2
|
||||||
certifi>=2024.0.0
|
certifi>=2023.7.22
|
||||||
python-dotenv>=1.0.0
|
python-dotenv>=1.0.0
|
||||||
python-multipart>=0.0.7
|
python-multipart>=0.0.7
|
||||||
openpyxl>=3.1.2
|
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:
|
async def create_http_client() -> httpx.AsyncClient:
|
||||||
"""
|
"""
|
||||||
Create an httpx client with proper certificate verification.
|
Create an httpx client with proper certificate verification.
|
||||||
Uses certifi for CA bundle and explicit TLS 1.2+ negotiation.
|
Uses certifi CA bundle for Meta's WhatsApp Cloud API.
|
||||||
"""
|
|
||||||
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
|
|
||||||
|
|
||||||
|
Falls back to system certificates if certifi fails.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Try using certifi's CA bundle first (most reliable)
|
||||||
return httpx.AsyncClient(
|
return httpx.AsyncClient(
|
||||||
verify=ssl_context,
|
verify=certifi.where(),
|
||||||
timeout=httpx.Timeout(30.0, connect=10.0),
|
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)
|
limits=httpx.Limits(max_keepalive_connections=5, max_connections=10)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user