1 /** **************************************************************************
4 * Copyright 2008 Bryan Ischo <bryan@ischo.com>
6 * This file is part of libs3.
8 * libs3 is free software: you can redistribute it and/or modify it under the
9 * terms of the GNU General Public License as published by the Free Software
10 * Foundation, version 3 of the License.
12 * In addition, as a special exception, the copyright holders give
13 * permission to link the code of this library and its programs with the
14 * OpenSSL library, and distribute linked combinations including the two.
16 * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY
17 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
21 * You should have received a copy of the GNU General Public License version 3
22 * along with libs3, in a file named COPYING. If not, see
23 * <http://www.gnu.org/licenses/>.
25 ************************************************************************** **/
27 #include <curl/curl.h>
29 #include <sys/select.h>
31 #include "request_context.h"
34 S3Status S3_create_request_context(S3RequestContext **requestContextReturn)
36 *requestContextReturn =
37 (S3RequestContext *) malloc(sizeof(S3RequestContext));
39 if (!*requestContextReturn) {
40 return S3StatusOutOfMemory;
43 if (!((*requestContextReturn)->curlm = curl_multi_init())) {
44 free(*requestContextReturn);
45 return S3StatusOutOfMemory;
48 (*requestContextReturn)->requests = 0;
54 void S3_destroy_request_context(S3RequestContext *requestContext)
56 curl_multi_cleanup(requestContext->curlm);
58 // For each request in the context, call back its done method with
59 // 'interrupted' status
60 Request *r = requestContext->requests, *rFirst = r;
63 r->status = S3StatusInterrupted;
64 Request *rNext = r->next;
67 } while (r != rFirst);
73 S3Status S3_runall_request_context(S3RequestContext *requestContext)
75 int requestsRemaining;
77 fd_set readfds, writefds, exceptfds;
82 S3Status status = S3_get_request_context_fdsets
83 (requestContext, &readfds, &writefds, &exceptfds, &maxfd);
84 if (status != S3StatusOK) {
87 // curl will return -1 if it hasn't even created any fds yet because
88 // none of the connections have started yet. In this case, don't
89 // do the select at all, because it will wait forever; instead, just
90 // skip it and go straight to running the underlying CURL handles
92 int64_t timeout = S3_get_request_context_timeout(requestContext);
93 struct timeval tv = { timeout / 1000, (timeout % 1000) * 1000 };
94 select(maxfd + 1, &readfds, &writefds, &exceptfds,
95 (timeout == -1) ? 0 : &tv);
97 status = S3_runonce_request_context(requestContext,
99 if (status != S3StatusOK) {
102 } while (requestsRemaining);
108 S3Status S3_runonce_request_context(S3RequestContext *requestContext,
109 int *requestsRemainingReturn)
114 status = curl_multi_perform(requestContext->curlm,
115 requestsRemainingReturn);
119 case CURLM_CALL_MULTI_PERFORM:
121 case CURLM_OUT_OF_MEMORY:
122 return S3StatusOutOfMemory;
124 return S3StatusInternalError;
129 while ((msg = curl_multi_info_read(requestContext->curlm, &junk))) {
130 if (msg->msg != CURLMSG_DONE) {
131 return S3StatusInternalError;
134 if (curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE,
135 (char **) (char *) &request) != CURLE_OK) {
136 return S3StatusInternalError;
138 // Remove the request from the list of requests
139 if (request->prev == request->next) {
140 // It was the only one on the list
141 requestContext->requests = 0;
144 // It doesn't matter what the order of them are, so just in
145 // case request was at the head of the list, put the one after
146 // request to the head of the list
147 requestContext->requests = request->next;
148 request->prev->next = request->next;
149 request->next->prev = request->prev;
151 if ((msg->data.result != CURLE_OK) &&
152 (request->status == S3StatusOK)) {
153 request->status = request_curl_code_to_status
156 if (curl_multi_remove_handle(requestContext->curlm,
157 msg->easy_handle) != CURLM_OK) {
158 return S3StatusInternalError;
160 // Finish the request, ensuring that all callbacks have been made,
161 // and also releases the request
162 request_finish(request);
163 // Now, since a callback was made, there may be new requests
164 // queued up to be performed immediately, so do so
165 status = CURLM_CALL_MULTI_PERFORM;
167 } while (status == CURLM_CALL_MULTI_PERFORM);
172 S3Status S3_get_request_context_fdsets(S3RequestContext *requestContext,
173 fd_set *readFdSet, fd_set *writeFdSet,
174 fd_set *exceptFdSet, int *maxFd)
176 return ((curl_multi_fdset(requestContext->curlm, readFdSet, writeFdSet,
177 exceptFdSet, maxFd) == CURLM_OK) ?
178 S3StatusOK : S3StatusInternalError);
181 int64_t S3_get_request_context_timeout(S3RequestContext *requestContext)
185 if (curl_multi_timeout(requestContext->curlm, &timeout) != CURLM_OK) {