Fix BlueSky build on modern Linux and libs3.
[bluesky.git] / libs3-1.4 / src / request_context.c
1 /** **************************************************************************
2  * request_context.c
3  * 
4  * Copyright 2008 Bryan Ischo <bryan@ischo.com>
5  * 
6  * This file is part of libs3.
7  * 
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.
11  *
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.
15  *
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
19  * details.
20  *
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/>.
24  *
25  ************************************************************************** **/
26
27 #include <curl/curl.h>
28 #include <stdlib.h>
29 #include <sys/select.h>
30 #include "request.h"
31 #include "request_context.h"
32
33
34 S3Status S3_create_request_context(S3RequestContext **requestContextReturn)
35 {
36     *requestContextReturn = 
37         (S3RequestContext *) malloc(sizeof(S3RequestContext));
38     
39     if (!*requestContextReturn) {
40         return S3StatusOutOfMemory;
41     }
42     
43     if (!((*requestContextReturn)->curlm = curl_multi_init())) {
44         free(*requestContextReturn);
45         return S3StatusOutOfMemory;
46     }
47
48     (*requestContextReturn)->requests = 0;
49
50     return S3StatusOK;
51 }
52
53
54 void S3_destroy_request_context(S3RequestContext *requestContext)
55 {
56     curl_multi_cleanup(requestContext->curlm);
57
58     // For each request in the context, call back its done method with
59     // 'interrupted' status
60     Request *r = requestContext->requests, *rFirst = r;
61     
62     if (r) do {
63         r->status = S3StatusInterrupted;
64         Request *rNext = r->next;
65         request_finish(r);
66         r = rNext;
67     } while (r != rFirst);
68
69     free(requestContext);
70 }
71
72
73 S3Status S3_runall_request_context(S3RequestContext *requestContext)
74 {
75     int requestsRemaining;
76     do {
77         fd_set readfds, writefds, exceptfds;
78         FD_ZERO(&readfds);
79         FD_ZERO(&writefds);
80         FD_ZERO(&exceptfds);
81         int maxfd;
82         S3Status status = S3_get_request_context_fdsets
83             (requestContext, &readfds, &writefds, &exceptfds, &maxfd);
84         if (status != S3StatusOK) {
85             return status;
86         }
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
91         if (maxfd != -1) {
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);
96         }
97         status = S3_runonce_request_context(requestContext,
98                                             &requestsRemaining);
99         if (status != S3StatusOK) {
100             return status;
101         }
102     } while (requestsRemaining);
103     
104     return S3StatusOK;
105 }
106
107
108 S3Status S3_runonce_request_context(S3RequestContext *requestContext, 
109                                     int *requestsRemainingReturn)
110 {
111     CURLMcode status;
112
113     do {
114         status = curl_multi_perform(requestContext->curlm,
115                                     requestsRemainingReturn);
116
117         switch (status) {
118         case CURLM_OK:
119         case CURLM_CALL_MULTI_PERFORM:
120             break;
121         case CURLM_OUT_OF_MEMORY:
122             return S3StatusOutOfMemory;
123         default:
124             return S3StatusInternalError;
125         }
126
127         CURLMsg *msg;
128         int junk;
129         while ((msg = curl_multi_info_read(requestContext->curlm, &junk))) {
130             if (msg->msg != CURLMSG_DONE) {
131                 return S3StatusInternalError;
132             }
133             Request *request;
134             if (curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, 
135                                   (char **) (char *) &request) != CURLE_OK) {
136                 return S3StatusInternalError;
137             }
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;
142             }
143             else {
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;
150             }
151             if ((msg->data.result != CURLE_OK) &&
152                 (request->status == S3StatusOK)) {
153                 request->status = request_curl_code_to_status
154                     (msg->data.result);
155             }
156             if (curl_multi_remove_handle(requestContext->curlm, 
157                                          msg->easy_handle) != CURLM_OK) {
158                 return S3StatusInternalError;
159             }
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;
166         }
167     } while (status == CURLM_CALL_MULTI_PERFORM);
168
169     return S3StatusOK;
170 }
171
172 S3Status S3_get_request_context_fdsets(S3RequestContext *requestContext,
173                                        fd_set *readFdSet, fd_set *writeFdSet,
174                                        fd_set *exceptFdSet, int *maxFd)
175 {
176     return ((curl_multi_fdset(requestContext->curlm, readFdSet, writeFdSet,
177                               exceptFdSet, maxFd) == CURLM_OK) ?
178             S3StatusOK : S3StatusInternalError);
179 }
180
181 int64_t S3_get_request_context_timeout(S3RequestContext *requestContext)
182 {
183     long timeout;
184
185     if (curl_multi_timeout(requestContext->curlm, &timeout) != CURLM_OK) {
186         timeout = 0;
187     }
188     
189     return timeout;
190 }