Include libs3 sources in the BlueSky tree.
[bluesky.git] / libs3-1.4 / src / request_context.c
diff --git a/libs3-1.4/src/request_context.c b/libs3-1.4/src/request_context.c
new file mode 100644 (file)
index 0000000..ae48e55
--- /dev/null
@@ -0,0 +1,190 @@
+/** **************************************************************************
+ * request_context.c
+ * 
+ * Copyright 2008 Bryan Ischo <bryan@ischo.com>
+ * 
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ ************************************************************************** **/
+
+#include <curl/curl.h>
+#include <stdlib.h>
+#include <sys/select.h>
+#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;
+}