From 436a1d4012553a2f33d0e3a5180b3b5ae0378bdd Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Fri, 23 Oct 2015 10:27:31 +0200 Subject: Interruptible requests ^C now works as expected: It interrupts API requests, input prompts and audio streaming. Timeouts have been removed. --- src/ui.c | 117 ++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 64 insertions(+), 53 deletions(-) (limited to 'src/ui.c') diff --git a/src/ui.c b/src/ui.c index 779dbf8..92a91c2 100644 --- a/src/ui.c +++ b/src/ui.c @@ -158,6 +158,18 @@ static size_t httpFetchCb (char *ptr, size_t size, size_t nmemb, return recvSize; } +/* libcurl progress callback. aborts the current request if user pressed ^C + */ +int progressCb (void * const data, double dltotal, double dlnow, + double ultotal, double ulnow) { + const sig_atomic_t lint = *((sig_atomic_t *) data); + if (lint) { + return 1; + } else { + return 0; + } +} + #define setAndCheck(k,v) \ httpret = curl_easy_setopt (http, k, v); \ assert (httpret == CURLE_OK); @@ -165,6 +177,7 @@ static size_t httpFetchCb (char *ptr, size_t size, size_t nmemb, static CURLcode BarPianoHttpRequest (CURL * const http, const BarSettings_t * const settings, PianoRequest_t * const req) { buffer buffer = {NULL, 0}; + sig_atomic_t lint = 0, *prevint; char url[2048]; int ret = snprintf (url, sizeof (url), "%s://%s:%s%s", req->secure ? "https" : "http", @@ -173,6 +186,10 @@ static CURLcode BarPianoHttpRequest (CURL * const http, req->urlPath); assert (ret >= 0 && ret <= (int) sizeof (url)); + /* save the previous interrupt destination */ + prevint = interrupted; + interrupted = &lint; + curl_easy_reset (http); CURLcode httpret; setAndCheck (CURLOPT_URL, url); @@ -180,8 +197,10 @@ static CURLcode BarPianoHttpRequest (CURL * const http, setAndCheck (CURLOPT_POSTFIELDS, req->postData); setAndCheck (CURLOPT_WRITEFUNCTION, httpFetchCb); setAndCheck (CURLOPT_WRITEDATA, &buffer); + setAndCheck (CURLOPT_PROGRESSFUNCTION, progressCb); + setAndCheck (CURLOPT_PROGRESSDATA, &lint); + setAndCheck (CURLOPT_NOPROGRESS, 0); setAndCheck (CURLOPT_POST, 1); - setAndCheck (CURLOPT_TIMEOUT, 30); if (settings->caBundle != NULL) { setAndCheck (CURLOPT_CAINFO, settings->caBundle); } @@ -215,90 +234,82 @@ static CURLcode BarPianoHttpRequest (CURL * const http, req->responseData = buffer.data; + interrupted = prevint; + return httpret; } /* piano wrapper: prepare/execute http request and pass result back to - * libpiano (updates data structures) - * @return 1 on success, 0 otherwise + * libpiano */ -int BarUiPianoCall (BarApp_t * const app, PianoRequestType_t type, - void *data, PianoReturn_t *pRet, CURLcode *wRet) { - PianoRequest_t req; - - memset (&req, 0, sizeof (req)); +bool BarUiPianoCall (BarApp_t * const app, const PianoRequestType_t type, + void * const data, PianoReturn_t * const pRet, CURLcode * const wRet) { + PianoReturn_t pRetLocal = PIANO_RET_OK; + CURLcode wRetLocal = CURLE_OK; + bool ret = false; /* repeat as long as there are http requests to do */ do { - req.data = data; + PianoRequest_t req = { .data = data, .responseData = NULL }; - *pRet = PianoRequest (&app->ph, &req, type); - if (*pRet != PIANO_RET_OK) { - BarUiMsg (&app->settings, MSG_NONE, "Error: %s\n", PianoErrorToStr (*pRet)); - PianoDestroyRequest (&req); - return 0; + pRetLocal = PianoRequest (&app->ph, &req, type); + if (pRetLocal != PIANO_RET_OK) { + BarUiMsg (&app->settings, MSG_NONE, "Error: %s\n", + PianoErrorToStr (pRetLocal)); + goto cleanup; } - *wRet = BarPianoHttpRequest (app->http, &app->settings, &req); - if (*wRet != CURLE_OK) { + wRetLocal = BarPianoHttpRequest (app->http, &app->settings, &req); + if (wRetLocal == CURLE_ABORTED_BY_CALLBACK) { + BarUiMsg (&app->settings, MSG_NONE, "Interrupted.\n"); + goto cleanup; + } else if (wRetLocal != CURLE_OK) { BarUiMsg (&app->settings, MSG_NONE, "Network error: %s\n", - curl_easy_strerror (*wRet)); - if (req.responseData != NULL) { - free (req.responseData); - } - PianoDestroyRequest (&req); - return 0; + curl_easy_strerror (wRetLocal)); + goto cleanup; } - *pRet = PianoResponse (&app->ph, &req); - if (*pRet != PIANO_RET_CONTINUE_REQUEST) { + pRetLocal = PianoResponse (&app->ph, &req); + if (pRetLocal != PIANO_RET_CONTINUE_REQUEST) { /* checking for request type avoids infinite loops */ - if (*pRet == PIANO_RET_P_INVALID_AUTH_TOKEN && + if (pRetLocal == PIANO_RET_P_INVALID_AUTH_TOKEN && type != PIANO_REQUEST_LOGIN) { /* reauthenticate */ - PianoReturn_t authpRet; - CURLcode authwRet; PianoRequestDataLogin_t reqData; reqData.user = app->settings.username; reqData.password = app->settings.password; reqData.step = 0; - BarUiMsg (&app->settings, MSG_NONE, "Reauthentication required... "); - if (!BarUiPianoCall (app, PIANO_REQUEST_LOGIN, &reqData, &authpRet, - &authwRet)) { - *pRet = authpRet; - *wRet = authwRet; - if (req.responseData != NULL) { - free (req.responseData); - } - PianoDestroyRequest (&req); - return 0; + BarUiMsg (&app->settings, MSG_NONE, + "Reauthentication required... "); + if (!BarUiPianoCall (app, PIANO_REQUEST_LOGIN, &reqData, + &pRetLocal, &wRetLocal)) { + goto cleanup; } else { /* try again */ - *pRet = PIANO_RET_CONTINUE_REQUEST; + pRetLocal = PIANO_RET_CONTINUE_REQUEST; BarUiMsg (&app->settings, MSG_INFO, "Trying again... "); } - } else if (*pRet != PIANO_RET_OK) { - BarUiMsg (&app->settings, MSG_NONE, "Error: %s\n", PianoErrorToStr (*pRet)); - if (req.responseData != NULL) { - free (req.responseData); - } - PianoDestroyRequest (&req); - return 0; + } else if (pRetLocal != PIANO_RET_OK) { + BarUiMsg (&app->settings, MSG_NONE, "Error: %s\n", + PianoErrorToStr (pRetLocal)); + goto cleanup; } else { BarUiMsg (&app->settings, MSG_NONE, "Ok.\n"); + ret = true; } } - /* we can destroy the request at this point, even when this call needs - * more than one http request. persistent data (step counter, e.g.) is - * stored in req.data */ - if (req.responseData != NULL) { - free (req.responseData); - } + +cleanup: + /* persistent data is stored in req.data */ + free (req.responseData); PianoDestroyRequest (&req); - } while (*pRet == PIANO_RET_CONTINUE_REQUEST); + } while (pRetLocal == PIANO_RET_CONTINUE_REQUEST); + + *pRet = pRetLocal; + *wRet = wRetLocal; - return 1; + return ret; } /* Station sorting functions */ -- cgit v1.2.3