[ 'Content-Type' => 'application/json', 'Accept' => 'application/json', 'User-Agent' => 'AI Alt Text Builder/' . ( defined( 'AATB_VERSION' ) ? AATB_VERSION : '1.x' ), ], 'body' => wp_json_encode( $payload ), 'timeout' => 25, 'httpversion' => '1.1', 'sslverify' => true, // No hardcoded sslcertificates path – use WP defaults 'redirection' => 2, ]; $args = array_replace_recursive( $base, $extra ); $args = apply_filters( 'aatb_http_args', $args, $url, $payload, $method ); $do = ( strtoupper( $method ) === 'GET' ) ? 'wp_remote_get' : 'wp_remote_post'; $retryCodes = apply_filters( 'aatb_http_retry_codes', [ 429, 500, 502, 503, 504, 520, 521, 522 ] ); $maxRetries = (int) apply_filters( 'aatb_http_retries', 1 ); // one additional attempt $attempt = 0; $res = null; while ( true ) { $res = $do( $url, $args ); if ( ! is_wp_error( $res ) ) { $code = (int) wp_remote_retrieve_response_code( $res ); if ( ! in_array( $code, $retryCodes, true ) ) { break; // success path or non retryable code } if ( $attempt >= $maxRetries ) { break; } usleep( 200000 * ( $attempt + 1 ) ); // 0.2s, then 0.4s $attempt++; continue; } // WP_Error – handle transient network errors with a short backoff retry $msg = implode( '; ', $res->get_error_messages() ); $transientErr = ( stripos( $msg, 'timed out' ) !== false ) || ( stripos( $msg, 'cURL error 28' ) !== false ) || ( stripos( $msg, 'Could not resolve' ) !== false ) || ( stripos( $msg, 'cURL error 6' ) !== false ); if ( $transientErr && $attempt < $maxRetries ) { usleep( 200000 * ( $attempt + 1 ) ); $attempt++; continue; } // For SSL verification errors or any other issues, exit safely without lowering security break; } return apply_filters( 'aatb_http_result', $res, $url, $payload, $method, $args, $attempt ); } }