Merge branch 'svnup' of http://netz.smurf.noris.de/git/git
[gitweb.git] / http-fetch.c
index ed1053ade3e7e71251bfcf890cb304c7ce928222..b8aa965ea32f2956fc162d0a3cee9923808e165f 100644 (file)
@@ -25,7 +25,7 @@
 #define PREV_BUF_SIZE 4096
 #define RANGE_HEADER_SIZE 30
 
-static int got_alternates = 0;
+static int got_alternates = -1;
 static int active_requests = 0;
 static int data_received;
 
@@ -87,9 +87,19 @@ struct active_request_slot
        int done;
        CURLcode curl_result;
        long http_code;
+       void *callback_data;
+       void (*callback_func)(void *data);
        struct active_request_slot *next;
 };
 
+struct alt_request {
+       char *base;
+       char *url;
+       struct buffer *buffer;
+       struct active_request_slot *slot;
+       int http_specific;
+};
+
 static struct transfer_request *request_queue_head = NULL;
 static struct active_request_slot *active_queue_head = NULL;
 
@@ -237,7 +247,7 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
 static void process_curl_messages(void);
 static void process_request_queue(void);
 #endif
-static int fetch_alternates(char *base);
+static void fetch_alternates(char *base);
 
 static CURL* get_curl_handle(void)
 {
@@ -269,6 +279,8 @@ static CURL* get_curl_handle(void)
                                 curl_low_speed_time);
        }
 
+       curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
+
        return result;
 }
 
@@ -322,6 +334,8 @@ static struct active_request_slot *get_active_slot(void)
        slot->in_use = 1;
        slot->done = 0;
        slot->local = NULL;
+       slot->callback_data = NULL;
+       slot->callback_func = NULL;
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_range_header);
        curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
@@ -520,12 +534,17 @@ static void start_request(struct transfer_request *request)
 
 static void finish_request(struct transfer_request *request)
 {
+       struct stat st;
+
        fchmod(request->local, 0444);
        close(request->local);
 
        if (request->http_code == 416) {
                fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
        } else if (request->curl_result != CURLE_OK) {
+               if (stat(request->tmpfile, &st) == 0)
+                       if (st.st_size == 0)
+                               unlink(request->tmpfile);
                return;
        }
 
@@ -564,7 +583,7 @@ static void release_request(struct transfer_request *request)
 }
 
 #ifdef USE_CURL_MULTI
-void process_curl_messages(void)
+static void process_curl_messages(void)
 {
        int num_messages;
        struct active_request_slot *slot;
@@ -573,6 +592,7 @@ void process_curl_messages(void)
 
        while (curl_message != NULL) {
                if (curl_message->msg == CURLMSG_DONE) {
+                       int curl_result = curl_message->data.result;
                        slot = active_queue_head;
                        while (slot != NULL &&
                               slot->curl != curl_message->easy_handle)
@@ -582,7 +602,7 @@ void process_curl_messages(void)
                                active_requests--;
                                slot->done = 1;
                                slot->in_use = 0;
-                               slot->curl_result = curl_message->data.result;
+                               slot->curl_result = curl_result;
                                curl_easy_getinfo(slot->curl,
                                                  CURLINFO_HTTP_CODE,
                                                  &slot->http_code);
@@ -593,9 +613,14 @@ void process_curl_messages(void)
                        } else {
                                fprintf(stderr, "Received DONE message for unknown request!\n");
                        }
+
+                       /* Process slot callback if appropriate */
+                       if (slot->callback_func != NULL) {
+                               slot->callback_func(slot->callback_data);
+                       }
+
                        if (request != NULL) {
-                               request->curl_result =
-                                       curl_message->data.result;
+                               request->curl_result = curl_result;
                                request->http_code = slot->http_code;
                                request->slot = NULL;
                                request->state = COMPLETE;
@@ -607,6 +632,8 @@ void process_curl_messages(void)
                                                request->repo =
                                                        request->repo->next;
                                                start_request(request);
+                                       } else {
+                                               finish_request(request);
                                        }
                                } else {
                                        finish_request(request);
@@ -620,7 +647,7 @@ void process_curl_messages(void)
        }
 }
 
-void process_request_queue(void)
+static void process_request_queue(void)
 {
        struct transfer_request *request = request_queue_head;
        struct active_request_slot *slot = active_queue_head;
@@ -759,72 +786,51 @@ static int setup_index(struct alt_base *repo, unsigned char *sha1)
        return 0;
 }
 
-static int fetch_alternates(char *base)
+static void process_alternates(void *callback_data)
 {
-       int ret = 0;
-       struct buffer buffer;
-       char *url;
-       char *data;
-       int i = 0;
-       int http_specific = 1;
+       struct alt_request *alt_req = (struct alt_request *)callback_data;
+       struct active_request_slot *slot = alt_req->slot;
        struct alt_base *tail = alt;
+       char *base = alt_req->base;
        static const char null_byte = '\0';
+       char *data;
+       int i = 0;
 
-       struct active_request_slot *slot;
-
-       if (got_alternates)
-               return 0;
-
-       data = xmalloc(4096);
-       buffer.size = 4096;
-       buffer.posn = 0;
-       buffer.buffer = data;
-
-       if (get_verbosely)
-               fprintf(stderr, "Getting alternates list\n");
-       
-       url = xmalloc(strlen(base) + 31);
-       sprintf(url, "%s/objects/info/http-alternates", base);
-
-       slot = get_active_slot();
-       curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
-       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
-                        fwrite_buffer_dynamic);
-       curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-       if (start_active_slot(slot)) {
-               run_active_slot(slot);
-               if (slot->curl_result != CURLE_OK || !buffer.posn) {
-                       http_specific = 0;
-
-                       sprintf(url, "%s/objects/info/alternates", base);
-
-                       slot = get_active_slot();
-                       curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
-                       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
-                                        fwrite_buffer_dynamic);
-                       curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+       if (alt_req->http_specific) {
+               if (slot->curl_result != CURLE_OK ||
+                   !alt_req->buffer->posn) {
+
+                       /* Try reusing the slot to get non-http alternates */
+                       alt_req->http_specific = 0;
+                       sprintf(alt_req->url, "%s/objects/info/alternates",
+                               base);
+                       curl_easy_setopt(slot->curl, CURLOPT_URL,
+                                        alt_req->url);
+                       active_requests++;
+                       slot->in_use = 1;
+                       slot->done = 0;
                        if (start_active_slot(slot)) {
-                               run_active_slot(slot);
-                               if (slot->curl_result != CURLE_OK) {
-                                       free(buffer.buffer);
-                                       if (slot->http_code == 404)
-                                               got_alternates = 1;
-                                       return 0;
-                               }
+                               return;
+                       } else {
+                               got_alternates = -1;
+                               slot->done = 1;
+                               return;
                        }
                }
-       } else {
-               free(buffer.buffer);
-               return 0;
+       } else if (slot->curl_result != CURLE_OK) {
+               if (slot->http_code != 404) {
+                       got_alternates = -1;
+                       return;
+               }
        }
 
-       fwrite_buffer_dynamic(&null_byte, 1, 1, &buffer);
-       buffer.posn--;
-       data = buffer.buffer;
+       fwrite_buffer_dynamic(&null_byte, 1, 1, alt_req->buffer);
+       alt_req->buffer->posn--;
+       data = alt_req->buffer->buffer;
 
-       while (i < buffer.posn) {
+       while (i < alt_req->buffer->posn) {
                int posn = i;
-               while (posn < buffer.posn && data[posn] != '\n')
+               while (posn < alt_req->buffer->posn && data[posn] != '\n')
                        posn++;
                if (data[posn] == '\n') {
                        int okay = 0;
@@ -848,7 +854,7 @@ static int fetch_alternates(char *base)
                                // If the server got removed, give up.
                                okay = strchr(base, ':') - base + 3 < 
                                        serverlen;
-                       } else if (http_specific) {
+                       } else if (alt_req->http_specific) {
                                char *colon = strchr(data + i, ':');
                                char *slash = strchr(data + i, '/');
                                if (colon && slash && colon < data + posn &&
@@ -874,15 +880,74 @@ static int fetch_alternates(char *base)
                                while (tail->next != NULL)
                                        tail = tail->next;
                                tail->next = newalt;
-                               ret++;
                        }
                }
                i = posn + 1;
        }
 
        got_alternates = 1;
-       free(buffer.buffer);
-       return ret;
+}
+
+static void fetch_alternates(char *base)
+{
+       struct buffer buffer;
+       char *url;
+       char *data;
+       struct active_request_slot *slot;
+       static struct alt_request alt_req;
+       int num_transfers;
+
+       /* If another request has already started fetching alternates,
+          wait for them to arrive and return to processing this request's
+          curl message */
+       while (got_alternates == 0) {
+               curl_multi_perform(curlm, &num_transfers);
+               process_curl_messages();
+               process_request_queue();
+       }
+
+       /* Nothing to do if they've already been fetched */
+       if (got_alternates == 1)
+               return;
+
+       /* Start the fetch */
+       got_alternates = 0;
+
+       data = xmalloc(4096);
+       buffer.size = 4096;
+       buffer.posn = 0;
+       buffer.buffer = data;
+
+       if (get_verbosely)
+               fprintf(stderr, "Getting alternates list for %s\n", base);
+       
+       url = xmalloc(strlen(base) + 31);
+       sprintf(url, "%s/objects/info/http-alternates", base);
+
+       /* Use a callback to process the result, since another request
+          may fail and need to have alternates loaded before continuing */
+       slot = get_active_slot();
+       slot->callback_func = process_alternates;
+       slot->callback_data = &alt_req;
+
+       curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
+       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
+                        fwrite_buffer_dynamic);
+       curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+
+       alt_req.base = base;
+       alt_req.url = url;
+       alt_req.buffer = &buffer;
+       alt_req.http_specific = 1;
+       alt_req.slot = slot;
+
+       if (start_active_slot(slot))
+               run_active_slot(slot);
+       else
+               got_alternates = -1;
+
+       free(data);
+       free(url);
 }
 
 static int fetch_indices(struct alt_base *repo)
@@ -904,7 +969,7 @@ static int fetch_indices(struct alt_base *repo)
        buffer.buffer = data;
 
        if (get_verbosely)
-               fprintf(stderr, "Getting pack list\n");
+               fprintf(stderr, "Getting pack list for %s\n", repo->base);
        
        url = xmalloc(strlen(repo->base) + 21);
        sprintf(url, "%s/objects/info/packs", repo->base);
@@ -1095,9 +1160,12 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1)
        }
 
        if (request->curl_result != CURLE_OK && request->http_code != 416) {
-               ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",
-                           request->errorstr, request->curl_result,
-                           request->http_code, hex);
+               if (request->http_code == 404)
+                       ret = -1; /* Be silent, it is probably in a pack. */
+               else
+                       ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",
+                                   request->errorstr, request->curl_result,
+                                   request->http_code, hex);
                release_request(request);
                return ret;
        }
@@ -1226,6 +1294,8 @@ int main(int argc, char **argv)
        struct active_request_slot *slot;
        char *low_speed_limit;
        char *low_speed_time;
+       char *wait_url;
+       int rc = 0;
 
        while (arg < argc && argv[arg][0] == '-') {
                if (argv[arg][1] == 't') {
@@ -1313,7 +1383,7 @@ int main(int argc, char **argv)
        alt->next = NULL;
 
        if (pull(commit_id))
-               return 1;
+               rc = 1;
 
        curl_slist_free_all(pragma_header);
        curl_slist_free_all(no_pragma_header);
@@ -1323,6 +1393,15 @@ int main(int argc, char **argv)
 #endif
        slot = active_queue_head;
        while (slot != NULL) {
+               if (slot->in_use) {
+                       if (get_verbosely) {
+                               curl_easy_getinfo(slot->curl,
+                                                 CURLINFO_EFFECTIVE_URL,
+                                                 &wait_url);
+                               fprintf(stderr, "Waiting for %s\n", wait_url);
+                       }
+                       run_active_slot(slot);
+               }
                if (slot->curl != NULL)
                        curl_easy_cleanup(slot->curl);
                slot = slot->next;
@@ -1331,5 +1410,5 @@ int main(int argc, char **argv)
        curl_multi_cleanup(curlm);
 #endif
        curl_global_cleanup();
-       return 0;
+       return rc;
 }