From 2997f32ad0644f4777ce3d5339cf77eab940968c Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 12 Feb 2024 20:16:09 -0800 Subject: [PATCH] implement sendViaWeb for linux Fixed the crash that was forcing synchronous sends (and so seriously slowing down my test script) by using http instead of https. That's due to limitations in ssl/curl interaction I can look at if necessary. --- xwords4/linux/lindutil.c | 199 ++++++++++++++++----------------------- xwords4/linux/relaycon.c | 1 + 2 files changed, 83 insertions(+), 117 deletions(-) diff --git a/xwords4/linux/lindutil.c b/xwords4/linux/lindutil.c index df005d6e7..2c9b39eea 100644 --- a/xwords4/linux/lindutil.c +++ b/xwords4/linux/lindutil.c @@ -37,20 +37,9 @@ #include "mqttcon.h" -/* Define ACK_IN_BACKGROUND and you'll crash inside curl_easy_perform(). No - idea why, and no time to debug it now. -*/ -// #define ACK_IN_BACKGROUND typedef struct _LinDUtilCtxt { XW_DUtilCtxt super; - -#ifdef ACK_IN_BACKGROUND - pthread_t ackThread; - pthread_mutex_t ackMutex; - pthread_cond_t ackCondVar; - GList* ackList; -#endif } LinDUtilCtxt; static XP_U32 linux_dutil_getCurSeconds( XW_DUtilCtxt* duc, XWEnv xwe ); @@ -202,124 +191,100 @@ linux_dutil_onGameGoneReceived( XW_DUtilCtxt* duc, XWEnv XP_UNUSED(xwe), } } -/* typedef struct _AckData { */ -/* XW_DUtilCtxt* duc; */ -/* XP_U8* msg; */ -/* XP_U16 len; */ -/* gchar* topic; */ -/* XP_U32 gameID; */ -/* } AckData; */ +typedef struct _SendViaData { + LinDUtilCtxt* lduc; + char* pstr; + char* api; +} SendViaData; -/* static void */ -/* sendViaCurl( LinDUtilCtxt* lduc, AckData* adp ) */ -/* { */ -/* LaunchParams* params = (LaunchParams*)lduc->super.closure; */ +typedef struct _FetchData { + char* payload; + size_t size; +} FetchData; -/* CURLcode res = curl_global_init(CURL_GLOBAL_DEFAULT); */ -/* XP_ASSERT(res == CURLE_OK); */ -/* CURL* curl = curl_easy_init(); */ - -/* char url[128]; */ -/* snprintf( url, sizeof(url), "https://%s/xw4/api/v1/ack", */ -/* params->connInfo.mqtt.hostName ); */ -/* curl_easy_setopt( curl, CURLOPT_URL, url ); */ - -/* gchar* sum = g_compute_checksum_for_data( G_CHECKSUM_MD5, adp->msg, adp->len ); */ -/* gchar* json */ -/* = g_strdup_printf("{\"topic\": \"%s\", \"gid\": %u, \"sum\": \"%s\"}", */ -/* adp->topic, adp->gameID, sum ); */ -/* // XP_LOGFF( "json: %s", json ); */ -/* g_free( sum ); */ -/* curl_easy_setopt( curl, CURLOPT_POSTFIELDS, json ); */ -/* curl_easy_setopt( curl, CURLOPT_POSTFIELDSIZE, -1L ); */ - -/* struct curl_slist *headers = NULL; */ -/* headers = curl_slist_append(headers, "Expect:"); */ -/* headers = curl_slist_append(headers, "Content-Type: application/json"); */ -/* curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); */ - -/* res = curl_easy_perform(curl); */ -/* XP_Bool success = res == CURLE_OK; */ -/* /\* XP_LOGFF( "curl_easy_perform() => %d", res ); *\/ */ -/* if ( ! success ) { */ -/* XP_LOGFF( "curl_easy_perform() failed: %s", curl_easy_strerror(res)); */ -/* } */ - -/* curl_slist_free_all( headers ); */ -/* curl_easy_cleanup( curl ); */ -/* curl_global_cleanup(); */ -/* g_free( json ); */ - -/* /\* g_idle_add( nuke_ack_data, ad ); *\/ */ -/* g_free( adp->topic ); */ -/* g_free( adp->msg ); */ -/* g_free( adp ); */ -/* } */ - -#ifdef ACK_IN_BACKGROUND -static void* -sendAckThreadProc( void* arg ) +static size_t +curl_callback( void *contents, size_t size, size_t nmemb, void *userp ) { - LOG_FUNC(); - LinDUtilCtxt* lduc = (LinDUtilCtxt*)arg; + size_t realsize = size * nmemb; + FetchData* fdp = (FetchData*)userp; + XP_LOGFF( "(realsize: %zu)", realsize ); - for ( ; ; ) { - XP_LOGFF( "top of loop" ); - pthread_mutex_lock( &lduc->ackMutex ); - while ( 0 == g_list_length(lduc->ackList) ) { - pthread_cond_wait( &lduc->ackCondVar, &lduc->ackMutex ); - } - GList* head = lduc->ackList; - lduc->ackList = g_list_remove_link( lduc->ackList, lduc->ackList ); - AckData* adp = (AckData*)head->data; - g_list_free( head ); - pthread_mutex_unlock( &lduc->ackMutex ); + fdp->payload = (char *) realloc(fdp->payload, fdp->size + realsize + 1); + memcpy(&(fdp->payload[fdp->size]), contents, realsize); + fdp->size += realsize; + fdp->payload[fdp->size] = 0; + return realsize; +} - sendViaCurl( lduc, adp ); - XP_LOGFF( "bottom of loop" ); +static void* +sendViaThreadProc( void* arg ) +{ + SendViaData* svdp = (SendViaData*)arg; + + const LaunchParams* params = (LaunchParams*)svdp->lduc->super.closure; + + XP_LOGFF( "(api: %s, json: %s)", svdp->api, svdp->pstr ); + + CURLcode res = curl_global_init(CURL_GLOBAL_DEFAULT); /* ok to call multiple times */ + XP_ASSERT(res == CURLE_OK); + + CURL* curl = curl_easy_init(); + + char url[128]; + /* Don't use https. Doing so triggers a race condition/crash in openssl as + called by curl. See https://curl.se/libcurl/c/threaded-ssl.html (which + has a fix to allow using https, but I'm to lazy to work it out now.) */ + const char* proto = "http"; + snprintf( url, sizeof(url), "%s://%s/xw4/api/v1/%s", proto, + params->connInfo.mqtt.hostName, svdp->api ); + curl_easy_setopt( curl, CURLOPT_URL, url ); + + curl_easy_setopt( curl, CURLOPT_POSTFIELDS, svdp->pstr ); + curl_easy_setopt( curl, CURLOPT_POSTFIELDSIZE, -1L ); + + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Expect:"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + + FetchData fd = {0}; + + curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, curl_callback ); + curl_easy_setopt( curl, CURLOPT_WRITEDATA, (void *) &fd ); + + res = curl_easy_perform( curl ); + if ( res != CURLE_OK ) { + XP_LOGFF( "curl_easy_perform() failed: %s", curl_easy_strerror(res)); + } else { + XP_LOGFF( "got buffer: %s", fd.payload ); } - LOG_RETURN_VOID(); + curl_slist_free_all( headers ); + curl_easy_cleanup( curl ); + + free( svdp->pstr ); + free( svdp->api ); + free( svdp ); return NULL; } -#endif - -/* static void */ -/* linux_dutil_ackMQTTMsg( XW_DUtilCtxt* duc, XWEnv XP_UNUSED(xwe), */ -/* const XP_UCHAR* topic, XP_U32 gameID, */ -/* const MQTTDevID* XP_UNUSED(senderID), */ -/* const XP_U8* msg, XP_U16 len ) */ -/* { */ -/* AckData ad = { */ -/* .duc = duc, */ -/* .topic = g_strdup( topic ), */ -/* .gameID = gameID, */ -/* .len = len, */ -/* .msg = g_memdup2( msg, len ), */ -/* }; */ -/* AckData* adp = g_memdup2( &ad, sizeof(ad) ); */ - -/* LinDUtilCtxt* lduc = (LinDUtilCtxt*)duc; */ -/* #ifdef ACK_IN_BACKGROUND */ -/* pthread_mutex_lock( &lduc->ackMutex ); */ -/* lduc->ackList = g_list_append( lduc->ackList, adp ); */ -/* pthread_cond_signal( &lduc->ackCondVar ); */ -/* pthread_mutex_unlock( &lduc->ackMutex ); */ -/* #else */ -/* sendViaCurl( lduc, adp ); */ -/* #endif */ -/* /\* LOG_RETURN_VOID(); *\/ */ -/* } */ static void -linux_dutil_sendViaWeb( XW_DUtilCtxt* duc, XWEnv xwe, const XP_UCHAR* api, - const cJSON* params ) +linux_dutil_sendViaWeb( XW_DUtilCtxt* duc, XWEnv XP_UNUSED(xwe), + const XP_UCHAR* api, const cJSON* json ) { - XP_USE(duc); - XP_USE(xwe); - char* pstr = cJSON_PrintUnformatted( params ); - XP_LOGFF( "(api: %s, params: %s)", api, pstr ); - free( pstr ); + LinDUtilCtxt* lduc = (LinDUtilCtxt*)duc; + + SendViaData svd = { + .lduc = lduc, + .pstr = cJSON_PrintUnformatted( json ), + .api = g_strdup(api), + }; + SendViaData* svdp = malloc( sizeof(*svdp) ); + *svdp = svd; + + pthread_t thrd; + (void)pthread_create( &thrd, NULL, sendViaThreadProc, svdp ); + pthread_detach( thrd ); } XW_DUtilCtxt* diff --git a/xwords4/linux/relaycon.c b/xwords4/linux/relaycon.c index fcd65917e..f4558de89 100644 --- a/xwords4/linux/relaycon.c +++ b/xwords4/linux/relaycon.c @@ -199,6 +199,7 @@ mkJsonParams( CURL* curl, va_list ap ) static XP_Bool runWitCurl( RelayTask* task, const gchar* proc, ...) { + XP_ASSERT(0); /* should no longer be getting called */ CURLcode res = curl_global_init(CURL_GLOBAL_DEFAULT); XP_ASSERT(res == CURLE_OK); CURL* curl = curl_easy_init();