{"thread":{"id":"2591","subject":"[PATCH 1/5] Isolate shared HTTP request functionality","startedAt":"2005-11-18T19:02:58Z","lastAt":"2005-11-18T19:02:58Z","messageCount":1,"participants":["Nick Hengeveld"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"12236","messageId":"20051118190258.GF3968@reactrix.com","threadId":"2591","inReplyTo":null,"subject":"[PATCH 1/5] Isolate shared HTTP request functionality","fromName":"Nick Hengeveld","fromEmail":"nickh@reactrix.com","sentAt":"2005-11-18T19:02:58Z","receivedAt":"2005-11-18T19:02:58Z","isPatch":true,"sender":{"key":"nickh@reactrix.com","avatar":null},"body":"Move shared HTTP request functionality out of http-fetch and http-push,\nand replace the two fwrite_buffer/fwrite_buffer_dynamic functions with\none fwrite_buffer function that does dynamic buffering.  Use slot\ncallbacks to process responses to fetch object transfer requests and\npush transfer requests, and put all of http-push into an #ifdef check\nfor curl multi support.\n\nSigned-off-by: Nick Hengeveld <nickh@reactrix.com>\n\n\n---\n\n Makefile     |    3 \n http-fetch.c |  586 ++++++----------------------------------------------------\n http-push.c  |  484 ++++--------------------------------------------\n http.c       |  443 ++++++++++++++++++++++++++++++++++++++++++++\n http.h       |   95 +++++++++\n 5 files changed, 635 insertions(+), 976 deletions(-)\n create mode 100644 http.c\n create mode 100644 http.h\n\napplies-to: ae209db4abcd6d0c83ac92c23bb9b16ffd5bcf7e\n4e68fe30c4999c9b1ad40ab562df61d9c267b8a2\ndiff --git a/Makefile b/Makefile\nindex 3bdd059..59c9a44 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -383,7 +383,8 @@ $(SIMPLE_PROGRAMS) : git-%$X : %.o\n \t$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(LIB_FILE) $(SIMPLE_LIB)\n \n-git-http-fetch$X: fetch.o\n+git-http-fetch$X: fetch.o http.o\n+git-http-push$X: http.o\n git-local-fetch$X: fetch.o\n git-ssh-fetch$X: rsh.o fetch.o\n git-ssh-upload$X: rsh.o\ndiff --git a/http-fetch.c b/http-fetch.c\nindex 21cc1b9..6a9eaf3 100644\n--- a/http-fetch.c\n+++ b/http-fetch.c\n@@ -2,44 +2,14 @@\n #include \"commit.h\"\n #include \"pack.h\"\n #include \"fetch.h\"\n-\n-#include <curl/curl.h>\n-#include <curl/easy.h>\n-\n-#if LIBCURL_VERSION_NUM >= 0x070908\n-#define USE_CURL_MULTI\n-#define DEFAULT_MAX_REQUESTS 5\n-#endif\n-\n-#if LIBCURL_VERSION_NUM < 0x070704\n-#define curl_global_cleanup() do { /* nothing */ } while(0)\n-#endif\n-#if LIBCURL_VERSION_NUM < 0x070800\n-#define curl_global_init(a) do { /* nothing */ } while(0)\n-#endif\n-\n-#if LIBCURL_VERSION_NUM < 0x070c04\n-#define NO_CURL_EASY_DUPHANDLE\n-#endif\n+#include \"http.h\"\n \n #define PREV_BUF_SIZE 4096\n #define RANGE_HEADER_SIZE 30\n \n static int got_alternates = -1;\n-static int active_requests = 0;\n-static int data_received;\n \n-#ifdef USE_CURL_MULTI\n-static int max_requests = -1;\n-static CURLM *curlm;\n-#endif\n-#ifndef NO_CURL_EASY_DUPHANDLE\n-static CURL *curl_default;\n-#endif\n-static struct curl_slist *pragma_header;\n static struct curl_slist *no_pragma_header;\n-static struct curl_slist *no_range_header;\n-static char curl_errorstr[CURL_ERROR_SIZE];\n \n struct alt_base\n {\n@@ -79,19 +49,6 @@ struct transfer_request\n \tstruct transfer_request *next;\n };\n \n-struct active_request_slot\n-{\n-\tCURL *curl;\n-\tFILE *local;\n-\tint in_use;\n-\tint done;\n-\tCURLcode curl_result;\n-\tlong http_code;\n-\tvoid *callback_data;\n-\tvoid (*callback_func)(void *data);\n-\tstruct active_request_slot *next;\n-};\n-\n struct alt_request {\n \tchar *base;\n \tchar *url;\n@@ -101,119 +58,6 @@ struct alt_request {\n };\n \n static struct transfer_request *request_queue_head = NULL;\n-static struct active_request_slot *active_queue_head = NULL;\n-\n-static int curl_ssl_verify = -1;\n-static char *ssl_cert = NULL;\n-#if LIBCURL_VERSION_NUM >= 0x070902\n-static char *ssl_key = NULL;\n-#endif\n-#if LIBCURL_VERSION_NUM >= 0x070908\n-static char *ssl_capath = NULL;\n-#endif\n-static char *ssl_cainfo = NULL;\n-static long curl_low_speed_limit = -1;\n-static long curl_low_speed_time = -1;\n-\n-struct buffer\n-{\n-        size_t posn;\n-        size_t size;\n-        void *buffer;\n-};\n-\n-static int http_options(const char *var, const char *value)\n-{\n-\tif (!strcmp(\"http.sslverify\", var)) {\n-\t\tif (curl_ssl_verify == -1) {\n-\t\t\tcurl_ssl_verify = git_config_bool(var, value);\n-\t\t}\n-\t\treturn 0;\n-\t}\n-\n-\tif (!strcmp(\"http.sslcert\", var)) {\n-\t\tif (ssl_cert == NULL) {\n-\t\t\tssl_cert = xmalloc(strlen(value)+1);\n-\t\t\tstrcpy(ssl_cert, value);\n-\t\t}\n-\t\treturn 0;\n-\t}\n-#if LIBCURL_VERSION_NUM >= 0x070902\n-\tif (!strcmp(\"http.sslkey\", var)) {\n-\t\tif (ssl_key == NULL) {\n-\t\t\tssl_key = xmalloc(strlen(value)+1);\n-\t\t\tstrcpy(ssl_key, value);\n-\t\t}\n-\t\treturn 0;\n-\t}\n-#endif\n-#if LIBCURL_VERSION_NUM >= 0x070908\n-\tif (!strcmp(\"http.sslcapath\", var)) {\n-\t\tif (ssl_capath == NULL) {\n-\t\t\tssl_capath = xmalloc(strlen(value)+1);\n-\t\t\tstrcpy(ssl_capath, value);\n-\t\t}\n-\t\treturn 0;\n-\t}\n-#endif\n-\tif (!strcmp(\"http.sslcainfo\", var)) {\n-\t\tif (ssl_cainfo == NULL) {\n-\t\t\tssl_cainfo = xmalloc(strlen(value)+1);\n-\t\t\tstrcpy(ssl_cainfo, value);\n-\t\t}\n-\t\treturn 0;\n-\t}\n-\n-#ifdef USE_CURL_MULTI\t\n-\tif (!strcmp(\"http.maxrequests\", var)) {\n-\t\tif (max_requests == -1)\n-\t\t\tmax_requests = git_config_int(var, value);\n-\t\treturn 0;\n-\t}\n-#endif\n-\n-\tif (!strcmp(\"http.lowspeedlimit\", var)) {\n-\t\tif (curl_low_speed_limit == -1)\n-\t\t\tcurl_low_speed_limit = (long)git_config_int(var, value);\n-\t\treturn 0;\n-\t}\n-\tif (!strcmp(\"http.lowspeedtime\", var)) {\n-\t\tif (curl_low_speed_time == -1)\n-\t\t\tcurl_low_speed_time = (long)git_config_int(var, value);\n-\t\treturn 0;\n-\t}\n-\n-\t/* Fall back on the default ones */\n-\treturn git_default_config(var, value);\n-}\n-\n-static size_t fwrite_buffer(void *ptr, size_t eltsize, size_t nmemb,\n-                            struct buffer *buffer)\n-{\n-        size_t size = eltsize * nmemb;\n-        if (size > buffer->size - buffer->posn)\n-                size = buffer->size - buffer->posn;\n-        memcpy(buffer->buffer + buffer->posn, ptr, size);\n-        buffer->posn += size;\n-\tdata_received++;\n-        return size;\n-}\n-\n-static size_t fwrite_buffer_dynamic(const void *ptr, size_t eltsize,\n-\t\t\t\t    size_t nmemb, struct buffer *buffer)\n-{\n-\tsize_t size = eltsize * nmemb;\n-\tif (size > buffer->size - buffer->posn) {\n-\t\tbuffer->size = buffer->size * 3 / 2;\n-\t\tif (buffer->size < buffer->posn + size)\n-\t\t\tbuffer->size = buffer->posn + size;\n-\t\tbuffer->buffer = xrealloc(buffer->buffer, buffer->size);\n-\t}\n-\tmemcpy(buffer->buffer + buffer->posn, ptr, size);\n-\tbuffer->posn += size;\n-\tdata_received++;\n-\treturn size;\n-}\n \n static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,\n \t\t\t       void *data)\n@@ -243,168 +87,9 @@ static size_t fwrite_sha1_file(void *ptr\n \treturn size;\n }\n \n-#ifdef USE_CURL_MULTI\n-static void process_curl_messages(void);\n-static void process_request_queue(void);\n-#endif\n static void fetch_alternates(char *base);\n \n-static CURL* get_curl_handle(void)\n-{\n-\tCURL* result = curl_easy_init();\n-\n-\tcurl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify);\n-#if LIBCURL_VERSION_NUM >= 0x070907\n-\tcurl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);\n-#endif\n-\n-\tif (ssl_cert != NULL)\n-\t\tcurl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);\n-#if LIBCURL_VERSION_NUM >= 0x070902\n-\tif (ssl_key != NULL)\n-\t\tcurl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);\n-#endif\n-#if LIBCURL_VERSION_NUM >= 0x070908\n-\tif (ssl_capath != NULL)\n-\t\tcurl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);\n-#endif\n-\tif (ssl_cainfo != NULL)\n-\t\tcurl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);\n-\tcurl_easy_setopt(result, CURLOPT_FAILONERROR, 1);\n-\n-\tif (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {\n-\t\tcurl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,\n-\t\t\t\t curl_low_speed_limit);\n-\t\tcurl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME,\n-\t\t\t\t curl_low_speed_time);\n-\t}\n-\n-\tcurl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);\n-\n-\treturn result;\n-}\n-\n-static struct active_request_slot *get_active_slot(void)\n-{\n-\tstruct active_request_slot *slot = active_queue_head;\n-\tstruct active_request_slot *newslot;\n-\n-#ifdef USE_CURL_MULTI\n-\tint num_transfers;\n-\n-\t/* Wait for a slot to open up if the queue is full */\n-\twhile (active_requests >= max_requests) {\n-\t\tcurl_multi_perform(curlm, &num_transfers);\n-\t\tif (num_transfers < active_requests) {\n-\t\t\tprocess_curl_messages();\n-\t\t}\n-\t}\n-#endif\n-\n-\twhile (slot != NULL && slot->in_use) {\n-\t\tslot = slot->next;\n-\t}\n-\tif (slot == NULL) {\n-\t\tnewslot = xmalloc(sizeof(*newslot));\n-\t\tnewslot->curl = NULL;\n-\t\tnewslot->in_use = 0;\n-\t\tnewslot->next = NULL;\n-\n-\t\tslot = active_queue_head;\n-\t\tif (slot == NULL) {\n-\t\t\tactive_queue_head = newslot;\n-\t\t} else {\n-\t\t\twhile (slot->next != NULL) {\n-\t\t\t\tslot = slot->next;\n-\t\t\t}\n-\t\t\tslot->next = newslot;\n-\t\t}\n-\t\tslot = newslot;\n-\t}\n-\n-\tif (slot->curl == NULL) {\n-#ifdef NO_CURL_EASY_DUPHANDLE\n-\t\tslot->curl = get_curl_handle();\n-#else\n-\t\tslot->curl = curl_easy_duphandle(curl_default);\n-#endif\n-\t}\n-\n-\tactive_requests++;\n-\tslot->in_use = 1;\n-\tslot->done = 0;\n-\tslot->local = NULL;\n-\tslot->callback_data = NULL;\n-\tslot->callback_func = NULL;\n-\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);\n-\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_range_header);\n-\tcurl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);\n-\n-\treturn slot;\n-}\n-\n-static int start_active_slot(struct active_request_slot *slot)\n-{\n-#ifdef USE_CURL_MULTI\n-\tCURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl);\n-\n-\tif (curlm_result != CURLM_OK &&\n-\t    curlm_result != CURLM_CALL_MULTI_PERFORM) {\n-\t\tactive_requests--;\n-\t\tslot->in_use = 0;\n-\t\treturn 0;\n-\t}\n-#endif\n-\treturn 1;\n-}\n-\n-static void run_active_slot(struct active_request_slot *slot)\n-{\n-#ifdef USE_CURL_MULTI\n-\tint num_transfers;\n-\tlong last_pos = 0;\n-\tlong current_pos;\n-\tfd_set readfds;\n-\tfd_set writefds;\n-\tfd_set excfds;\n-\tint max_fd;\n-\tstruct timeval select_timeout;\n-\tCURLMcode curlm_result;\n-\n-\twhile (!slot->done) {\n-\t\tdata_received = 0;\n-\t\tdo {\n-\t\t\tcurlm_result = curl_multi_perform(curlm,\n-\t\t\t\t\t\t\t  &num_transfers);\n-\t\t} while (curlm_result == CURLM_CALL_MULTI_PERFORM);\n-\t\tif (num_transfers < active_requests) {\n-\t\t\tprocess_curl_messages();\n-\t\t\tprocess_request_queue();\n-\t\t}\n-\n-\t\tif (!data_received && slot->local != NULL) {\n-\t\t\tcurrent_pos = ftell(slot->local);\n-\t\t\tif (current_pos > last_pos)\n-\t\t\t\tdata_received++;\n-\t\t\tlast_pos = current_pos;\n-\t\t}\n-\n-\t\tif (!slot->done && !data_received) {\n-\t\t\tmax_fd = 0;\n-\t\t\tFD_ZERO(&readfds);\n-\t\t\tFD_ZERO(&writefds);\n-\t\t\tFD_ZERO(&excfds);\n-\t\t\tselect_timeout.tv_sec = 0;\n-\t\t\tselect_timeout.tv_usec = 50000;\n-\t\t\tselect(max_fd, &readfds, &writefds,\n-\t\t\t       &excfds, &select_timeout);\n-\t\t}\n-\t}\n-#else\n-\tslot->curl_result = curl_easy_perform(slot->curl);\n-\tactive_requests--;\n-#endif\n-}\n+static void process_object_response(void *callback_data);\n \n static void start_request(struct transfer_request *request)\n {\n@@ -503,6 +188,10 @@ static void start_request(struct transfe\n \t}\n \n \tslot = get_active_slot();\n+\tslot->callback_func = process_object_response;\n+\tslot->callback_data = request;\n+\trequest->slot = slot;\n+\n \tcurl_easy_setopt(slot->curl, CURLOPT_FILE, request);\n \tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);\n \tcurl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);\n@@ -523,15 +212,13 @@ static void start_request(struct transfe\n \t}\n \n \t/* Try to get the request started, abort the request on error */\n+\trequest->state = ACTIVE;\n \tif (!start_active_slot(slot)) {\n \t\trequest->state = ABORTED;\n+\t\trequest->slot = NULL;\n \t\tclose(request->local); request->local = -1;\n \t\tfree(request->url);\n-\t\treturn;\n \t}\n-\t\n-\trequest->slot = slot;\n-\trequest->state = ACTIVE;\n }\n \n static void finish_request(struct transfer_request *request)\n@@ -567,6 +254,32 @@ static void finish_request(struct transf\n \t\tpull_say(\"got %s\\n\", sha1_to_hex(request->sha1));\n }\n \n+static void process_object_response(void *callback_data)\n+{\n+\tstruct transfer_request *request =\n+\t\t(struct transfer_request *)callback_data;\n+\n+\trequest->curl_result = request->slot->curl_result;\n+\trequest->http_code = request->slot->http_code;\n+\trequest->slot = NULL;\n+\trequest->state = COMPLETE;\n+\n+\t/* Use alternates if necessary */\n+\tif (request->http_code == 404) {\n+\t\tfetch_alternates(alt->base);\n+\t\tif (request->repo->next != NULL) {\n+\t\t\trequest->repo =\n+\t\t\t\trequest->repo->next;\n+\t\t\tclose(request->local);\n+\t\t\trequest->local = -1;\n+\t\t\tstart_request(request);\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\tfinish_request(request);\n+}\n+\n static void release_request(struct transfer_request *request)\n {\n \tstruct transfer_request *entry = request_queue_head;\n@@ -587,73 +300,7 @@ static void release_request(struct trans\n }\n \n #ifdef USE_CURL_MULTI\n-static void process_curl_messages(void)\n-{\n-\tint num_messages;\n-\tstruct active_request_slot *slot;\n-\tstruct transfer_request *request = NULL;\n-\tCURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages);\n-\n-\twhile (curl_message != NULL) {\n-\t\tif (curl_message->msg == CURLMSG_DONE) {\n-\t\t\tint curl_result = curl_message->data.result;\n-\t\t\tslot = active_queue_head;\n-\t\t\twhile (slot != NULL &&\n-\t\t\t       slot->curl != curl_message->easy_handle)\n-\t\t\t\tslot = slot->next;\n-\t\t\tif (slot != NULL) {\n-\t\t\t\tcurl_multi_remove_handle(curlm, slot->curl);\n-\t\t\t\tactive_requests--;\n-\t\t\t\tslot->done = 1;\n-\t\t\t\tslot->in_use = 0;\n-\t\t\t\tslot->curl_result = curl_result;\n-\t\t\t\tcurl_easy_getinfo(slot->curl,\n-\t\t\t\t\t\t  CURLINFO_HTTP_CODE,\n-\t\t\t\t\t\t  &slot->http_code);\n-\t\t\t\trequest = request_queue_head;\n-\t\t\t\twhile (request != NULL &&\n-\t\t\t\t       request->slot != slot)\n-\t\t\t\t\trequest = request->next;\n-\t\t\t} else {\n-\t\t\t\tfprintf(stderr, \"Received DONE message for unknown request!\\n\");\n-\t\t\t}\n-\n-\t\t\t/* Process slot callback if appropriate */\n-\t\t\tif (slot->callback_func != NULL) {\n-\t\t\t\tslot->callback_func(slot->callback_data);\n-\t\t\t}\n-\n-\t\t\tif (request != NULL) {\n-\t\t\t\trequest->curl_result = curl_result;\n-\t\t\t\trequest->http_code = slot->http_code;\n-\t\t\t\trequest->slot = NULL;\n-\t\t\t\trequest->state = COMPLETE;\n-\n-\t\t\t\t/* Use alternates if necessary */\n-\t\t\t\tif (request->http_code == 404) {\n-\t\t\t\t\tfetch_alternates(alt->base);\n-\t\t\t\t\tif (request->repo->next != NULL) {\n-\t\t\t\t\t\trequest->repo =\n-\t\t\t\t\t\t\trequest->repo->next;\n-\t\t\t\t\t\tclose(request->local);\n-\t\t\t\t\t\t\trequest->local = -1;\n-\t\t\t\t\t\tstart_request(request);\n-\t\t\t\t\t} else {\n-\t\t\t\t\t\tfinish_request(request);\n-\t\t\t\t\t}\n-\t\t\t\t} else {\n-\t\t\t\t\tfinish_request(request);\n-\t\t\t\t}\n-\t\t\t}\n-\t\t} else {\n-\t\t\tfprintf(stderr, \"Unknown CURL message received: %d\\n\",\n-\t\t\t\t(int)curl_message->msg);\n-\t\t}\n-\t\tcurl_message = curl_multi_info_read(curlm, &num_messages);\n-\t}\n-}\n-\n-static void process_request_queue(void)\n+void fill_active_slots(void)\n {\n \tstruct transfer_request *request = request_queue_head;\n \tstruct active_request_slot *slot = active_queue_head;\n@@ -706,9 +353,10 @@ void prefetch(unsigned char *sha1)\n \t\t}\n \t\ttail->next = newreq;\n \t}\n+\n #ifdef USE_CURL_MULTI\n-\tprocess_request_queue();\n-\tprocess_curl_messages();\n+\tfill_active_slots();\n+\tstep_active_slots();\n #endif\n }\n \n@@ -815,12 +463,11 @@ static void process_alternates(void *cal\n \t\t\t\t\t alt_req->url);\n \t\t\tactive_requests++;\n \t\t\tslot->in_use = 1;\n-\t\t\tslot->done = 0;\n \t\t\tif (start_active_slot(slot)) {\n \t\t\t\treturn;\n \t\t\t} else {\n \t\t\t\tgot_alternates = -1;\n-\t\t\t\tslot->done = 1;\n+\t\t\t\tslot->in_use = 0;\n \t\t\t\treturn;\n \t\t\t}\n \t\t}\n@@ -831,7 +478,7 @@ static void process_alternates(void *cal\n \t\t}\n \t}\n \n-\tfwrite_buffer_dynamic(&null_byte, 1, 1, alt_req->buffer);\n+\tfwrite_buffer(&null_byte, 1, 1, alt_req->buffer);\n \talt_req->buffer->posn--;\n \tdata = alt_req->buffer->buffer;\n \n@@ -902,16 +549,15 @@ static void fetch_alternates(char *base)\n \tchar *data;\n \tstruct active_request_slot *slot;\n \tstatic struct alt_request alt_req;\n-\tint num_transfers;\n \n \t/* If another request has already started fetching alternates,\n \t   wait for them to arrive and return to processing this request's\n \t   curl message */\n+#ifdef USE_CURL_MULTI\n \twhile (got_alternates == 0) {\n-\t\tcurl_multi_perform(curlm, &num_transfers);\n-\t\tprocess_curl_messages();\n-\t\tprocess_request_queue();\n+\t\tstep_active_slots();\n \t}\n+#endif\n \n \t/* Nothing to do if they've already been fetched */\n \tif (got_alternates == 1)\n@@ -938,8 +584,7 @@ static void fetch_alternates(char *base)\n \tslot->callback_data = &alt_req;\n \n \tcurl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);\n-\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,\n-\t\t\t fwrite_buffer_dynamic);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);\n \tcurl_easy_setopt(slot->curl, CURLOPT_URL, url);\n \n \talt_req.base = base;\n@@ -983,8 +628,7 @@ static int fetch_indices(struct alt_base\n \n \tslot = get_active_slot();\n \tcurl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);\n-\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,\n-\t\t\t fwrite_buffer_dynamic);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);\n \tcurl_easy_setopt(slot->curl, CURLOPT_URL, url);\n \tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);\n \tif (start_active_slot(slot)) {\n@@ -1115,7 +759,7 @@ static int fetch_pack(struct alt_base *r\n static int fetch_object(struct alt_base *repo, unsigned char *sha1)\n {\n \tchar *hex = sha1_to_hex(sha1);\n-\tint ret;\n+\tint ret = 0;\n \tstruct transfer_request *request = request_queue_head;\n \n \twhile (request != NULL && memcmp(request->sha1, sha1, 20))\n@@ -1130,12 +774,7 @@ static int fetch_object(struct alt_base \n \n #ifdef USE_CURL_MULTI\n \twhile (request->state == WAITING) {\n-\t\tint num_transfers;\n-\t\tcurl_multi_perform(curlm, &num_transfers);\n-\t\tif (num_transfers < active_requests) {\n-\t\t\tprocess_curl_messages();\n-\t\t\tprocess_request_queue();\n-\t\t}\n+\t\tstep_active_slots();\n \t}\n #else\n \tstart_request(request);\n@@ -1143,66 +782,33 @@ static int fetch_object(struct alt_base \n \n \twhile (request->state == ACTIVE) {\n \t\trun_active_slot(request->slot);\n-#ifndef USE_CURL_MULTI\n-\t\trequest->curl_result = request->slot->curl_result;\n-\t\trequest->http_code = request->slot->http_code;\n-\t\trequest->slot = NULL;\n-\n-\t\t/* Use alternates if necessary */\n-\t\tif (request->http_code == 404) {\n-\t\t\tfetch_alternates(alt->base);\n-\t\t\tif (request->repo->next != NULL) {\n-\t\t\t\trequest->repo = request->repo->next;\n-\t\t\t\tclose(request->local); request->local = -1;\n-\t\t\t\tstart_request(request);\n-\t\t\t}\n-\t\t} else {\n-\t\t\tfinish_request(request);\n-\t\t\trequest->state = COMPLETE;\n-\t\t}\n-#endif\n \t}\n \tif (request->local != -1) {\n \t\tclose(request->local); request->local = -1;\n \t}\n \n \tif (request->state == ABORTED) {\n-\t\trelease_request(request);\n-\t\treturn error(\"Request for %s aborted\", hex);\n-\t}\n-\n-\tif (request->curl_result != CURLE_OK && request->http_code != 416) {\n+\t\tret = error(\"Request for %s aborted\", hex);\n+\t} else if (request->curl_result != CURLE_OK &&\n+\t\t   request->http_code != 416) {\n \t\tif (request->http_code == 404)\n \t\t\tret = -1; /* Be silent, it is probably in a pack. */\n \t\telse\n \t\t\tret = error(\"%s (curl_result = %d, http_code = %ld, sha1 = %s)\",\n \t\t\t\t    request->errorstr, request->curl_result,\n \t\t\t\t    request->http_code, hex);\n-\t\trelease_request(request);\n-\t\treturn ret;\n-\t}\n-\n-\tif (request->zret != Z_STREAM_END) {\n+\t} else if (request->zret != Z_STREAM_END) {\n \t\tret = error(\"File %s (%s) corrupt\\n\", hex, request->url);\n-\t\trelease_request(request);\n-\t\treturn ret;\n-\t}\n-\n-\tif (memcmp(request->sha1, request->real_sha1, 20)) {\n-\t\trelease_request(request);\n-\t\treturn error(\"File %s has bad hash\\n\", hex);\n-\t}\n-\n-\tif (request->rename < 0) {\n+\t} else if (memcmp(request->sha1, request->real_sha1, 20)) {\n+\t\tret = error(\"File %s has bad hash\\n\", hex);\n+\t} else if (request->rename < 0) {\n \t\tret = error(\"unable to write sha1 filename %s: %s\",\n \t\t\t    request->filename,\n \t\t\t    strerror(request->rename));\n-\t\trelease_request(request);\n-\t\treturn ret;\n \t}\n \n \trelease_request(request);\n-\treturn 0;\n+\treturn ret;\n }\n \n int fetch(unsigned char *sha1)\n@@ -1303,10 +909,6 @@ int main(int argc, char **argv)\n \tchar *commit_id;\n \tchar *url;\n \tint arg = 1;\n-\tstruct active_request_slot *slot;\n-\tchar *low_speed_limit;\n-\tchar *low_speed_time;\n-\tchar *wait_url;\n \tint rc = 0;\n \n \twhile (arg < argc && argv[arg][0] == '-') {\n@@ -1335,58 +937,9 @@ int main(int argc, char **argv)\n \tcommit_id = argv[arg];\n \turl = argv[arg + 1];\n \n-\tcurl_global_init(CURL_GLOBAL_ALL);\n-\n-#ifdef USE_CURL_MULTI\n-\t{\n-\t\tchar *http_max_requests = getenv(\"GIT_HTTP_MAX_REQUESTS\");\n-\t\tif (http_max_requests != NULL)\n-\t\t\tmax_requests = atoi(http_max_requests);\n-\t}\n+\thttp_init();\n \n-\tcurlm = curl_multi_init();\n-\tif (curlm == NULL) {\n-\t\tfprintf(stderr, \"Error creating curl multi handle.\\n\");\n-\t\treturn 1;\n-\t}\n-#endif\n-\n-\tif (getenv(\"GIT_SSL_NO_VERIFY\"))\n-\t\tcurl_ssl_verify = 0;\n-\n-\tssl_cert = getenv(\"GIT_SSL_CERT\");\n-#if LIBCURL_VERSION_NUM >= 0x070902\n-\tssl_key = getenv(\"GIT_SSL_KEY\");\n-#endif\n-#if LIBCURL_VERSION_NUM >= 0x070908\n-\tssl_capath = getenv(\"GIT_SSL_CAPATH\");\n-#endif\n-\tssl_cainfo = getenv(\"GIT_SSL_CAINFO\");\n-\n-\tlow_speed_limit = getenv(\"GIT_HTTP_LOW_SPEED_LIMIT\");\n-\tif (low_speed_limit != NULL)\n-\t\tcurl_low_speed_limit = strtol(low_speed_limit, NULL, 10);\n-\tlow_speed_time = getenv(\"GIT_HTTP_LOW_SPEED_TIME\");\n-\tif (low_speed_time != NULL)\n-\t\tcurl_low_speed_time = strtol(low_speed_time, NULL, 10);\n-\n-\tgit_config(http_options);\n-\n-\tif (curl_ssl_verify == -1)\n-\t\tcurl_ssl_verify = 1;\n-\n-#ifdef USE_CURL_MULTI\n-\tif (max_requests < 1)\n-\t\tmax_requests = DEFAULT_MAX_REQUESTS;\n-#endif\n-\n-\tpragma_header = curl_slist_append(pragma_header, \"Pragma: no-cache\");\n \tno_pragma_header = curl_slist_append(no_pragma_header, \"Pragma:\");\n-\tno_range_header = curl_slist_append(no_range_header, \"Range:\");\n-\n-#ifndef NO_CURL_EASY_DUPHANDLE\n-\tcurl_default = get_curl_handle();\n-#endif\n \n \talt = xmalloc(sizeof(*alt));\n \talt->base = url;\n@@ -1397,30 +950,9 @@ int main(int argc, char **argv)\n \tif (pull(commit_id))\n \t\trc = 1;\n \n-\tcurl_slist_free_all(pragma_header);\n \tcurl_slist_free_all(no_pragma_header);\n-\tcurl_slist_free_all(no_range_header);\n-#ifndef NO_CURL_EASY_DUPHANDLE\n-\tcurl_easy_cleanup(curl_default);\n-#endif\n-\tslot = active_queue_head;\n-\twhile (slot != NULL) {\n-\t\tif (slot->in_use) {\n-\t\t\tif (get_verbosely) {\n-\t\t\t\tcurl_easy_getinfo(slot->curl,\n-\t\t\t\t\t\t  CURLINFO_EFFECTIVE_URL,\n-\t\t\t\t\t\t  &wait_url);\n-\t\t\t\tfprintf(stderr, \"Waiting for %s\\n\", wait_url);\n-\t\t\t}\n-\t\t\trun_active_slot(slot);\n-\t\t}\n-\t\tif (slot->curl != NULL)\n-\t\t\tcurl_easy_cleanup(slot->curl);\n-\t\tslot = slot->next;\n-\t}\n-#ifdef USE_CURL_MULTI\n-\tcurl_multi_cleanup(curlm);\n-#endif\n-\tcurl_global_cleanup();\n+\n+\thttp_cleanup();\n+\n \treturn rc;\n }\ndiff --git a/http-push.c b/http-push.c\nindex 8866189..afdcd8b 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -4,30 +4,15 @@\n #include \"fetch.h\"\n #include \"tag.h\"\n #include \"blob.h\"\n+#include \"http.h\"\n+\n+#ifdef USE_CURL_MULTI\n \n-#include <curl/curl.h>\n-#include <curl/easy.h>\n #include <expat.h>\n \n static const char http_push_usage[] =\n \"git-http-push [--complete] [--force] [--verbose] <url> <ref> [<ref>...]\\n\";\n \n-#if LIBCURL_VERSION_NUM >= 0x070908\n-#define USE_CURL_MULTI\n-#define DEFAULT_MAX_REQUESTS 5\n-#endif\n-\n-#if LIBCURL_VERSION_NUM < 0x070704\n-#define curl_global_cleanup() do { /* nothing */ } while(0)\n-#endif\n-#if LIBCURL_VERSION_NUM < 0x070800\n-#define curl_global_init(a) do { /* nothing */ } while(0)\n-#endif\n-\n-#if LIBCURL_VERSION_NUM < 0x070c04\n-#define NO_CURL_EASY_DUPHANDLE\n-#endif\n-\n #ifndef XML_STATUS_OK\n enum XML_Status {\n   XML_STATUS_OK = 1,\n@@ -52,34 +37,17 @@ enum XML_Status {\n #define LOCK_TIME 600\n #define LOCK_REFRESH 30\n \n-static int active_requests = 0;\n-static int data_received;\n static int pushing = 0;\n static int aborted = 0;\n static char remote_dir_exists[256];\n \n-#ifdef USE_CURL_MULTI\n-static int max_requests = -1;\n-static CURLM *curlm;\n-#endif\n-#ifndef NO_CURL_EASY_DUPHANDLE\n-static CURL *curl_default;\n-#endif\n static struct curl_slist *no_pragma_header;\n static struct curl_slist *default_headers;\n-static char curl_errorstr[CURL_ERROR_SIZE];\n \n static int push_verbosely = 0;\n static int push_all = 0;\n static int force_all = 0;\n \n-struct buffer\n-{\n-        size_t posn;\n-        size_t size;\n-        void *buffer;\n-};\n-\n struct repo\n {\n \tchar *url;\n@@ -122,31 +90,7 @@ struct transfer_request\n \tstruct transfer_request *next;\n };\n \n-struct active_request_slot\n-{\n-\tCURL *curl;\n-\tFILE *local;\n-\tint in_use;\n-\tint done;\n-\tCURLcode curl_result;\n-\tlong http_code;\n-\tstruct active_request_slot *next;\n-};\n-\n static struct transfer_request *request_queue_head = NULL;\n-static struct active_request_slot *active_queue_head = NULL;\n-\n-static int curl_ssl_verify = -1;\n-static char *ssl_cert = NULL;\n-#if LIBCURL_VERSION_NUM >= 0x070902\n-static char *ssl_key = NULL;\n-#endif\n-#if LIBCURL_VERSION_NUM >= 0x070908\n-static char *ssl_capath = NULL;\n-#endif\n-static char *ssl_cainfo = NULL;\n-static long curl_low_speed_limit = -1;\n-static long curl_low_speed_time = -1;\n \n struct active_lock\n {\n@@ -174,260 +118,14 @@ struct lockprop\n \tint lock_exclusive_write;\n };\n \n-static int http_options(const char *var, const char *value)\n-{\n-\tif (!strcmp(\"http.sslverify\", var)) {\n-\t\tif (curl_ssl_verify == -1) {\n-\t\t\tcurl_ssl_verify = git_config_bool(var, value);\n-\t\t}\n-\t\treturn 0;\n-\t}\n-\n-\tif (!strcmp(\"http.sslcert\", var)) {\n-\t\tif (ssl_cert == NULL) {\n-\t\t\tssl_cert = xmalloc(strlen(value)+1);\n-\t\t\tstrcpy(ssl_cert, value);\n-\t\t}\n-\t\treturn 0;\n-\t}\n-#if LIBCURL_VERSION_NUM >= 0x070902\n-\tif (!strcmp(\"http.sslkey\", var)) {\n-\t\tif (ssl_key == NULL) {\n-\t\t\tssl_key = xmalloc(strlen(value)+1);\n-\t\t\tstrcpy(ssl_key, value);\n-\t\t}\n-\t\treturn 0;\n-\t}\n-#endif\n-#if LIBCURL_VERSION_NUM >= 0x070908\n-\tif (!strcmp(\"http.sslcapath\", var)) {\n-\t\tif (ssl_capath == NULL) {\n-\t\t\tssl_capath = xmalloc(strlen(value)+1);\n-\t\t\tstrcpy(ssl_capath, value);\n-\t\t}\n-\t\treturn 0;\n-\t}\n-#endif\n-\tif (!strcmp(\"http.sslcainfo\", var)) {\n-\t\tif (ssl_cainfo == NULL) {\n-\t\t\tssl_cainfo = xmalloc(strlen(value)+1);\n-\t\t\tstrcpy(ssl_cainfo, value);\n-\t\t}\n-\t\treturn 0;\n-\t}\n+static void finish_request(struct transfer_request *request);\n \n-#ifdef USE_CURL_MULTI\t\n-\tif (!strcmp(\"http.maxrequests\", var)) {\n-\t\tif (max_requests == -1)\n-\t\t\tmax_requests = git_config_int(var, value);\n-\t\treturn 0;\n-\t}\n-#endif\n-\n-\tif (!strcmp(\"http.lowspeedlimit\", var)) {\n-\t\tif (curl_low_speed_limit == -1)\n-\t\t\tcurl_low_speed_limit = (long)git_config_int(var, value);\n-\t\treturn 0;\n-\t}\n-\tif (!strcmp(\"http.lowspeedtime\", var)) {\n-\t\tif (curl_low_speed_time == -1)\n-\t\t\tcurl_low_speed_time = (long)git_config_int(var, value);\n-\t\treturn 0;\n-\t}\n-\n-\t/* Fall back on the default ones */\n-\treturn git_default_config(var, value);\n-}\n-\n-static size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb,\n-\t\t\t   struct buffer *buffer)\n+static void process_response(void *callback_data)\n {\n-\tsize_t size = eltsize * nmemb;\n-\tif (size > buffer->size - buffer->posn)\n-\t\tsize = buffer->size - buffer->posn;\n-\tmemcpy(ptr, buffer->buffer + buffer->posn, size);\n-\tbuffer->posn += size;\n-\treturn size;\n-}\n-\n-static size_t fwrite_buffer_dynamic(const void *ptr, size_t eltsize,\n-\t\t\t\t    size_t nmemb, struct buffer *buffer)\n-{\n-\tsize_t size = eltsize * nmemb;\n-\tif (size > buffer->size - buffer->posn) {\n-\t\tbuffer->size = buffer->size * 3 / 2;\n-\t\tif (buffer->size < buffer->posn + size)\n-\t\t\tbuffer->size = buffer->posn + size;\n-\t\tbuffer->buffer = xrealloc(buffer->buffer, buffer->size);\n-\t}\n-\tmemcpy(buffer->buffer + buffer->posn, ptr, size);\n-\tbuffer->posn += size;\n-\tdata_received++;\n-\treturn size;\n-}\n+\tstruct transfer_request *request =\n+\t\t(struct transfer_request *)callback_data;\n \n-static size_t fwrite_null(const void *ptr, size_t eltsize,\n-\t\t\t  size_t nmemb, struct buffer *buffer)\n-{\n-\tdata_received++;\n-\treturn eltsize * nmemb;\n-}\n-\n-#ifdef USE_CURL_MULTI\n-static void process_curl_messages(void);\n-static void process_request_queue(void);\n-#endif\n-\n-static CURL* get_curl_handle(void)\n-{\n-\tCURL* result = curl_easy_init();\n-\n-\tcurl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify);\n-#if LIBCURL_VERSION_NUM >= 0x070907\n-\tcurl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);\n-#endif\n-\n-\tif (ssl_cert != NULL)\n-\t\tcurl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);\n-#if LIBCURL_VERSION_NUM >= 0x070902\n-\tif (ssl_key != NULL)\n-\t\tcurl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);\n-#endif\n-#if LIBCURL_VERSION_NUM >= 0x070908\n-\tif (ssl_capath != NULL)\n-\t\tcurl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);\n-#endif\n-\tif (ssl_cainfo != NULL)\n-\t\tcurl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);\n-\tcurl_easy_setopt(result, CURLOPT_FAILONERROR, 1);\n-\n-\tif (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {\n-\t\tcurl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,\n-\t\t\t\t curl_low_speed_limit);\n-\t\tcurl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME,\n-\t\t\t\t curl_low_speed_time);\n-\t}\n-\n-\treturn result;\n-}\n-\n-static struct active_request_slot *get_active_slot(void)\n-{\n-\tstruct active_request_slot *slot = active_queue_head;\n-\tstruct active_request_slot *newslot;\n-\n-#ifdef USE_CURL_MULTI\n-\tint num_transfers;\n-\n-\t/* Wait for a slot to open up if the queue is full */\n-\twhile (active_requests >= max_requests) {\n-\t\tcurl_multi_perform(curlm, &num_transfers);\n-\t\tif (num_transfers < active_requests) {\n-\t\t\tprocess_curl_messages();\n-\t\t}\n-\t}\n-#endif\n-\n-\twhile (slot != NULL && slot->in_use) {\n-\t\tslot = slot->next;\n-\t}\n-\tif (slot == NULL) {\n-\t\tnewslot = xmalloc(sizeof(*newslot));\n-\t\tnewslot->curl = NULL;\n-\t\tnewslot->in_use = 0;\n-\t\tnewslot->next = NULL;\n-\n-\t\tslot = active_queue_head;\n-\t\tif (slot == NULL) {\n-\t\t\tactive_queue_head = newslot;\n-\t\t} else {\n-\t\t\twhile (slot->next != NULL) {\n-\t\t\t\tslot = slot->next;\n-\t\t\t}\n-\t\t\tslot->next = newslot;\n-\t\t}\n-\t\tslot = newslot;\n-\t}\n-\n-\tif (slot->curl == NULL) {\n-#ifdef NO_CURL_EASY_DUPHANDLE\n-\t\tslot->curl = get_curl_handle();\n-#else\n-\t\tslot->curl = curl_easy_duphandle(curl_default);\n-#endif\n-\t}\n-\n-\tactive_requests++;\n-\tslot->in_use = 1;\n-\tslot->done = 0;\n-\tslot->local = NULL;\n-\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, default_headers);\n-\tcurl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);\n-\n-\treturn slot;\n-}\n-\n-static int start_active_slot(struct active_request_slot *slot)\n-{\n-#ifdef USE_CURL_MULTI\n-\tCURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl);\n-\n-\tif (curlm_result != CURLM_OK &&\n-\t    curlm_result != CURLM_CALL_MULTI_PERFORM) {\n-\t\tactive_requests--;\n-\t\tslot->in_use = 0;\n-\t\treturn 0;\n-\t}\n-#endif\n-\treturn 1;\n-}\n-\n-static void run_active_slot(struct active_request_slot *slot)\n-{\n-#ifdef USE_CURL_MULTI\n-\tint num_transfers;\n-\tlong last_pos = 0;\n-\tlong current_pos;\n-\tfd_set readfds;\n-\tfd_set writefds;\n-\tfd_set excfds;\n-\tint max_fd;\n-\tstruct timeval select_timeout;\n-\tCURLMcode curlm_result;\n-\n-\twhile (!slot->done) {\n-\t\tdata_received = 0;\n-\t\tdo {\n-\t\t\tcurlm_result = curl_multi_perform(curlm,\n-\t\t\t\t\t\t\t  &num_transfers);\n-\t\t} while (curlm_result == CURLM_CALL_MULTI_PERFORM);\n-\t\tif (num_transfers < active_requests) {\n-\t\t\tprocess_curl_messages();\n-\t\t\tprocess_request_queue();\n-\t\t}\n-\n-\t\tif (!data_received && slot->local != NULL) {\n-\t\t\tcurrent_pos = ftell(slot->local);\n-\t\t\tif (current_pos > last_pos)\n-\t\t\t\tdata_received++;\n-\t\t\tlast_pos = current_pos;\n-\t\t}\n-\n-\t\tif (!slot->done && !data_received) {\n-\t\t\tmax_fd = 0;\n-\t\t\tFD_ZERO(&readfds);\n-\t\t\tFD_ZERO(&writefds);\n-\t\t\tFD_ZERO(&excfds);\n-\t\t\tselect_timeout.tv_sec = 0;\n-\t\t\tselect_timeout.tv_usec = 50000;\n-\t\t\tselect(max_fd, &readfds, &writefds,\n-\t\t\t       &excfds, &select_timeout);\n-\t\t}\n-\t}\n-#else\n-\tslot->curl_result = curl_easy_perform(slot->curl);\n-\tactive_requests--;\n-#endif\n+\tfinish_request(request);\n }\n \n static void start_check(struct transfer_request *request)\n@@ -447,6 +145,8 @@ static void start_check(struct transfer_\n \tstrcpy(posn, hex + 2);\n \n \tslot = get_active_slot();\n+\tslot->callback_func = process_response;\n+\tslot->callback_data = request;\n \tcurl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);\n \tcurl_easy_setopt(slot->curl, CURLOPT_URL, request->url);\n \tcurl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);\n@@ -476,6 +176,8 @@ static void start_mkcol(struct transfer_\n \tstrcpy(posn, \"/\");\n \n \tslot = get_active_slot();\n+\tslot->callback_func = process_response;\n+\tslot->callback_data = request;\n \tcurl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */\n \tcurl_easy_setopt(slot->curl, CURLOPT_URL, request->url);\n \tcurl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);\n@@ -553,6 +255,8 @@ static void start_put(struct transfer_re\n \tstrcpy(posn, request->lock->token);\n \n \tslot = get_active_slot();\n+\tslot->callback_func = process_response;\n+\tslot->callback_data = request;\n \tcurl_easy_setopt(slot->curl, CURLOPT_INFILE, &request->buffer);\n \tcurl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, request->buffer.size);\n \tcurl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);\n@@ -578,6 +282,8 @@ static void start_move(struct transfer_r\n \tstruct curl_slist *dav_headers = NULL;\n \n \tslot = get_active_slot();\n+\tslot->callback_func = process_response;\n+\tslot->callback_data = request;\n \tcurl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */\n \tcurl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MOVE);\n \tdav_headers = curl_slist_append(dav_headers, request->dest);\n@@ -725,48 +431,7 @@ static void release_request(struct trans\n \tfree(request);\n }\n \n-#ifdef USE_CURL_MULTI\n-static void process_curl_messages(void)\n-{\n-\tint num_messages;\n-\tstruct active_request_slot *slot;\n-\tstruct transfer_request *request = NULL;\n-\tCURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages);\n-\n-\twhile (curl_message != NULL) {\n-\t\tif (curl_message->msg == CURLMSG_DONE) {\n-\t\t\tslot = active_queue_head;\n-\t\t\twhile (slot != NULL &&\n-\t\t\t       slot->curl != curl_message->easy_handle)\n-\t\t\t\tslot = slot->next;\n-\t\t\tif (slot != NULL) {\n-\t\t\t\tint curl_result = curl_message->data.result;\n-\t\t\t\tcurl_multi_remove_handle(curlm, slot->curl);\n-\t\t\t\tactive_requests--;\n-\t\t\t\tslot->done = 1;\n-\t\t\t\tslot->in_use = 0;\n-\t\t\t\tslot->curl_result = curl_result;\n-\t\t\t\tcurl_easy_getinfo(slot->curl,\n-\t\t\t\t\t\t  CURLINFO_HTTP_CODE,\n-\t\t\t\t\t\t  &slot->http_code);\n-\t\t\t\trequest = request_queue_head;\n-\t\t\t\twhile (request != NULL &&\n-\t\t\t\t       request->slot != slot)\n-\t\t\t\t\trequest = request->next;\n-\t\t\t\tif (request != NULL)\n-\t\t\t\t\tfinish_request(request);\n-\t\t\t} else {\n-\t\t\t\tfprintf(stderr, \"Received DONE message for unknown request!\\n\");\n-\t\t\t}\n-\t\t} else {\n-\t\t\tfprintf(stderr, \"Unknown CURL message received: %d\\n\",\n-\t\t\t\t(int)curl_message->msg);\n-\t\t}\n-\t\tcurl_message = curl_multi_info_read(curlm, &num_messages);\n-\t}\n-}\n-\n-static void process_request_queue(void)\n+void fill_active_slots(void)\n {\n \tstruct transfer_request *request = request_queue_head;\n \tstruct active_request_slot *slot = active_queue_head;\n@@ -797,20 +462,6 @@ static void process_request_queue(void)\n \t\tslot = slot->next;\n \t}\t\t\t\t\n }\n-#endif\n-\n-static void process_waiting_requests(void)\n-{\n-\tstruct active_request_slot *slot = active_queue_head;\n-\n-\twhile (slot != NULL)\n-\t\tif (slot->in_use) {\n-\t\t\trun_active_slot(slot);\n-\t\t\tslot = active_queue_head;\n-\t\t} else {\n-\t\t\tslot = slot->next;\n-\t\t}\n-}\n \n static void add_request(unsigned char *sha1, struct active_lock *lock)\n {\n@@ -834,10 +485,9 @@ static void add_request(unsigned char *s\n \trequest->state = NEED_CHECK;\n \trequest->next = request_queue_head;\n \trequest_queue_head = request;\n-#ifdef USE_CURL_MULTI\n-\tprocess_request_queue();\n-\tprocess_curl_messages();\n-#endif\n+\n+\tfill_active_slots();\n+\tstep_active_slots();\n }\n \n static int fetch_index(unsigned char *sha1)\n@@ -963,8 +613,7 @@ static int fetch_indices(void)\n \n \tslot = get_active_slot();\n \tcurl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);\n-\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,\n-\t\t\t fwrite_buffer_dynamic);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);\n \tcurl_easy_setopt(slot->curl, CURLOPT_URL, url);\n \tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);\n \tif (start_active_slot(slot)) {\n@@ -1068,8 +717,7 @@ int fetch_ref(char *ref, unsigned char *\n \turl = quote_ref_url(base, ref);\n \tslot = get_active_slot();\n \tcurl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);\n-\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,\n-\t\t\t fwrite_buffer_dynamic);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);\n \tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);\n \tcurl_easy_setopt(slot->curl, CURLOPT_URL, url);\n \tif (start_active_slot(slot)) {\n@@ -1261,8 +909,7 @@ static struct active_lock *lock_remote(c\n \tcurl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);\n \tcurl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);\n \tcurl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);\n-\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,\n-\t\t\t fwrite_buffer_dynamic);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);\n \tcurl_easy_setopt(slot->curl, CURLOPT_URL, url);\n \tcurl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);\n \tcurl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK);\n@@ -1390,8 +1037,7 @@ static int check_locking(void)\n \tcurl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);\n \tcurl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);\n \tcurl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);\n-\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,\n-\t\t\t fwrite_buffer_dynamic);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);\n \tcurl_easy_setopt(slot->curl, CURLOPT_URL, remote->url);\n \tcurl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);\n \tcurl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);\n@@ -1560,8 +1206,6 @@ static int update_remote(unsigned char *\n \n int main(int argc, char **argv)\n {\n-\tstruct active_request_slot *slot;\n-\tstruct active_request_slot *next_slot;\n \tstruct transfer_request *request;\n \tstruct transfer_request *next_request;\n \tint nr_refspec = 0;\n@@ -1576,8 +1220,6 @@ int main(int argc, char **argv)\n \tunsigned char remote_sha1[20];\n \tstruct active_lock *remote_lock;\n \tchar *remote_path = NULL;\n-\tchar *low_speed_limit;\n-\tchar *low_speed_time;\n \tint rc = 0;\n \tint i;\n \n@@ -1617,50 +1259,7 @@ int main(int argc, char **argv)\n \n \tmemset(remote_dir_exists, 0, 256);\n \n-\tcurl_global_init(CURL_GLOBAL_ALL);\n-\n-#ifdef USE_CURL_MULTI\n-\t{\n-\t\tchar *http_max_requests = getenv(\"GIT_HTTP_MAX_REQUESTS\");\n-\t\tif (http_max_requests != NULL)\n-\t\t\tmax_requests = atoi(http_max_requests);\n-\t}\n-\n-\tcurlm = curl_multi_init();\n-\tif (curlm == NULL) {\n-\t\tfprintf(stderr, \"Error creating curl multi handle.\\n\");\n-\t\treturn 1;\n-\t}\n-#endif\n-\n-\tif (getenv(\"GIT_SSL_NO_VERIFY\"))\n-\t\tcurl_ssl_verify = 0;\n-\n-\tssl_cert = getenv(\"GIT_SSL_CERT\");\n-#if LIBCURL_VERSION_NUM >= 0x070902\n-\tssl_key = getenv(\"GIT_SSL_KEY\");\n-#endif\n-#if LIBCURL_VERSION_NUM >= 0x070908\n-\tssl_capath = getenv(\"GIT_SSL_CAPATH\");\n-#endif\n-\tssl_cainfo = getenv(\"GIT_SSL_CAINFO\");\n-\n-\tlow_speed_limit = getenv(\"GIT_HTTP_LOW_SPEED_LIMIT\");\n-\tif (low_speed_limit != NULL)\n-\t\tcurl_low_speed_limit = strtol(low_speed_limit, NULL, 10);\n-\tlow_speed_time = getenv(\"GIT_HTTP_LOW_SPEED_TIME\");\n-\tif (low_speed_time != NULL)\n-\t\tcurl_low_speed_time = strtol(low_speed_time, NULL, 10);\n-\n-\tgit_config(http_options);\n-\n-\tif (curl_ssl_verify == -1)\n-\t\tcurl_ssl_verify = 1;\n-\n-#ifdef USE_CURL_MULTI\n-\tif (max_requests < 1)\n-\t\tmax_requests = DEFAULT_MAX_REQUESTS;\n-#endif\n+\thttp_init();\n \n \tno_pragma_header = curl_slist_append(no_pragma_header, \"Pragma:\");\n \tdefault_headers = curl_slist_append(default_headers, \"Range:\");\n@@ -1669,10 +1268,6 @@ int main(int argc, char **argv)\n \tdefault_headers = curl_slist_append(default_headers,\n \t\t\t\t\t    \"Pragma: no-cache\");\n \n-#ifndef NO_CURL_EASY_DUPHANDLE\n-\tcurl_default = get_curl_handle();\n-#endif\n-\n \t/* Verify DAV compliance/lock support */\n \tif (check_locking() != 0) {\n \t\tfprintf(stderr, \"Error: no DAV locking support on remote repo %s\\n\", remote->url);\n@@ -1766,13 +1361,13 @@ int main(int argc, char **argv)\n \t\t\tfetch_indices();\n \t\tget_delta(push_all ? NULL : remote_sha1,\n \t\t\t  local_object, remote_lock);\n-\t\tprocess_waiting_requests();\n+\t\tfinish_all_active_slots();\n \n \t\t/* Push missing objects to remote, this would be a\n \t\t   convenient time to pack them first if appropriate. */\n \t\tpushing = 1;\n-\t\tprocess_request_queue();\n-\t\tprocess_waiting_requests();\n+\t\tfill_active_slots();\n+\t\tfinish_all_active_slots();\n \n \t\t/* Update the remote branch if all went well */\n \t\tif (do_remote_update) {\n@@ -1802,14 +1397,7 @@ int main(int argc, char **argv)\n \tcurl_slist_free_all(no_pragma_header);\n \tcurl_slist_free_all(default_headers);\n \n-\tslot = active_queue_head;\n-\twhile (slot != NULL) {\n-\t\tnext_slot = slot->next;\n-\t\tif (slot->curl != NULL)\n-\t\t\tcurl_easy_cleanup(slot->curl);\n-\t\tfree(slot);\n-\t\tslot = next_slot;\n-\t}\n+\thttp_cleanup();\n \n \trequest = request_queue_head;\n \twhile (request != NULL) {\n@@ -1818,12 +1406,12 @@ int main(int argc, char **argv)\n \t\trequest = next_request;\n \t}\n \n-#ifndef NO_CURL_EASY_DUPHANDLE\n-\tcurl_easy_cleanup(curl_default);\n-#endif\n-#ifdef USE_CURL_MULTI\n-\tcurl_multi_cleanup(curlm);\n-#endif\n-\tcurl_global_cleanup();\n \treturn rc;\n }\n+#else /* ifdef USE_CURL_MULTI */\n+int main(int argc, char **argv)\n+{\n+\tfprintf(stderr, \"http-push requires curl 7.9.8 or higher.\\n\");\n+\treturn 1;\n+}\n+#endif\ndiff --git a/http.c b/http.c\nnew file mode 100644\nindex 0000000..470b56e\n--- /dev/null\n+++ b/http.c\n@@ -0,0 +1,443 @@\n+#include \"http.h\"\n+\n+int data_received;\n+int active_requests = 0;\n+\n+#ifdef USE_CURL_MULTI\n+int max_requests = -1;\n+CURLM *curlm;\n+#endif\n+#ifndef NO_CURL_EASY_DUPHANDLE\n+CURL *curl_default;\n+#endif\n+char curl_errorstr[CURL_ERROR_SIZE];\n+\n+int curl_ssl_verify = -1;\n+char *ssl_cert = NULL;\n+#if LIBCURL_VERSION_NUM >= 0x070902\n+char *ssl_key = NULL;\n+#endif\n+#if LIBCURL_VERSION_NUM >= 0x070908\n+char *ssl_capath = NULL;\n+#endif\n+char *ssl_cainfo = NULL;\n+long curl_low_speed_limit = -1;\n+long curl_low_speed_time = -1;\n+\n+struct curl_slist *pragma_header;\n+struct curl_slist *no_range_header;\n+\n+struct active_request_slot *active_queue_head = NULL;\n+\n+size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb,\n+\t\t\t   struct buffer *buffer)\n+{\n+\tsize_t size = eltsize * nmemb;\n+\tif (size > buffer->size - buffer->posn)\n+\t\tsize = buffer->size - buffer->posn;\n+\tmemcpy(ptr, buffer->buffer + buffer->posn, size);\n+\tbuffer->posn += size;\n+\treturn size;\n+}\n+\n+size_t fwrite_buffer(const void *ptr, size_t eltsize,\n+\t\t\t    size_t nmemb, struct buffer *buffer)\n+{\n+\tsize_t size = eltsize * nmemb;\n+\tif (size > buffer->size - buffer->posn) {\n+\t\tbuffer->size = buffer->size * 3 / 2;\n+\t\tif (buffer->size < buffer->posn + size)\n+\t\t\tbuffer->size = buffer->posn + size;\n+\t\tbuffer->buffer = xrealloc(buffer->buffer, buffer->size);\n+\t}\n+\tmemcpy(buffer->buffer + buffer->posn, ptr, size);\n+\tbuffer->posn += size;\n+\tdata_received++;\n+\treturn size;\n+}\n+\n+size_t fwrite_null(const void *ptr, size_t eltsize,\n+\t\t\t  size_t nmemb, struct buffer *buffer)\n+{\n+\tdata_received++;\n+\treturn eltsize * nmemb;\n+}\n+\n+static void finish_active_slot(struct active_request_slot *slot);\n+\n+#ifdef USE_CURL_MULTI\n+static void process_curl_messages(void)\n+{\n+\tint num_messages;\n+\tstruct active_request_slot *slot;\n+\tCURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages);\n+\n+\twhile (curl_message != NULL) {\n+\t\tif (curl_message->msg == CURLMSG_DONE) {\n+\t\t\tint curl_result = curl_message->data.result;\n+\t\t\tslot = active_queue_head;\n+\t\t\twhile (slot != NULL &&\n+\t\t\t       slot->curl != curl_message->easy_handle)\n+\t\t\t\tslot = slot->next;\n+\t\t\tif (slot != NULL) {\n+\t\t\t\tcurl_multi_remove_handle(curlm, slot->curl);\n+\t\t\t\tslot->curl_result = curl_result;\n+\t\t\t\tfinish_active_slot(slot);\n+\t\t\t} else {\n+\t\t\t\tfprintf(stderr, \"Received DONE message for unknown request!\\n\");\n+\t\t\t}\n+\t\t} else {\n+\t\t\tfprintf(stderr, \"Unknown CURL message received: %d\\n\",\n+\t\t\t\t(int)curl_message->msg);\n+\t\t}\n+\t\tcurl_message = curl_multi_info_read(curlm, &num_messages);\n+\t}\n+}\n+#endif\n+\n+static int http_options(const char *var, const char *value)\n+{\n+\tif (!strcmp(\"http.sslverify\", var)) {\n+\t\tif (curl_ssl_verify == -1) {\n+\t\t\tcurl_ssl_verify = git_config_bool(var, value);\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n+\tif (!strcmp(\"http.sslcert\", var)) {\n+\t\tif (ssl_cert == NULL) {\n+\t\t\tssl_cert = xmalloc(strlen(value)+1);\n+\t\t\tstrcpy(ssl_cert, value);\n+\t\t}\n+\t\treturn 0;\n+\t}\n+#if LIBCURL_VERSION_NUM >= 0x070902\n+\tif (!strcmp(\"http.sslkey\", var)) {\n+\t\tif (ssl_key == NULL) {\n+\t\t\tssl_key = xmalloc(strlen(value)+1);\n+\t\t\tstrcpy(ssl_key, value);\n+\t\t}\n+\t\treturn 0;\n+\t}\n+#endif\n+#if LIBCURL_VERSION_NUM >= 0x070908\n+\tif (!strcmp(\"http.sslcapath\", var)) {\n+\t\tif (ssl_capath == NULL) {\n+\t\t\tssl_capath = xmalloc(strlen(value)+1);\n+\t\t\tstrcpy(ssl_capath, value);\n+\t\t}\n+\t\treturn 0;\n+\t}\n+#endif\n+\tif (!strcmp(\"http.sslcainfo\", var)) {\n+\t\tif (ssl_cainfo == NULL) {\n+\t\t\tssl_cainfo = xmalloc(strlen(value)+1);\n+\t\t\tstrcpy(ssl_cainfo, value);\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n+#ifdef USE_CURL_MULTI\t\n+\tif (!strcmp(\"http.maxrequests\", var)) {\n+\t\tif (max_requests == -1)\n+\t\t\tmax_requests = git_config_int(var, value);\n+\t\treturn 0;\n+\t}\n+#endif\n+\n+\tif (!strcmp(\"http.lowspeedlimit\", var)) {\n+\t\tif (curl_low_speed_limit == -1)\n+\t\t\tcurl_low_speed_limit = (long)git_config_int(var, value);\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(\"http.lowspeedtime\", var)) {\n+\t\tif (curl_low_speed_time == -1)\n+\t\t\tcurl_low_speed_time = (long)git_config_int(var, value);\n+\t\treturn 0;\n+\t}\n+\n+\t/* Fall back on the default ones */\n+\treturn git_default_config(var, value);\n+}\n+\n+void http_init(void)\n+{\n+\tchar *low_speed_limit;\n+\tchar *low_speed_time;\n+\n+\tcurl_global_init(CURL_GLOBAL_ALL);\n+\n+\tpragma_header = curl_slist_append(pragma_header, \"Pragma: no-cache\");\n+\tno_range_header = curl_slist_append(no_range_header, \"Range:\");\n+\n+#ifdef USE_CURL_MULTI\n+\t{\n+\t\tchar *http_max_requests = getenv(\"GIT_HTTP_MAX_REQUESTS\");\n+\t\tif (http_max_requests != NULL)\n+\t\t\tmax_requests = atoi(http_max_requests);\n+\t}\n+\n+\tcurlm = curl_multi_init();\n+\tif (curlm == NULL) {\n+\t\tfprintf(stderr, \"Error creating curl multi handle.\\n\");\n+\t\texit(1);\n+\t}\n+#endif\n+\n+\tif (getenv(\"GIT_SSL_NO_VERIFY\"))\n+\t\tcurl_ssl_verify = 0;\n+\n+\tssl_cert = getenv(\"GIT_SSL_CERT\");\n+#if LIBCURL_VERSION_NUM >= 0x070902\n+\tssl_key = getenv(\"GIT_SSL_KEY\");\n+#endif\n+#if LIBCURL_VERSION_NUM >= 0x070908\n+\tssl_capath = getenv(\"GIT_SSL_CAPATH\");\n+#endif\n+\tssl_cainfo = getenv(\"GIT_SSL_CAINFO\");\n+\n+\tlow_speed_limit = getenv(\"GIT_HTTP_LOW_SPEED_LIMIT\");\n+\tif (low_speed_limit != NULL)\n+\t\tcurl_low_speed_limit = strtol(low_speed_limit, NULL, 10);\n+\tlow_speed_time = getenv(\"GIT_HTTP_LOW_SPEED_TIME\");\n+\tif (low_speed_time != NULL)\n+\t\tcurl_low_speed_time = strtol(low_speed_time, NULL, 10);\n+\n+\tgit_config(http_options);\n+\n+\tif (curl_ssl_verify == -1)\n+\t\tcurl_ssl_verify = 1;\n+\n+#ifdef USE_CURL_MULTI\n+\tif (max_requests < 1)\n+\t\tmax_requests = DEFAULT_MAX_REQUESTS;\n+#endif\n+\n+#ifndef NO_CURL_EASY_DUPHANDLE\n+\tcurl_default = get_curl_handle();\n+#endif\n+}\n+\n+void http_cleanup(void)\n+{\n+\tstruct active_request_slot *slot = active_queue_head;\n+#ifdef USE_CURL_MULTI\n+\tchar *wait_url;\n+\tCURLMcode curlm_result;\n+#endif\n+\n+\twhile (slot != NULL) {\n+#ifdef USE_CURL_MULTI\n+\t\tif (slot->in_use) {\n+\t\t\tcurl_easy_getinfo(slot->curl,\n+\t\t\t\t\t  CURLINFO_EFFECTIVE_URL,\n+\t\t\t\t\t  &wait_url);\n+\t\t\tfprintf(stderr, \"Waiting for %s\\n\", wait_url);\n+\t\t\trun_active_slot(slot);\n+\t\t}\n+#endif\n+\t\tif (slot->curl != NULL)\n+\t\t\tcurl_easy_cleanup(slot->curl);\n+\t\tslot = slot->next;\n+\t}\n+\n+#ifndef NO_CURL_EASY_DUPHANDLE\n+\tcurl_easy_cleanup(curl_default);\n+#endif\n+\n+#ifdef USE_CURL_MULTI\n+\tcurl_multi_cleanup(curlm);\n+#endif\n+\tcurl_global_cleanup();\n+\t\n+}\n+\n+static CURL* get_curl_handle(void)\n+{\n+\tCURL* result = curl_easy_init();\n+\n+\tcurl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify);\n+#if LIBCURL_VERSION_NUM >= 0x070907\n+\tcurl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);\n+#endif\n+\n+\tif (ssl_cert != NULL)\n+\t\tcurl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);\n+#if LIBCURL_VERSION_NUM >= 0x070902\n+\tif (ssl_key != NULL)\n+\t\tcurl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);\n+#endif\n+#if LIBCURL_VERSION_NUM >= 0x070908\n+\tif (ssl_capath != NULL)\n+\t\tcurl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);\n+#endif\n+\tif (ssl_cainfo != NULL)\n+\t\tcurl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);\n+\tcurl_easy_setopt(result, CURLOPT_FAILONERROR, 1);\n+\n+\tif (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {\n+\t\tcurl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,\n+\t\t\t\t curl_low_speed_limit);\n+\t\tcurl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME,\n+\t\t\t\t curl_low_speed_time);\n+\t}\n+\n+\tcurl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);\n+\n+\treturn result;\n+}\n+\n+struct active_request_slot *get_active_slot(void)\n+{\n+\tstruct active_request_slot *slot = active_queue_head;\n+\tstruct active_request_slot *newslot;\n+\n+#ifdef USE_CURL_MULTI\n+\tint num_transfers;\n+\n+\t/* Wait for a slot to open up if the queue is full */\n+\twhile (active_requests >= max_requests) {\n+\t\tcurl_multi_perform(curlm, &num_transfers);\n+\t\tif (num_transfers < active_requests) {\n+\t\t\tprocess_curl_messages();\n+\t\t}\n+\t}\n+#endif\n+\n+\twhile (slot != NULL && slot->in_use) {\n+\t\tslot = slot->next;\n+\t}\n+\tif (slot == NULL) {\n+\t\tnewslot = xmalloc(sizeof(*newslot));\n+\t\tnewslot->curl = NULL;\n+\t\tnewslot->in_use = 0;\n+\t\tnewslot->next = NULL;\n+\n+\t\tslot = active_queue_head;\n+\t\tif (slot == NULL) {\n+\t\t\tactive_queue_head = newslot;\n+\t\t} else {\n+\t\t\twhile (slot->next != NULL) {\n+\t\t\t\tslot = slot->next;\n+\t\t\t}\n+\t\t\tslot->next = newslot;\n+\t\t}\n+\t\tslot = newslot;\n+\t}\n+\n+\tif (slot->curl == NULL) {\n+#ifdef NO_CURL_EASY_DUPHANDLE\n+\t\tslot->curl = get_curl_handle();\n+#else\n+\t\tslot->curl = curl_easy_duphandle(curl_default);\n+#endif\n+\t}\n+\n+\tactive_requests++;\n+\tslot->in_use = 1;\n+\tslot->local = NULL;\n+\tslot->callback_data = NULL;\n+\tslot->callback_func = NULL;\n+\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_range_header);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);\n+\n+\treturn slot;\n+}\n+\n+int start_active_slot(struct active_request_slot *slot)\n+{\n+#ifdef USE_CURL_MULTI\n+\tCURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl);\n+\n+\tif (curlm_result != CURLM_OK &&\n+\t    curlm_result != CURLM_CALL_MULTI_PERFORM) {\n+\t\tactive_requests--;\n+\t\tslot->in_use = 0;\n+\t\treturn 0;\n+\t}\n+#endif\n+\treturn 1;\n+}\n+\n+#ifdef USE_CURL_MULTI\n+void step_active_slots(void)\n+{\n+\tint num_transfers;\n+\tCURLMcode curlm_result;\n+\n+\tdo {\n+\t\tcurlm_result = curl_multi_perform(curlm, &num_transfers);\n+\t} while (curlm_result == CURLM_CALL_MULTI_PERFORM);\n+\tif (num_transfers < active_requests) {\n+\t\tprocess_curl_messages();\n+\t\tfill_active_slots();\n+\t}\n+}\n+#endif\n+\n+void run_active_slot(struct active_request_slot *slot)\n+{\n+#ifdef USE_CURL_MULTI\n+\tlong last_pos = 0;\n+\tlong current_pos;\n+\tfd_set readfds;\n+\tfd_set writefds;\n+\tfd_set excfds;\n+\tint max_fd;\n+\tstruct timeval select_timeout;\n+\n+\twhile (slot->in_use) {\n+\t\tdata_received = 0;\n+\t\tstep_active_slots();\n+\n+\t\tif (!data_received && slot->local != NULL) {\n+\t\t\tcurrent_pos = ftell(slot->local);\n+\t\t\tif (current_pos > last_pos)\n+\t\t\t\tdata_received++;\n+\t\t\tlast_pos = current_pos;\n+\t\t}\n+\n+\t\tif (slot->in_use && !data_received) {\n+\t\t\tmax_fd = 0;\n+\t\t\tFD_ZERO(&readfds);\n+\t\t\tFD_ZERO(&writefds);\n+\t\t\tFD_ZERO(&excfds);\n+\t\t\tselect_timeout.tv_sec = 0;\n+\t\t\tselect_timeout.tv_usec = 50000;\n+\t\t\tselect(max_fd, &readfds, &writefds,\n+\t\t\t       &excfds, &select_timeout);\n+\t\t}\n+\t}\n+#else\n+\twhile (slot->in_use) {\n+\t\tslot->curl_result = curl_easy_perform(slot->curl);\n+\t\tfinish_active_slot(slot);\n+\t}\n+#endif\n+}\n+\n+static void finish_active_slot(struct active_request_slot *slot)\n+{\n+        active_requests--;\n+        slot->in_use = 0;\n+        curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);\n+ \n+        /* Run callback if appropriate */\n+        if (slot->callback_func != NULL) {\n+                slot->callback_func(slot->callback_data);\n+        }\n+}\n+\n+void finish_all_active_slots(void)\n+{\n+\tstruct active_request_slot *slot = active_queue_head;\n+\n+\twhile (slot != NULL)\n+\t\tif (slot->in_use) {\n+\t\t\trun_active_slot(slot);\n+\t\t\tslot = active_queue_head;\n+\t\t} else {\n+\t\t\tslot = slot->next;\n+\t\t}\n+}\ndiff --git a/http.h b/http.h\nnew file mode 100644\nindex 0000000..ed4ea33\n--- /dev/null\n+++ b/http.h\n@@ -0,0 +1,95 @@\n+#ifndef HTTP_H\n+#define HTTP_H\n+\n+#include \"cache.h\"\n+\n+#include <curl/curl.h>\n+#include <curl/easy.h>\n+\n+#if LIBCURL_VERSION_NUM >= 0x070908\n+#define USE_CURL_MULTI\n+#define DEFAULT_MAX_REQUESTS 5\n+#endif\n+\n+#if LIBCURL_VERSION_NUM < 0x070704\n+#define curl_global_cleanup() do { /* nothing */ } while(0)\n+#endif\n+#if LIBCURL_VERSION_NUM < 0x070800\n+#define curl_global_init(a) do { /* nothing */ } while(0)\n+#endif\n+\n+#if LIBCURL_VERSION_NUM < 0x070c04\n+#define NO_CURL_EASY_DUPHANDLE\n+#endif\n+\n+struct active_request_slot\n+{\n+\tCURL *curl;\n+\tFILE *local;\n+\tint in_use;\n+\tCURLcode curl_result;\n+\tlong http_code;\n+\tvoid *callback_data;\n+\tvoid (*callback_func)(void *data);\n+\tstruct active_request_slot *next;\n+};\n+\n+struct buffer\n+{\n+        size_t posn;\n+        size_t size;\n+        void *buffer;\n+};\n+\n+/* Curl request read/write callbacks */\n+extern size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb,\n+\t\t\t   struct buffer *buffer);\n+extern size_t fwrite_buffer(const void *ptr, size_t eltsize,\n+\t\t\t    size_t nmemb, struct buffer *buffer);\n+extern size_t fwrite_null(const void *ptr, size_t eltsize,\n+\t\t\t  size_t nmemb, struct buffer *buffer);\n+\n+/* Slot lifecycle functions */\n+extern struct active_request_slot *get_active_slot(void);\n+extern int start_active_slot(struct active_request_slot *slot);\n+extern void run_active_slot(struct active_request_slot *slot);\n+extern void finish_all_active_slots(void);\n+\n+#ifdef USE_CURL_MULTI\n+extern void fill_active_slots(void);\n+extern void step_active_slots(void);\n+#endif\n+\n+extern void http_init(void);\n+extern void http_cleanup(void);\n+\n+extern int data_received;\n+extern int active_requests;\n+\n+#ifdef USE_CURL_MULTI\n+extern int max_requests;\n+extern CURLM *curlm;\n+#endif\n+#ifndef NO_CURL_EASY_DUPHANDLE\n+extern CURL *curl_default;\n+#endif\n+extern char curl_errorstr[CURL_ERROR_SIZE];\n+\n+extern int curl_ssl_verify;\n+extern char *ssl_cert;\n+#if LIBCURL_VERSION_NUM >= 0x070902\n+extern char *ssl_key;\n+#endif\n+#if LIBCURL_VERSION_NUM >= 0x070908\n+extern char *ssl_capath;\n+#endif\n+extern char *ssl_cainfo;\n+extern long curl_low_speed_limit;\n+extern long curl_low_speed_time;\n+\n+extern struct curl_slist *pragma_header;\n+extern struct curl_slist *no_range_header;\n+\n+extern struct active_request_slot *active_queue_head;\n+\n+#endif /* HTTP_H */\n---\n0.99.9.GIT\n"}]}