curl-library
multi Error Code 23 (Failed writing body) corner case
From: Dan Donahue <ddonahuex_at_gmail.com>
Date: Thu, 02 Oct 2014 14:42:58 -0400
Date: Thu, 02 Oct 2014 14:42:58 -0400
Hi,
This is my first post to the libcurl mailing list. My hope is this mail
is "etiquettely" correct according to your mailing list rules, which I
read in their entirety.
Please review the info below and provide feedback that will help me
isolate the issue to either my application or libcurl.
Info
---- Embedded PowerPC CPU libcurl 7.29.0 linux kernel 3.0.34 libssh2 1.4.3 openssl 1.0.1e All multi sessions contained herein use sftp employing username and password for authentication. Issue ----- A second failed upload attempt ("Login denied (67)") during a download in process causes said download to fail with: "Failed writing received data to disk/application (23)". This only occurs if an upload was attempted and failed prior to initiating the download. Note, the 67 error is expected because the wrong username is used. Download progress reports received before the second upload attempt indicate download is proceeding as expected. The 23 error occurs immediately after the second 67 error. The 23 error occurs because the fwrite in the write function did not write out all the data given (e.g. 209 != 2000). ferror for the failed fwrite reports "Bad file descriptor". This occurs whether I use the libcurl default write function or a registered callback. Subsequent download attempts complete successfully. Debug Notes ----------- "client" refers to an easy session associated w/a multi intfc * Issue only seen for error upload 1->download start->error upload 2 * Issue not seen for upload error 1->upload error 2->download start * Issue not seen for good upload 1->download start->good upload 2 * Issue not seen for download start->upload error 1 * Issue seen when upload/download use same or different servers * Issue seen when upload/download use same or different credentials * Issue seen whether or not client cleanup is done during active download or pended until all sessions complete. * Verified correct easy client/file streams associations (e.g. upload not closing download file stream) * Verified no errant/duplicate fcloses * The file being written during download persists after failure * Output from CURLOPT_VERBOSE for both download/upload is below CURLOPT_VERBOSE for download: * About to connect() to 10.40.10.211 port 22 (#1) * Trying 10.40.10.211... * Connected to 10.40.10.211 (10.40.10.211) port 22 (#1) * SSH MD5 fingerprint: <removed> * SSH authentication methods available: publickey,gssapi-keyex,gssapi-with-mic,password * Using ssh public key file /home/root/.ssh/id_dsa.pub * Using ssh private key file /home/root/.ssh/id_dsa * SSH public key authentication failed: Unable to open public key file * Initialized password authentication * Authentication complete * Failed writing body (288 != 2000) * Closing connection 1 CURLOPT_VERBOSE for failed upload: * About to connect() to 10.40.10.211 port 22 (#0) * Trying 10.40.10.211... * Connected to 10.40.10.211 (10.40.10.211) port 22 (#0) * SSH MD5 fingerprint: <removed> * SSH authentication methods available: publickey,gssapi-keyex,gssapi-with-mic,password * Using ssh public key file /home/root/.ssh/id_dsa.pub * Using ssh private key file /home/root/.ssh/id_dsa * SSH public key authentication failed: Unable to open public key file * Failure connecting to agent * Authentication failure * Closing connection 0 SW Design --------- My process is responsible for handling asynchronous file transfer requests as well as periodically monitoring a directory and uploading any files contained therein. Code ---- A 10ms timer (different from "dir check timer" above) is used to call curlMultiPeform (below) when there are active sessions. Bumped timer to 1000ms just for test, but issue still occurs. These are stripped down (NULL checks, esoteric code, etc) versions of my curl functions. I did not include my write callback used during debug because the design uses the libcurl default. Testing proved no difference. // derived from multi-app.c on your site bool curlMultiPerform(void) { struct timeval timeout; int rc; CURLMcode mcode; int activeTransfers; fd_set fdread; fd_set fdwrite; fd_set fdexcep; int maxfd = -1; long curl_timeo = -1; FD_ZERO(&fdread); FD_ZERO(&fdwrite); FD_ZERO(&fdexcep); timeout.tv_sec = 0; timeout.tv_usec = 1000; curl_multi_timeout(multiHandle, &curl_timeo); if(curl_timeo >= 0) { timeout.tv_sec = curl_timeo / 1000; if(timeout.tv_sec > 1) timeout.tv_sec = 1; else timeout.tv_usec = (curl_timeo % 1000) * 1000; } mcode = curl_multi_fdset(multiHandle, &fdread, &fdwrite, &fdexcep, &maxfd); if (mcode != CURLM_OK) { curlPurgeClients(); return false; } rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout); if (rc < 0) curlPurgeClients(); return false; } mcode = curl_multi_perform(multiHandle, &activeTransfers); if (mcode != CURLM_OK) { curlPurgeClients(); return false; } if (activeTransfers < activeTransfersLast) curlProcessMessages(); // somebody completed activeTransfersLast = activeTransfers; if (curlClients.size() > 0) curlTimerStart(); // somebody still active return true; } void curlProcessMessages(void) { CURLMsg *msg = NULL; CurlClient* client = NULL; int msgsLeft = 0; while ((msg = curl_multi_info_read(multiHandle, &msgsLeft))) { client = curlClientGet(msg->easy_handle); if (msg->msg == CURLMSG_DONE) { if (msg->data.result == CURLE_OK) status = OK; else status = NOT_OK; curlClientComplete(client); } } } void curlClientComplete(CurlClient *client) { if (client->curlHandle) { curl_multi_remove_handle(multiHandle, client->curlHandle); curl_easy_cleanup(client->curlHandle); } if (client->fd) fclose(client->fd); if (curlClients.size() == 0) { curl_multi_cleanup(multiHandle); // all sessions complete multiHandle = NULL; } } CurlClient * curlClientCreate(FtpReq *req) { CurlClient *client = NULL; if (multiHandle == NULL) multiHandle = curl_multi_init(); client = new CurlClient; memcpy(&(client->reqInfo), req, sizeof(FtpReq)); client->reqHandle = req->handle; client->curlHandle = NULL; client->fd = NULL; client->reqInfo.download = req->download; if (req->download) { snprintf(&(client->reqInfo.fileName[0]), FTP_MAX_STRLEN, %s/%d.download", DOWNLOAD_DIR, client->reqHandle); } else { strncpy(&(client->reqInfo.fileName[0]), &(req->fileName[0]), FTP_MAX_STRLEN); } client->fd = fopen(client->reqInfo.fileName, (req->download?"w+b":"r")); client->curlHandle = curl_easy_init(); curl_multi_add_handle(multiHandle, client->curlHandle); curlClients.push_back(client); return client; } Thank you for taking the time. -- Thanks, Dan ------------------------------------------------------------------- List admin: http://cool.haxx.se/list/listinfo/curl-library Etiquette: http://curl.haxx.se/mail/etiquette.htmlReceived on 2014-10-02