/** ************************************************************************** * request_context.c * * Copyright 2008 Bryan Ischo * * This file is part of libs3. * * libs3 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, version 3 of the License. * * In addition, as a special exception, the copyright holders give * permission to link the code of this library and its programs with the * OpenSSL library, and distribute linked combinations including the two. * * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License version 3 * along with libs3, in a file named COPYING. If not, see * . * ************************************************************************** **/ #include #include #include #include "request.h" #include "request_context.h" S3Status S3_create_request_context(S3RequestContext **requestContextReturn) { *requestContextReturn = (S3RequestContext *) malloc(sizeof(S3RequestContext)); if (!*requestContextReturn) { return S3StatusOutOfMemory; } if (!((*requestContextReturn)->curlm = curl_multi_init())) { free(*requestContextReturn); return S3StatusOutOfMemory; } (*requestContextReturn)->requests = 0; return S3StatusOK; } void S3_destroy_request_context(S3RequestContext *requestContext) { curl_multi_cleanup(requestContext->curlm); // For each request in the context, call back its done method with // 'interrupted' status Request *r = requestContext->requests, *rFirst = r; if (r) do { r->status = S3StatusInterrupted; Request *rNext = r->next; request_finish(r); r = rNext; } while (r != rFirst); free(requestContext); } S3Status S3_runall_request_context(S3RequestContext *requestContext) { int requestsRemaining; do { fd_set readfds, writefds, exceptfds; FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); int maxfd; S3Status status = S3_get_request_context_fdsets (requestContext, &readfds, &writefds, &exceptfds, &maxfd); if (status != S3StatusOK) { return status; } // curl will return -1 if it hasn't even created any fds yet because // none of the connections have started yet. In this case, don't // do the select at all, because it will wait forever; instead, just // skip it and go straight to running the underlying CURL handles if (maxfd != -1) { int64_t timeout = S3_get_request_context_timeout(requestContext); struct timeval tv = { timeout / 1000, (timeout % 1000) * 1000 }; select(maxfd + 1, &readfds, &writefds, &exceptfds, (timeout == -1) ? 0 : &tv); } status = S3_runonce_request_context(requestContext, &requestsRemaining); if (status != S3StatusOK) { return status; } } while (requestsRemaining); return S3StatusOK; } S3Status S3_runonce_request_context(S3RequestContext *requestContext, int *requestsRemainingReturn) { CURLMcode status; do { status = curl_multi_perform(requestContext->curlm, requestsRemainingReturn); switch (status) { case CURLM_OK: case CURLM_CALL_MULTI_PERFORM: break; case CURLM_OUT_OF_MEMORY: return S3StatusOutOfMemory; default: return S3StatusInternalError; } CURLMsg *msg; int junk; while ((msg = curl_multi_info_read(requestContext->curlm, &junk))) { if (msg->msg != CURLMSG_DONE) { return S3StatusInternalError; } Request *request; if (curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char **) (char *) &request) != CURLE_OK) { return S3StatusInternalError; } // Remove the request from the list of requests if (request->prev == request->next) { // It was the only one on the list requestContext->requests = 0; } else { // It doesn't matter what the order of them are, so just in // case request was at the head of the list, put the one after // request to the head of the list requestContext->requests = request->next; request->prev->next = request->next; request->next->prev = request->prev; } if ((msg->data.result != CURLE_OK) && (request->status == S3StatusOK)) { request->status = request_curl_code_to_status (msg->data.result); } if (curl_multi_remove_handle(requestContext->curlm, msg->easy_handle) != CURLM_OK) { return S3StatusInternalError; } // Finish the request, ensuring that all callbacks have been made, // and also releases the request request_finish(request); // Now, since a callback was made, there may be new requests // queued up to be performed immediately, so do so status = CURLM_CALL_MULTI_PERFORM; } } while (status == CURLM_CALL_MULTI_PERFORM); return S3StatusOK; } S3Status S3_get_request_context_fdsets(S3RequestContext *requestContext, fd_set *readFdSet, fd_set *writeFdSet, fd_set *exceptFdSet, int *maxFd) { return ((curl_multi_fdset(requestContext->curlm, readFdSet, writeFdSet, exceptFdSet, maxFd) == CURLM_OK) ? S3StatusOK : S3StatusInternalError); } int64_t S3_get_request_context_timeout(S3RequestContext *requestContext) { long timeout; if (curl_multi_timeout(requestContext->curlm, &timeout) != CURLM_OK) { timeout = 0; } return timeout; }