Fix SSL/TLS handshake failure - simplify httpx client creation
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

- Removed manual SSL context creation that was causing handshake failures
- Simplified create_http_client() to let httpx handle SSL negotiation directly
- Disabled HTTP/2 for better compatibility
- Added proper connection limits and timeouts
- Updated all three client instantiations to await the async function
- Improved overall SSL/TLS stability for Meta API connections
This commit is contained in:
dvirlabs 2026-05-13 13:26:51 +03:00
parent 18de92aa3a
commit 9b6f053d86
2 changed files with 108 additions and 28 deletions

View File

@ -0,0 +1,94 @@
#!/usr/bin/env python3
"""
Test script to send WhatsApp message via template
Usage: python test_whatsapp_send.py
"""
import asyncio
import os
import sys
import logging
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Configure logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# Import after logging is configured
from database import SessionLocal
from whatsapp import WhatsAppService
async def test_send_whatsapp():
"""Test sending a WhatsApp message using hina_invitation template"""
db = SessionLocal()
try:
# Initialize WhatsApp service
service = WhatsAppService(db=db)
# Phone number to test (Israeli format)
phone = "0504370045"
# Template parameters for hina_invitation
params = {
"contact_name": "דוד",
"event_date": "17/05",
"event_date_day": "17",
"venue": "אולם הגן",
"location": "ירושלים",
"reception_time": "18:30",
"ceremony_time": "19:00",
"dinner_time": "20:00",
"bride_name": "ורד",
"groom_name": "דביר",
"event_id": "f3122a7d-1d7c-4cc1-955d-1c6b7358bd25"
}
print("\n" + "="*80)
print("TESTING WHATSAPP MESSAGE SEND")
print("="*80)
print(f"Phone: {phone}")
print(f"Template: hina_invitation")
print(f"Parameters: {params}")
print("="*80 + "\n")
# Send the message
logger.info(f"Attempting to send WhatsApp message to {phone}")
result = await service.send_by_template_key(
template_key="wedding_invitation_by_vered",
to_phone=phone,
params=params
)
print("\n" + "="*80)
print("SUCCESS!")
print("="*80)
print(f"Result: {result}")
print("="*80 + "\n")
return result
except Exception as e:
print("\n" + "="*80)
print("ERROR!")
print("="*80)
print(f"Error: {str(e)}")
import traceback
traceback.print_exc()
print("="*80 + "\n")
raise
finally:
db.close()
if __name__ == "__main__":
asyncio.run(test_send_whatsapp())

View File

@ -3,7 +3,6 @@ WhatsApp Cloud API Service
Handles sending WhatsApp messages via Meta's API Handles sending WhatsApp messages via Meta's API
""" """
import os import os
import ssl
import httpx import httpx
import certifi import certifi
import re import re
@ -15,32 +14,19 @@ from datetime import datetime
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def create_http_client() -> httpx.AsyncClient: async def create_http_client() -> httpx.AsyncClient:
""" """
Create an httpx client with robust SSL configuration. Create an httpx client with proper certificate verification.
Tries to use system certificates first, falls back to certifi. Uses certifi for CA bundle and lets httpx handle SSL/TLS negotiation.
""" """
try: # Let httpx use certifi's CA bundle - simpler and more reliable
# Try using system SSL context with certifi certificates return httpx.AsyncClient(
ssl_context = ssl.create_default_context(cafile=certifi.where()) verify=certifi.where(),
# Enable hostname checking but allow some TLS flexibility timeout=httpx.Timeout(30.0, connect=10.0),
ssl_context.check_hostname = True http2=False, # Disable HTTP/2 to avoid compatibility issues
ssl_context.verify_mode = ssl.CERT_REQUIRED limits=httpx.Limits(max_keepalive_connections=5, max_connections=10)
# Use more compatible TLS versions )
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
return httpx.AsyncClient(verify=ssl_context, timeout=30.0)
except Exception as e:
logger.warning(f"[WhatsApp] Failed to create SSL context with certifi: {e}. Trying default SSL.")
try:
# Fallback to default SSL context
ssl_context = ssl.create_default_context()
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
return httpx.AsyncClient(verify=ssl_context, timeout=30.0)
except Exception as e2:
logger.error(f"[WhatsApp] Failed to create SSL context: {e2}. Using basic client.")
# Last resort fallback - httpx will use its own SSL handling
return httpx.AsyncClient(timeout=30.0)
class WhatsAppError(Exception): class WhatsAppError(Exception):
@ -195,7 +181,7 @@ class WhatsAppService:
url = f"{self.base_url}/{self.phone_number_id}/messages" url = f"{self.base_url}/{self.phone_number_id}/messages"
try: try:
async with create_http_client() as client: async with await create_http_client() as client:
response = await client.post( response = await client.post(
url, url,
json=payload, json=payload,
@ -325,7 +311,7 @@ class WhatsAppService:
print("=" * 80 + "\n") print("=" * 80 + "\n")
try: try:
async with create_http_client() as client: async with await create_http_client() as client:
response = await client.post( response = await client.post(
url, url,
json=payload, json=payload,
@ -588,7 +574,7 @@ class WhatsAppService:
url = f"{self.base_url}/{self.phone_number_id}/messages" url = f"{self.base_url}/{self.phone_number_id}/messages"
try: try:
async with create_http_client() as client: async with await create_http_client() as client:
response = await client.post( response = await client.post(
url, url,
json=payload, json=payload,