package com.bigcrunch.ads.internal import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import java.util.concurrent.TimeUnit /** * HTTP client for making network requests * * Uses OkHttp for reliable networking with automatic retries and connection pooling. * All methods are suspend functions that run on the IO dispatcher. */ internal class HttpClient { private val client = OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) // Whole-call ceiling: readTimeout resets per read, so a slow-dripping // response body could otherwise hold a call open indefinitely .callTimeout(30, TimeUnit.SECONDS) .build() /** * Perform a GET request * * @param url The URL to fetch * @param headers Optional HTTP headers * @return Result containing response body or error */ suspend fun get( url: String, headers: Map = emptyMap() ): Result { return withContext(Dispatchers.IO) { try { val requestBuilder = Request.Builder().url(url) headers.forEach { (key, value) -> requestBuilder.addHeader(key, value) } val response = client.newCall(requestBuilder.build()).execute() if (response.isSuccessful) { val body = response.body?.string() ?: "" BCLogger.v("HttpClient", "GET success: $url (${body.length} bytes)") Result.success(body) } else { val error = "HTTP ${response.code}: ${response.message}" BCLogger.w("HttpClient", "GET failed: $url - $error") Result.failure(Exception(error)) } } catch (e: Exception) { BCLogger.e("HttpClient", "GET request failed: $url", e) Result.failure(e) } } } /** * Perform a POST request * * @param url The URL to post to * @param body Request body (JSON string) * @param headers Optional HTTP headers * @return Result containing response body or error */ suspend fun post( url: String, body: String, headers: Map = emptyMap() ): Result { return withContext(Dispatchers.IO) { try { // Log the request body for debugging BCLogger.d("HttpClient", "POST to: $url") BCLogger.d("HttpClient", "Request body: $body") val mediaType = "application/json".toMediaType() val requestBody = body.toRequestBody(mediaType) val requestBuilder = Request.Builder() .url(url) .post(requestBody) headers.forEach { (key, value) -> requestBuilder.addHeader(key, value) } val response = client.newCall(requestBuilder.build()).execute() if (response.isSuccessful) { val responseBody = response.body?.string() ?: "" BCLogger.v("HttpClient", "POST success: $url") Result.success(responseBody) } else { // Log response body for 4xx errors to see what the server says val errorBody = response.body?.string() ?: "" val error = "HTTP ${response.code}: ${response.message}" BCLogger.w("HttpClient", "POST failed: $url - $error") if (errorBody.isNotEmpty()) { BCLogger.w("HttpClient", "Error response body: $errorBody") } Result.failure(Exception(error)) } } catch (e: Exception) { BCLogger.e("HttpClient", "POST request failed: $url", e) Result.failure(e) } } } }