Isolate shared HTTP request functionality
Move shared HTTP request functionality out of http-fetch and http-push, and replace the two fwrite_buffer/fwrite_buffer_dynamic functions with one fwrite_buffer function that does dynamic buffering. Use slot callbacks to process responses to fetch object transfer requests and push transfer requests, and put all of http-push into an #ifdef check for curl multi support. Signed-off-by: Nick Hengeveld <nickh@reactrix.com> Signed-off-by: Junio C Hamano <junkio@cox.net>maint
							parent
							
								
									80e21a9ed8
								
							
						
					
					
						commit
						29508e1efb
					
				
							
								
								
									
										3
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										3
									
								
								Makefile
								
								
								
								
							|  | @ -387,7 +387,8 @@ $(SIMPLE_PROGRAMS) : git-%$X : %.o | ||||||
| 	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ | 	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ | ||||||
| 		$(LIB_FILE) $(SIMPLE_LIB) | 		$(LIB_FILE) $(SIMPLE_LIB) | ||||||
|  |  | ||||||
| git-http-fetch$X: fetch.o | git-http-fetch$X: fetch.o http.o | ||||||
|  | git-http-push$X: http.o | ||||||
| git-local-fetch$X: fetch.o | git-local-fetch$X: fetch.o | ||||||
| git-ssh-fetch$X: rsh.o fetch.o | git-ssh-fetch$X: rsh.o fetch.o | ||||||
| git-ssh-upload$X: rsh.o | git-ssh-upload$X: rsh.o | ||||||
|  |  | ||||||
							
								
								
									
										586
									
								
								http-fetch.c
								
								
								
								
							
							
						
						
									
										586
									
								
								http-fetch.c
								
								
								
								
							|  | @ -2,44 +2,14 @@ | ||||||
| #include "commit.h" | #include "commit.h" | ||||||
| #include "pack.h" | #include "pack.h" | ||||||
| #include "fetch.h" | #include "fetch.h" | ||||||
|  | #include "http.h" | ||||||
| #include <curl/curl.h> |  | ||||||
| #include <curl/easy.h> |  | ||||||
|  |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070908 |  | ||||||
| #define USE_CURL_MULTI |  | ||||||
| #define DEFAULT_MAX_REQUESTS 5 |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #if LIBCURL_VERSION_NUM < 0x070704 |  | ||||||
| #define curl_global_cleanup() do { /* nothing */ } while(0) |  | ||||||
| #endif |  | ||||||
| #if LIBCURL_VERSION_NUM < 0x070800 |  | ||||||
| #define curl_global_init(a) do { /* nothing */ } while(0) |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #if LIBCURL_VERSION_NUM < 0x070c04 |  | ||||||
| #define NO_CURL_EASY_DUPHANDLE |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #define PREV_BUF_SIZE 4096 | #define PREV_BUF_SIZE 4096 | ||||||
| #define RANGE_HEADER_SIZE 30 | #define RANGE_HEADER_SIZE 30 | ||||||
|  |  | ||||||
| static int got_alternates = -1; | static int got_alternates = -1; | ||||||
| static int active_requests = 0; |  | ||||||
| static int data_received; |  | ||||||
|  |  | ||||||
| #ifdef USE_CURL_MULTI |  | ||||||
| static int max_requests = -1; |  | ||||||
| static CURLM *curlm; |  | ||||||
| #endif |  | ||||||
| #ifndef NO_CURL_EASY_DUPHANDLE |  | ||||||
| static CURL *curl_default; |  | ||||||
| #endif |  | ||||||
| static struct curl_slist *pragma_header; |  | ||||||
| static struct curl_slist *no_pragma_header; | static struct curl_slist *no_pragma_header; | ||||||
| static struct curl_slist *no_range_header; |  | ||||||
| static char curl_errorstr[CURL_ERROR_SIZE]; |  | ||||||
|  |  | ||||||
| struct alt_base | struct alt_base | ||||||
| { | { | ||||||
|  | @ -79,19 +49,6 @@ struct transfer_request | ||||||
| 	struct transfer_request *next; | 	struct transfer_request *next; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct active_request_slot |  | ||||||
| { |  | ||||||
| 	CURL *curl; |  | ||||||
| 	FILE *local; |  | ||||||
| 	int in_use; |  | ||||||
| 	int done; |  | ||||||
| 	CURLcode curl_result; |  | ||||||
| 	long http_code; |  | ||||||
| 	void *callback_data; |  | ||||||
| 	void (*callback_func)(void *data); |  | ||||||
| 	struct active_request_slot *next; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct alt_request { | struct alt_request { | ||||||
| 	char *base; | 	char *base; | ||||||
| 	char *url; | 	char *url; | ||||||
|  | @ -101,119 +58,6 @@ struct alt_request { | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct transfer_request *request_queue_head = NULL; | static struct transfer_request *request_queue_head = NULL; | ||||||
| static struct active_request_slot *active_queue_head = NULL; |  | ||||||
|  |  | ||||||
| static int curl_ssl_verify = -1; |  | ||||||
| static char *ssl_cert = NULL; |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070902 |  | ||||||
| static char *ssl_key = NULL; |  | ||||||
| #endif |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070908 |  | ||||||
| static char *ssl_capath = NULL; |  | ||||||
| #endif |  | ||||||
| static char *ssl_cainfo = NULL; |  | ||||||
| static long curl_low_speed_limit = -1; |  | ||||||
| static long curl_low_speed_time = -1; |  | ||||||
|  |  | ||||||
| struct buffer |  | ||||||
| { |  | ||||||
|         size_t posn; |  | ||||||
|         size_t size; |  | ||||||
|         void *buffer; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static int http_options(const char *var, const char *value) |  | ||||||
| { |  | ||||||
| 	if (!strcmp("http.sslverify", var)) { |  | ||||||
| 		if (curl_ssl_verify == -1) { |  | ||||||
| 			curl_ssl_verify = git_config_bool(var, value); |  | ||||||
| 		} |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (!strcmp("http.sslcert", var)) { |  | ||||||
| 		if (ssl_cert == NULL) { |  | ||||||
| 			ssl_cert = xmalloc(strlen(value)+1); |  | ||||||
| 			strcpy(ssl_cert, value); |  | ||||||
| 		} |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070902 |  | ||||||
| 	if (!strcmp("http.sslkey", var)) { |  | ||||||
| 		if (ssl_key == NULL) { |  | ||||||
| 			ssl_key = xmalloc(strlen(value)+1); |  | ||||||
| 			strcpy(ssl_key, value); |  | ||||||
| 		} |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| #endif |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070908 |  | ||||||
| 	if (!strcmp("http.sslcapath", var)) { |  | ||||||
| 		if (ssl_capath == NULL) { |  | ||||||
| 			ssl_capath = xmalloc(strlen(value)+1); |  | ||||||
| 			strcpy(ssl_capath, value); |  | ||||||
| 		} |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| #endif |  | ||||||
| 	if (!strcmp("http.sslcainfo", var)) { |  | ||||||
| 		if (ssl_cainfo == NULL) { |  | ||||||
| 			ssl_cainfo = xmalloc(strlen(value)+1); |  | ||||||
| 			strcpy(ssl_cainfo, value); |  | ||||||
| 		} |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| #ifdef USE_CURL_MULTI	 |  | ||||||
| 	if (!strcmp("http.maxrequests", var)) { |  | ||||||
| 		if (max_requests == -1) |  | ||||||
| 			max_requests = git_config_int(var, value); |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| 	if (!strcmp("http.lowspeedlimit", var)) { |  | ||||||
| 		if (curl_low_speed_limit == -1) |  | ||||||
| 			curl_low_speed_limit = (long)git_config_int(var, value); |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| 	if (!strcmp("http.lowspeedtime", var)) { |  | ||||||
| 		if (curl_low_speed_time == -1) |  | ||||||
| 			curl_low_speed_time = (long)git_config_int(var, value); |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* Fall back on the default ones */ |  | ||||||
| 	return git_default_config(var, value); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static size_t fwrite_buffer(void *ptr, size_t eltsize, size_t nmemb, |  | ||||||
|                             struct buffer *buffer) |  | ||||||
| { |  | ||||||
|         size_t size = eltsize * nmemb; |  | ||||||
|         if (size > buffer->size - buffer->posn) |  | ||||||
|                 size = buffer->size - buffer->posn; |  | ||||||
|         memcpy(buffer->buffer + buffer->posn, ptr, size); |  | ||||||
|         buffer->posn += size; |  | ||||||
| 	data_received++; |  | ||||||
|         return size; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static size_t fwrite_buffer_dynamic(const void *ptr, size_t eltsize, |  | ||||||
| 				    size_t nmemb, struct buffer *buffer) |  | ||||||
| { |  | ||||||
| 	size_t size = eltsize * nmemb; |  | ||||||
| 	if (size > buffer->size - buffer->posn) { |  | ||||||
| 		buffer->size = buffer->size * 3 / 2; |  | ||||||
| 		if (buffer->size < buffer->posn + size) |  | ||||||
| 			buffer->size = buffer->posn + size; |  | ||||||
| 		buffer->buffer = xrealloc(buffer->buffer, buffer->size); |  | ||||||
| 	} |  | ||||||
| 	memcpy(buffer->buffer + buffer->posn, ptr, size); |  | ||||||
| 	buffer->posn += size; |  | ||||||
| 	data_received++; |  | ||||||
| 	return size; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb, | static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb, | ||||||
| 			       void *data) | 			       void *data) | ||||||
|  | @ -243,168 +87,9 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb, | ||||||
| 	return size; | 	return size; | ||||||
| } | } | ||||||
|  |  | ||||||
| #ifdef USE_CURL_MULTI |  | ||||||
| static void process_curl_messages(void); |  | ||||||
| static void process_request_queue(void); |  | ||||||
| #endif |  | ||||||
| static void fetch_alternates(char *base); | static void fetch_alternates(char *base); | ||||||
|  |  | ||||||
| static CURL* get_curl_handle(void) | static void process_object_response(void *callback_data); | ||||||
| { |  | ||||||
| 	CURL* result = curl_easy_init(); |  | ||||||
|  |  | ||||||
| 	curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify); |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070907 |  | ||||||
| 	curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| 	if (ssl_cert != NULL) |  | ||||||
| 		curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert); |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070902 |  | ||||||
| 	if (ssl_key != NULL) |  | ||||||
| 		curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key); |  | ||||||
| #endif |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070908 |  | ||||||
| 	if (ssl_capath != NULL) |  | ||||||
| 		curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath); |  | ||||||
| #endif |  | ||||||
| 	if (ssl_cainfo != NULL) |  | ||||||
| 		curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo); |  | ||||||
| 	curl_easy_setopt(result, CURLOPT_FAILONERROR, 1); |  | ||||||
|  |  | ||||||
| 	if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) { |  | ||||||
| 		curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT, |  | ||||||
| 				 curl_low_speed_limit); |  | ||||||
| 		curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME, |  | ||||||
| 				 curl_low_speed_time); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1); |  | ||||||
|  |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static struct active_request_slot *get_active_slot(void) |  | ||||||
| { |  | ||||||
| 	struct active_request_slot *slot = active_queue_head; |  | ||||||
| 	struct active_request_slot *newslot; |  | ||||||
|  |  | ||||||
| #ifdef USE_CURL_MULTI |  | ||||||
| 	int num_transfers; |  | ||||||
|  |  | ||||||
| 	/* Wait for a slot to open up if the queue is full */ |  | ||||||
| 	while (active_requests >= max_requests) { |  | ||||||
| 		curl_multi_perform(curlm, &num_transfers); |  | ||||||
| 		if (num_transfers < active_requests) { |  | ||||||
| 			process_curl_messages(); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| 	while (slot != NULL && slot->in_use) { |  | ||||||
| 		slot = slot->next; |  | ||||||
| 	} |  | ||||||
| 	if (slot == NULL) { |  | ||||||
| 		newslot = xmalloc(sizeof(*newslot)); |  | ||||||
| 		newslot->curl = NULL; |  | ||||||
| 		newslot->in_use = 0; |  | ||||||
| 		newslot->next = NULL; |  | ||||||
|  |  | ||||||
| 		slot = active_queue_head; |  | ||||||
| 		if (slot == NULL) { |  | ||||||
| 			active_queue_head = newslot; |  | ||||||
| 		} else { |  | ||||||
| 			while (slot->next != NULL) { |  | ||||||
| 				slot = slot->next; |  | ||||||
| 			} |  | ||||||
| 			slot->next = newslot; |  | ||||||
| 		} |  | ||||||
| 		slot = newslot; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (slot->curl == NULL) { |  | ||||||
| #ifdef NO_CURL_EASY_DUPHANDLE |  | ||||||
| 		slot->curl = get_curl_handle(); |  | ||||||
| #else |  | ||||||
| 		slot->curl = curl_easy_duphandle(curl_default); |  | ||||||
| #endif |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	active_requests++; |  | ||||||
| 	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); |  | ||||||
|  |  | ||||||
| 	return slot; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int start_active_slot(struct active_request_slot *slot) |  | ||||||
| { |  | ||||||
| #ifdef USE_CURL_MULTI |  | ||||||
| 	CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl); |  | ||||||
|  |  | ||||||
| 	if (curlm_result != CURLM_OK && |  | ||||||
| 	    curlm_result != CURLM_CALL_MULTI_PERFORM) { |  | ||||||
| 		active_requests--; |  | ||||||
| 		slot->in_use = 0; |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| #endif |  | ||||||
| 	return 1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void run_active_slot(struct active_request_slot *slot) |  | ||||||
| { |  | ||||||
| #ifdef USE_CURL_MULTI |  | ||||||
| 	int num_transfers; |  | ||||||
| 	long last_pos = 0; |  | ||||||
| 	long current_pos; |  | ||||||
| 	fd_set readfds; |  | ||||||
| 	fd_set writefds; |  | ||||||
| 	fd_set excfds; |  | ||||||
| 	int max_fd; |  | ||||||
| 	struct timeval select_timeout; |  | ||||||
| 	CURLMcode curlm_result; |  | ||||||
|  |  | ||||||
| 	while (!slot->done) { |  | ||||||
| 		data_received = 0; |  | ||||||
| 		do { |  | ||||||
| 			curlm_result = curl_multi_perform(curlm, |  | ||||||
| 							  &num_transfers); |  | ||||||
| 		} while (curlm_result == CURLM_CALL_MULTI_PERFORM); |  | ||||||
| 		if (num_transfers < active_requests) { |  | ||||||
| 			process_curl_messages(); |  | ||||||
| 			process_request_queue(); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if (!data_received && slot->local != NULL) { |  | ||||||
| 			current_pos = ftell(slot->local); |  | ||||||
| 			if (current_pos > last_pos) |  | ||||||
| 				data_received++; |  | ||||||
| 			last_pos = current_pos; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if (!slot->done && !data_received) { |  | ||||||
| 			max_fd = 0; |  | ||||||
| 			FD_ZERO(&readfds); |  | ||||||
| 			FD_ZERO(&writefds); |  | ||||||
| 			FD_ZERO(&excfds); |  | ||||||
| 			select_timeout.tv_sec = 0; |  | ||||||
| 			select_timeout.tv_usec = 50000; |  | ||||||
| 			select(max_fd, &readfds, &writefds, |  | ||||||
| 			       &excfds, &select_timeout); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| #else |  | ||||||
| 	slot->curl_result = curl_easy_perform(slot->curl); |  | ||||||
| 	active_requests--; |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void start_request(struct transfer_request *request) | static void start_request(struct transfer_request *request) | ||||||
| { | { | ||||||
|  | @ -503,6 +188,10 @@ static void start_request(struct transfer_request *request) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	slot = get_active_slot(); | 	slot = get_active_slot(); | ||||||
|  | 	slot->callback_func = process_object_response; | ||||||
|  | 	slot->callback_data = request; | ||||||
|  | 	request->slot = slot; | ||||||
|  |  | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_FILE, request); | 	curl_easy_setopt(slot->curl, CURLOPT_FILE, request); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file); | 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr); | 	curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr); | ||||||
|  | @ -523,15 +212,13 @@ static void start_request(struct transfer_request *request) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/* Try to get the request started, abort the request on error */ | 	/* Try to get the request started, abort the request on error */ | ||||||
|  | 	request->state = ACTIVE; | ||||||
| 	if (!start_active_slot(slot)) { | 	if (!start_active_slot(slot)) { | ||||||
| 		request->state = ABORTED; | 		request->state = ABORTED; | ||||||
|  | 		request->slot = NULL; | ||||||
| 		close(request->local); request->local = -1; | 		close(request->local); request->local = -1; | ||||||
| 		free(request->url); | 		free(request->url); | ||||||
| 		return; |  | ||||||
| 	} | 	} | ||||||
| 	 |  | ||||||
| 	request->slot = slot; |  | ||||||
| 	request->state = ACTIVE; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void finish_request(struct transfer_request *request) | static void finish_request(struct transfer_request *request) | ||||||
|  | @ -567,6 +254,32 @@ static void finish_request(struct transfer_request *request) | ||||||
| 		pull_say("got %s\n", sha1_to_hex(request->sha1)); | 		pull_say("got %s\n", sha1_to_hex(request->sha1)); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void process_object_response(void *callback_data) | ||||||
|  | { | ||||||
|  | 	struct transfer_request *request = | ||||||
|  | 		(struct transfer_request *)callback_data; | ||||||
|  |  | ||||||
|  | 	request->curl_result = request->slot->curl_result; | ||||||
|  | 	request->http_code = request->slot->http_code; | ||||||
|  | 	request->slot = NULL; | ||||||
|  | 	request->state = COMPLETE; | ||||||
|  |  | ||||||
|  | 	/* Use alternates if necessary */ | ||||||
|  | 	if (request->http_code == 404) { | ||||||
|  | 		fetch_alternates(alt->base); | ||||||
|  | 		if (request->repo->next != NULL) { | ||||||
|  | 			request->repo = | ||||||
|  | 				request->repo->next; | ||||||
|  | 			close(request->local); | ||||||
|  | 			request->local = -1; | ||||||
|  | 			start_request(request); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	finish_request(request); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void release_request(struct transfer_request *request) | static void release_request(struct transfer_request *request) | ||||||
| { | { | ||||||
| 	struct transfer_request *entry = request_queue_head; | 	struct transfer_request *entry = request_queue_head; | ||||||
|  | @ -587,73 +300,7 @@ static void release_request(struct transfer_request *request) | ||||||
| } | } | ||||||
|  |  | ||||||
| #ifdef USE_CURL_MULTI | #ifdef USE_CURL_MULTI | ||||||
| static void process_curl_messages(void) | void fill_active_slots(void) | ||||||
| { |  | ||||||
| 	int num_messages; |  | ||||||
| 	struct active_request_slot *slot; |  | ||||||
| 	struct transfer_request *request = NULL; |  | ||||||
| 	CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages); |  | ||||||
|  |  | ||||||
| 	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) |  | ||||||
| 				slot = slot->next; |  | ||||||
| 			if (slot != NULL) { |  | ||||||
| 				curl_multi_remove_handle(curlm, slot->curl); |  | ||||||
| 				active_requests--; |  | ||||||
| 				slot->done = 1; |  | ||||||
| 				slot->in_use = 0; |  | ||||||
| 				slot->curl_result = curl_result; |  | ||||||
| 				curl_easy_getinfo(slot->curl, |  | ||||||
| 						  CURLINFO_HTTP_CODE, |  | ||||||
| 						  &slot->http_code); |  | ||||||
| 				request = request_queue_head; |  | ||||||
| 				while (request != NULL && |  | ||||||
| 				       request->slot != slot) |  | ||||||
| 					request = request->next; |  | ||||||
| 			} 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_result; |  | ||||||
| 				request->http_code = slot->http_code; |  | ||||||
| 				request->slot = NULL; |  | ||||||
| 				request->state = COMPLETE; |  | ||||||
|  |  | ||||||
| 				/* Use alternates if necessary */ |  | ||||||
| 				if (request->http_code == 404) { |  | ||||||
| 					fetch_alternates(alt->base); |  | ||||||
| 					if (request->repo->next != NULL) { |  | ||||||
| 						request->repo = |  | ||||||
| 							request->repo->next; |  | ||||||
| 						close(request->local); |  | ||||||
| 							request->local = -1; |  | ||||||
| 						start_request(request); |  | ||||||
| 					} else { |  | ||||||
| 						finish_request(request); |  | ||||||
| 					} |  | ||||||
| 				} else { |  | ||||||
| 					finish_request(request); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			fprintf(stderr, "Unknown CURL message received: %d\n", |  | ||||||
| 				(int)curl_message->msg); |  | ||||||
| 		} |  | ||||||
| 		curl_message = curl_multi_info_read(curlm, &num_messages); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void process_request_queue(void) |  | ||||||
| { | { | ||||||
| 	struct transfer_request *request = request_queue_head; | 	struct transfer_request *request = request_queue_head; | ||||||
| 	struct active_request_slot *slot = active_queue_head; | 	struct active_request_slot *slot = active_queue_head; | ||||||
|  | @ -706,9 +353,10 @@ void prefetch(unsigned char *sha1) | ||||||
| 		} | 		} | ||||||
| 		tail->next = newreq; | 		tail->next = newreq; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| #ifdef USE_CURL_MULTI | #ifdef USE_CURL_MULTI | ||||||
| 	process_request_queue(); | 	fill_active_slots(); | ||||||
| 	process_curl_messages(); | 	step_active_slots(); | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -815,12 +463,11 @@ static void process_alternates(void *callback_data) | ||||||
| 					 alt_req->url); | 					 alt_req->url); | ||||||
| 			active_requests++; | 			active_requests++; | ||||||
| 			slot->in_use = 1; | 			slot->in_use = 1; | ||||||
| 			slot->done = 0; |  | ||||||
| 			if (start_active_slot(slot)) { | 			if (start_active_slot(slot)) { | ||||||
| 				return; | 				return; | ||||||
| 			} else { | 			} else { | ||||||
| 				got_alternates = -1; | 				got_alternates = -1; | ||||||
| 				slot->done = 1; | 				slot->in_use = 0; | ||||||
| 				return; | 				return; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | @ -831,7 +478,7 @@ static void process_alternates(void *callback_data) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	fwrite_buffer_dynamic(&null_byte, 1, 1, alt_req->buffer); | 	fwrite_buffer(&null_byte, 1, 1, alt_req->buffer); | ||||||
| 	alt_req->buffer->posn--; | 	alt_req->buffer->posn--; | ||||||
| 	data = alt_req->buffer->buffer; | 	data = alt_req->buffer->buffer; | ||||||
|  |  | ||||||
|  | @ -902,16 +549,15 @@ static void fetch_alternates(char *base) | ||||||
| 	char *data; | 	char *data; | ||||||
| 	struct active_request_slot *slot; | 	struct active_request_slot *slot; | ||||||
| 	static struct alt_request alt_req; | 	static struct alt_request alt_req; | ||||||
| 	int num_transfers; |  | ||||||
|  |  | ||||||
| 	/* If another request has already started fetching alternates, | 	/* If another request has already started fetching alternates, | ||||||
| 	   wait for them to arrive and return to processing this request's | 	   wait for them to arrive and return to processing this request's | ||||||
| 	   curl message */ | 	   curl message */ | ||||||
|  | #ifdef USE_CURL_MULTI | ||||||
| 	while (got_alternates == 0) { | 	while (got_alternates == 0) { | ||||||
| 		curl_multi_perform(curlm, &num_transfers); | 		step_active_slots(); | ||||||
| 		process_curl_messages(); |  | ||||||
| 		process_request_queue(); |  | ||||||
| 	} | 	} | ||||||
|  | #endif | ||||||
|  |  | ||||||
| 	/* Nothing to do if they've already been fetched */ | 	/* Nothing to do if they've already been fetched */ | ||||||
| 	if (got_alternates == 1) | 	if (got_alternates == 1) | ||||||
|  | @ -938,8 +584,7 @@ static void fetch_alternates(char *base) | ||||||
| 	slot->callback_data = &alt_req; | 	slot->callback_data = &alt_req; | ||||||
|  |  | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); | 	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, | 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); | ||||||
| 			 fwrite_buffer_dynamic); |  | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_URL, url); | 	curl_easy_setopt(slot->curl, CURLOPT_URL, url); | ||||||
|  |  | ||||||
| 	alt_req.base = base; | 	alt_req.base = base; | ||||||
|  | @ -983,8 +628,7 @@ static int fetch_indices(struct alt_base *repo) | ||||||
|  |  | ||||||
| 	slot = get_active_slot(); | 	slot = get_active_slot(); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); | 	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, | 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); | ||||||
| 			 fwrite_buffer_dynamic); |  | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_URL, url); | 	curl_easy_setopt(slot->curl, CURLOPT_URL, url); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL); | 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL); | ||||||
| 	if (start_active_slot(slot)) { | 	if (start_active_slot(slot)) { | ||||||
|  | @ -1115,7 +759,7 @@ static int fetch_pack(struct alt_base *repo, unsigned char *sha1) | ||||||
| static int fetch_object(struct alt_base *repo, unsigned char *sha1) | static int fetch_object(struct alt_base *repo, unsigned char *sha1) | ||||||
| { | { | ||||||
| 	char *hex = sha1_to_hex(sha1); | 	char *hex = sha1_to_hex(sha1); | ||||||
| 	int ret; | 	int ret = 0; | ||||||
| 	struct transfer_request *request = request_queue_head; | 	struct transfer_request *request = request_queue_head; | ||||||
|  |  | ||||||
| 	while (request != NULL && memcmp(request->sha1, sha1, 20)) | 	while (request != NULL && memcmp(request->sha1, sha1, 20)) | ||||||
|  | @ -1130,12 +774,7 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1) | ||||||
|  |  | ||||||
| #ifdef USE_CURL_MULTI | #ifdef USE_CURL_MULTI | ||||||
| 	while (request->state == WAITING) { | 	while (request->state == WAITING) { | ||||||
| 		int num_transfers; | 		step_active_slots(); | ||||||
| 		curl_multi_perform(curlm, &num_transfers); |  | ||||||
| 		if (num_transfers < active_requests) { |  | ||||||
| 			process_curl_messages(); |  | ||||||
| 			process_request_queue(); |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| #else | #else | ||||||
| 	start_request(request); | 	start_request(request); | ||||||
|  | @ -1143,66 +782,33 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1) | ||||||
|  |  | ||||||
| 	while (request->state == ACTIVE) { | 	while (request->state == ACTIVE) { | ||||||
| 		run_active_slot(request->slot); | 		run_active_slot(request->slot); | ||||||
| #ifndef USE_CURL_MULTI |  | ||||||
| 		request->curl_result = request->slot->curl_result; |  | ||||||
| 		request->http_code = request->slot->http_code; |  | ||||||
| 		request->slot = NULL; |  | ||||||
|  |  | ||||||
| 		/* Use alternates if necessary */ |  | ||||||
| 		if (request->http_code == 404) { |  | ||||||
| 			fetch_alternates(alt->base); |  | ||||||
| 			if (request->repo->next != NULL) { |  | ||||||
| 				request->repo = request->repo->next; |  | ||||||
| 				close(request->local); request->local = -1; |  | ||||||
| 				start_request(request); |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			finish_request(request); |  | ||||||
| 			request->state = COMPLETE; |  | ||||||
| 		} |  | ||||||
| #endif |  | ||||||
| 	} | 	} | ||||||
| 	if (request->local != -1) { | 	if (request->local != -1) { | ||||||
| 		close(request->local); request->local = -1; | 		close(request->local); request->local = -1; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (request->state == ABORTED) { | 	if (request->state == ABORTED) { | ||||||
| 		release_request(request); | 		ret = error("Request for %s aborted", hex); | ||||||
| 		return error("Request for %s aborted", hex); | 	} else if (request->curl_result != CURLE_OK && | ||||||
| 	} | 		   request->http_code != 416) { | ||||||
|  |  | ||||||
| 	if (request->curl_result != CURLE_OK && request->http_code != 416) { |  | ||||||
| 		if (request->http_code == 404) | 		if (request->http_code == 404) | ||||||
| 			ret = -1; /* Be silent, it is probably in a pack. */ | 			ret = -1; /* Be silent, it is probably in a pack. */ | ||||||
| 		else | 		else | ||||||
| 			ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)", | 			ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)", | ||||||
| 				    request->errorstr, request->curl_result, | 				    request->errorstr, request->curl_result, | ||||||
| 				    request->http_code, hex); | 				    request->http_code, hex); | ||||||
| 		release_request(request); | 	} else if (request->zret != Z_STREAM_END) { | ||||||
| 		return ret; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (request->zret != Z_STREAM_END) { |  | ||||||
| 		ret = error("File %s (%s) corrupt\n", hex, request->url); | 		ret = error("File %s (%s) corrupt\n", hex, request->url); | ||||||
| 		release_request(request); | 	} else if (memcmp(request->sha1, request->real_sha1, 20)) { | ||||||
| 		return ret; | 		ret = error("File %s has bad hash\n", hex); | ||||||
| 	} | 	} else if (request->rename < 0) { | ||||||
|  |  | ||||||
| 	if (memcmp(request->sha1, request->real_sha1, 20)) { |  | ||||||
| 		release_request(request); |  | ||||||
| 		return error("File %s has bad hash\n", hex); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (request->rename < 0) { |  | ||||||
| 		ret = error("unable to write sha1 filename %s: %s", | 		ret = error("unable to write sha1 filename %s: %s", | ||||||
| 			    request->filename, | 			    request->filename, | ||||||
| 			    strerror(request->rename)); | 			    strerror(request->rename)); | ||||||
| 		release_request(request); |  | ||||||
| 		return ret; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	release_request(request); | 	release_request(request); | ||||||
| 	return 0; | 	return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| int fetch(unsigned char *sha1) | int fetch(unsigned char *sha1) | ||||||
|  | @ -1303,10 +909,6 @@ int main(int argc, char **argv) | ||||||
| 	char *commit_id; | 	char *commit_id; | ||||||
| 	char *url; | 	char *url; | ||||||
| 	int arg = 1; | 	int arg = 1; | ||||||
| 	struct active_request_slot *slot; |  | ||||||
| 	char *low_speed_limit; |  | ||||||
| 	char *low_speed_time; |  | ||||||
| 	char *wait_url; |  | ||||||
| 	int rc = 0; | 	int rc = 0; | ||||||
|  |  | ||||||
| 	while (arg < argc && argv[arg][0] == '-') { | 	while (arg < argc && argv[arg][0] == '-') { | ||||||
|  | @ -1335,58 +937,9 @@ int main(int argc, char **argv) | ||||||
| 	commit_id = argv[arg]; | 	commit_id = argv[arg]; | ||||||
| 	url = argv[arg + 1]; | 	url = argv[arg + 1]; | ||||||
|  |  | ||||||
| 	curl_global_init(CURL_GLOBAL_ALL); | 	http_init(); | ||||||
|  |  | ||||||
| #ifdef USE_CURL_MULTI |  | ||||||
| 	{ |  | ||||||
| 		char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS"); |  | ||||||
| 		if (http_max_requests != NULL) |  | ||||||
| 			max_requests = atoi(http_max_requests); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	curlm = curl_multi_init(); |  | ||||||
| 	if (curlm == NULL) { |  | ||||||
| 		fprintf(stderr, "Error creating curl multi handle.\n"); |  | ||||||
| 		return 1; |  | ||||||
| 	} |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| 	if (getenv("GIT_SSL_NO_VERIFY")) |  | ||||||
| 		curl_ssl_verify = 0; |  | ||||||
|  |  | ||||||
| 	ssl_cert = getenv("GIT_SSL_CERT"); |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070902 |  | ||||||
| 	ssl_key = getenv("GIT_SSL_KEY"); |  | ||||||
| #endif |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070908 |  | ||||||
| 	ssl_capath = getenv("GIT_SSL_CAPATH"); |  | ||||||
| #endif |  | ||||||
| 	ssl_cainfo = getenv("GIT_SSL_CAINFO"); |  | ||||||
|  |  | ||||||
| 	low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT"); |  | ||||||
| 	if (low_speed_limit != NULL) |  | ||||||
| 		curl_low_speed_limit = strtol(low_speed_limit, NULL, 10); |  | ||||||
| 	low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME"); |  | ||||||
| 	if (low_speed_time != NULL) |  | ||||||
| 		curl_low_speed_time = strtol(low_speed_time, NULL, 10); |  | ||||||
|  |  | ||||||
| 	git_config(http_options); |  | ||||||
|  |  | ||||||
| 	if (curl_ssl_verify == -1) |  | ||||||
| 		curl_ssl_verify = 1; |  | ||||||
|  |  | ||||||
| #ifdef USE_CURL_MULTI |  | ||||||
| 	if (max_requests < 1) |  | ||||||
| 		max_requests = DEFAULT_MAX_REQUESTS; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| 	pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache"); |  | ||||||
| 	no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:"); | 	no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:"); | ||||||
| 	no_range_header = curl_slist_append(no_range_header, "Range:"); |  | ||||||
|  |  | ||||||
| #ifndef NO_CURL_EASY_DUPHANDLE |  | ||||||
| 	curl_default = get_curl_handle(); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| 	alt = xmalloc(sizeof(*alt)); | 	alt = xmalloc(sizeof(*alt)); | ||||||
| 	alt->base = url; | 	alt->base = url; | ||||||
|  | @ -1397,30 +950,9 @@ int main(int argc, char **argv) | ||||||
| 	if (pull(commit_id)) | 	if (pull(commit_id)) | ||||||
| 		rc = 1; | 		rc = 1; | ||||||
|  |  | ||||||
| 	curl_slist_free_all(pragma_header); |  | ||||||
| 	curl_slist_free_all(no_pragma_header); | 	curl_slist_free_all(no_pragma_header); | ||||||
| 	curl_slist_free_all(no_range_header); |  | ||||||
| #ifndef NO_CURL_EASY_DUPHANDLE | 	http_cleanup(); | ||||||
| 	curl_easy_cleanup(curl_default); |  | ||||||
| #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; |  | ||||||
| 	} |  | ||||||
| #ifdef USE_CURL_MULTI |  | ||||||
| 	curl_multi_cleanup(curlm); |  | ||||||
| #endif |  | ||||||
| 	curl_global_cleanup(); |  | ||||||
| 	return rc; | 	return rc; | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										486
									
								
								http-push.c
								
								
								
								
							
							
						
						
									
										486
									
								
								http-push.c
								
								
								
								
							|  | @ -4,30 +4,15 @@ | ||||||
| #include "fetch.h" | #include "fetch.h" | ||||||
| #include "tag.h" | #include "tag.h" | ||||||
| #include "blob.h" | #include "blob.h" | ||||||
|  | #include "http.h" | ||||||
|  |  | ||||||
|  | #ifdef USE_CURL_MULTI | ||||||
|  |  | ||||||
| #include <curl/curl.h> |  | ||||||
| #include <curl/easy.h> |  | ||||||
| #include <expat.h> | #include <expat.h> | ||||||
|  |  | ||||||
| static const char http_push_usage[] = | static const char http_push_usage[] = | ||||||
| "git-http-push [--complete] [--force] [--verbose] <url> <ref> [<ref>...]\n"; | "git-http-push [--complete] [--force] [--verbose] <url> <ref> [<ref>...]\n"; | ||||||
|  |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070908 |  | ||||||
| #define USE_CURL_MULTI |  | ||||||
| #define DEFAULT_MAX_REQUESTS 5 |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #if LIBCURL_VERSION_NUM < 0x070704 |  | ||||||
| #define curl_global_cleanup() do { /* nothing */ } while(0) |  | ||||||
| #endif |  | ||||||
| #if LIBCURL_VERSION_NUM < 0x070800 |  | ||||||
| #define curl_global_init(a) do { /* nothing */ } while(0) |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #if LIBCURL_VERSION_NUM < 0x070c04 |  | ||||||
| #define NO_CURL_EASY_DUPHANDLE |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifndef XML_STATUS_OK | #ifndef XML_STATUS_OK | ||||||
| enum XML_Status { | enum XML_Status { | ||||||
|   XML_STATUS_OK = 1, |   XML_STATUS_OK = 1, | ||||||
|  | @ -52,34 +37,17 @@ enum XML_Status { | ||||||
| #define LOCK_TIME 600 | #define LOCK_TIME 600 | ||||||
| #define LOCK_REFRESH 30 | #define LOCK_REFRESH 30 | ||||||
|  |  | ||||||
| static int active_requests = 0; |  | ||||||
| static int data_received; |  | ||||||
| static int pushing = 0; | static int pushing = 0; | ||||||
| static int aborted = 0; | static int aborted = 0; | ||||||
| static char remote_dir_exists[256]; | static char remote_dir_exists[256]; | ||||||
|  |  | ||||||
| #ifdef USE_CURL_MULTI |  | ||||||
| static int max_requests = -1; |  | ||||||
| static CURLM *curlm; |  | ||||||
| #endif |  | ||||||
| #ifndef NO_CURL_EASY_DUPHANDLE |  | ||||||
| static CURL *curl_default; |  | ||||||
| #endif |  | ||||||
| static struct curl_slist *no_pragma_header; | static struct curl_slist *no_pragma_header; | ||||||
| static struct curl_slist *default_headers; | static struct curl_slist *default_headers; | ||||||
| static char curl_errorstr[CURL_ERROR_SIZE]; |  | ||||||
|  |  | ||||||
| static int push_verbosely = 0; | static int push_verbosely = 0; | ||||||
| static int push_all = 0; | static int push_all = 0; | ||||||
| static int force_all = 0; | static int force_all = 0; | ||||||
|  |  | ||||||
| struct buffer |  | ||||||
| { |  | ||||||
|         size_t posn; |  | ||||||
|         size_t size; |  | ||||||
|         void *buffer; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct repo | struct repo | ||||||
| { | { | ||||||
| 	char *url; | 	char *url; | ||||||
|  | @ -122,31 +90,7 @@ struct transfer_request | ||||||
| 	struct transfer_request *next; | 	struct transfer_request *next; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct active_request_slot |  | ||||||
| { |  | ||||||
| 	CURL *curl; |  | ||||||
| 	FILE *local; |  | ||||||
| 	int in_use; |  | ||||||
| 	int done; |  | ||||||
| 	CURLcode curl_result; |  | ||||||
| 	long http_code; |  | ||||||
| 	struct active_request_slot *next; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static struct transfer_request *request_queue_head = NULL; | static struct transfer_request *request_queue_head = NULL; | ||||||
| static struct active_request_slot *active_queue_head = NULL; |  | ||||||
|  |  | ||||||
| static int curl_ssl_verify = -1; |  | ||||||
| static char *ssl_cert = NULL; |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070902 |  | ||||||
| static char *ssl_key = NULL; |  | ||||||
| #endif |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070908 |  | ||||||
| static char *ssl_capath = NULL; |  | ||||||
| #endif |  | ||||||
| static char *ssl_cainfo = NULL; |  | ||||||
| static long curl_low_speed_limit = -1; |  | ||||||
| static long curl_low_speed_time = -1; |  | ||||||
|  |  | ||||||
| struct active_lock | struct active_lock | ||||||
| { | { | ||||||
|  | @ -174,260 +118,14 @@ struct lockprop | ||||||
| 	int lock_exclusive_write; | 	int lock_exclusive_write; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static int http_options(const char *var, const char *value) | static void finish_request(struct transfer_request *request); | ||||||
|  |  | ||||||
|  | static void process_response(void *callback_data) | ||||||
| { | { | ||||||
| 	if (!strcmp("http.sslverify", var)) { | 	struct transfer_request *request = | ||||||
| 		if (curl_ssl_verify == -1) { | 		(struct transfer_request *)callback_data; | ||||||
| 			curl_ssl_verify = git_config_bool(var, value); |  | ||||||
| 		} |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (!strcmp("http.sslcert", var)) { | 	finish_request(request); | ||||||
| 		if (ssl_cert == NULL) { |  | ||||||
| 			ssl_cert = xmalloc(strlen(value)+1); |  | ||||||
| 			strcpy(ssl_cert, value); |  | ||||||
| 		} |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070902 |  | ||||||
| 	if (!strcmp("http.sslkey", var)) { |  | ||||||
| 		if (ssl_key == NULL) { |  | ||||||
| 			ssl_key = xmalloc(strlen(value)+1); |  | ||||||
| 			strcpy(ssl_key, value); |  | ||||||
| 		} |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| #endif |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070908 |  | ||||||
| 	if (!strcmp("http.sslcapath", var)) { |  | ||||||
| 		if (ssl_capath == NULL) { |  | ||||||
| 			ssl_capath = xmalloc(strlen(value)+1); |  | ||||||
| 			strcpy(ssl_capath, value); |  | ||||||
| 		} |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| #endif |  | ||||||
| 	if (!strcmp("http.sslcainfo", var)) { |  | ||||||
| 		if (ssl_cainfo == NULL) { |  | ||||||
| 			ssl_cainfo = xmalloc(strlen(value)+1); |  | ||||||
| 			strcpy(ssl_cainfo, value); |  | ||||||
| 		} |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| #ifdef USE_CURL_MULTI	 |  | ||||||
| 	if (!strcmp("http.maxrequests", var)) { |  | ||||||
| 		if (max_requests == -1) |  | ||||||
| 			max_requests = git_config_int(var, value); |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| 	if (!strcmp("http.lowspeedlimit", var)) { |  | ||||||
| 		if (curl_low_speed_limit == -1) |  | ||||||
| 			curl_low_speed_limit = (long)git_config_int(var, value); |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| 	if (!strcmp("http.lowspeedtime", var)) { |  | ||||||
| 		if (curl_low_speed_time == -1) |  | ||||||
| 			curl_low_speed_time = (long)git_config_int(var, value); |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* Fall back on the default ones */ |  | ||||||
| 	return git_default_config(var, value); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, |  | ||||||
| 			   struct buffer *buffer) |  | ||||||
| { |  | ||||||
| 	size_t size = eltsize * nmemb; |  | ||||||
| 	if (size > buffer->size - buffer->posn) |  | ||||||
| 		size = buffer->size - buffer->posn; |  | ||||||
| 	memcpy(ptr, buffer->buffer + buffer->posn, size); |  | ||||||
| 	buffer->posn += size; |  | ||||||
| 	return size; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static size_t fwrite_buffer_dynamic(const void *ptr, size_t eltsize, |  | ||||||
| 				    size_t nmemb, struct buffer *buffer) |  | ||||||
| { |  | ||||||
| 	size_t size = eltsize * nmemb; |  | ||||||
| 	if (size > buffer->size - buffer->posn) { |  | ||||||
| 		buffer->size = buffer->size * 3 / 2; |  | ||||||
| 		if (buffer->size < buffer->posn + size) |  | ||||||
| 			buffer->size = buffer->posn + size; |  | ||||||
| 		buffer->buffer = xrealloc(buffer->buffer, buffer->size); |  | ||||||
| 	} |  | ||||||
| 	memcpy(buffer->buffer + buffer->posn, ptr, size); |  | ||||||
| 	buffer->posn += size; |  | ||||||
| 	data_received++; |  | ||||||
| 	return size; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static size_t fwrite_null(const void *ptr, size_t eltsize, |  | ||||||
| 			  size_t nmemb, struct buffer *buffer) |  | ||||||
| { |  | ||||||
| 	data_received++; |  | ||||||
| 	return eltsize * nmemb; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #ifdef USE_CURL_MULTI |  | ||||||
| static void process_curl_messages(void); |  | ||||||
| static void process_request_queue(void); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| static CURL* get_curl_handle(void) |  | ||||||
| { |  | ||||||
| 	CURL* result = curl_easy_init(); |  | ||||||
|  |  | ||||||
| 	curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify); |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070907 |  | ||||||
| 	curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| 	if (ssl_cert != NULL) |  | ||||||
| 		curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert); |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070902 |  | ||||||
| 	if (ssl_key != NULL) |  | ||||||
| 		curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key); |  | ||||||
| #endif |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070908 |  | ||||||
| 	if (ssl_capath != NULL) |  | ||||||
| 		curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath); |  | ||||||
| #endif |  | ||||||
| 	if (ssl_cainfo != NULL) |  | ||||||
| 		curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo); |  | ||||||
| 	curl_easy_setopt(result, CURLOPT_FAILONERROR, 1); |  | ||||||
|  |  | ||||||
| 	if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) { |  | ||||||
| 		curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT, |  | ||||||
| 				 curl_low_speed_limit); |  | ||||||
| 		curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME, |  | ||||||
| 				 curl_low_speed_time); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static struct active_request_slot *get_active_slot(void) |  | ||||||
| { |  | ||||||
| 	struct active_request_slot *slot = active_queue_head; |  | ||||||
| 	struct active_request_slot *newslot; |  | ||||||
|  |  | ||||||
| #ifdef USE_CURL_MULTI |  | ||||||
| 	int num_transfers; |  | ||||||
|  |  | ||||||
| 	/* Wait for a slot to open up if the queue is full */ |  | ||||||
| 	while (active_requests >= max_requests) { |  | ||||||
| 		curl_multi_perform(curlm, &num_transfers); |  | ||||||
| 		if (num_transfers < active_requests) { |  | ||||||
| 			process_curl_messages(); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| 	while (slot != NULL && slot->in_use) { |  | ||||||
| 		slot = slot->next; |  | ||||||
| 	} |  | ||||||
| 	if (slot == NULL) { |  | ||||||
| 		newslot = xmalloc(sizeof(*newslot)); |  | ||||||
| 		newslot->curl = NULL; |  | ||||||
| 		newslot->in_use = 0; |  | ||||||
| 		newslot->next = NULL; |  | ||||||
|  |  | ||||||
| 		slot = active_queue_head; |  | ||||||
| 		if (slot == NULL) { |  | ||||||
| 			active_queue_head = newslot; |  | ||||||
| 		} else { |  | ||||||
| 			while (slot->next != NULL) { |  | ||||||
| 				slot = slot->next; |  | ||||||
| 			} |  | ||||||
| 			slot->next = newslot; |  | ||||||
| 		} |  | ||||||
| 		slot = newslot; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (slot->curl == NULL) { |  | ||||||
| #ifdef NO_CURL_EASY_DUPHANDLE |  | ||||||
| 		slot->curl = get_curl_handle(); |  | ||||||
| #else |  | ||||||
| 		slot->curl = curl_easy_duphandle(curl_default); |  | ||||||
| #endif |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	active_requests++; |  | ||||||
| 	slot->in_use = 1; |  | ||||||
| 	slot->done = 0; |  | ||||||
| 	slot->local = NULL; |  | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, default_headers); |  | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr); |  | ||||||
|  |  | ||||||
| 	return slot; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int start_active_slot(struct active_request_slot *slot) |  | ||||||
| { |  | ||||||
| #ifdef USE_CURL_MULTI |  | ||||||
| 	CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl); |  | ||||||
|  |  | ||||||
| 	if (curlm_result != CURLM_OK && |  | ||||||
| 	    curlm_result != CURLM_CALL_MULTI_PERFORM) { |  | ||||||
| 		active_requests--; |  | ||||||
| 		slot->in_use = 0; |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| #endif |  | ||||||
| 	return 1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void run_active_slot(struct active_request_slot *slot) |  | ||||||
| { |  | ||||||
| #ifdef USE_CURL_MULTI |  | ||||||
| 	int num_transfers; |  | ||||||
| 	long last_pos = 0; |  | ||||||
| 	long current_pos; |  | ||||||
| 	fd_set readfds; |  | ||||||
| 	fd_set writefds; |  | ||||||
| 	fd_set excfds; |  | ||||||
| 	int max_fd; |  | ||||||
| 	struct timeval select_timeout; |  | ||||||
| 	CURLMcode curlm_result; |  | ||||||
|  |  | ||||||
| 	while (!slot->done) { |  | ||||||
| 		data_received = 0; |  | ||||||
| 		do { |  | ||||||
| 			curlm_result = curl_multi_perform(curlm, |  | ||||||
| 							  &num_transfers); |  | ||||||
| 		} while (curlm_result == CURLM_CALL_MULTI_PERFORM); |  | ||||||
| 		if (num_transfers < active_requests) { |  | ||||||
| 			process_curl_messages(); |  | ||||||
| 			process_request_queue(); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if (!data_received && slot->local != NULL) { |  | ||||||
| 			current_pos = ftell(slot->local); |  | ||||||
| 			if (current_pos > last_pos) |  | ||||||
| 				data_received++; |  | ||||||
| 			last_pos = current_pos; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if (!slot->done && !data_received) { |  | ||||||
| 			max_fd = 0; |  | ||||||
| 			FD_ZERO(&readfds); |  | ||||||
| 			FD_ZERO(&writefds); |  | ||||||
| 			FD_ZERO(&excfds); |  | ||||||
| 			select_timeout.tv_sec = 0; |  | ||||||
| 			select_timeout.tv_usec = 50000; |  | ||||||
| 			select(max_fd, &readfds, &writefds, |  | ||||||
| 			       &excfds, &select_timeout); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| #else |  | ||||||
| 	slot->curl_result = curl_easy_perform(slot->curl); |  | ||||||
| 	active_requests--; |  | ||||||
| #endif |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void start_check(struct transfer_request *request) | static void start_check(struct transfer_request *request) | ||||||
|  | @ -447,6 +145,8 @@ static void start_check(struct transfer_request *request) | ||||||
| 	strcpy(posn, hex + 2); | 	strcpy(posn, hex + 2); | ||||||
|  |  | ||||||
| 	slot = get_active_slot(); | 	slot = get_active_slot(); | ||||||
|  | 	slot->callback_func = process_response; | ||||||
|  | 	slot->callback_data = request; | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr); | 	curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_URL, request->url); | 	curl_easy_setopt(slot->curl, CURLOPT_URL, request->url); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1); | 	curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1); | ||||||
|  | @ -476,6 +176,8 @@ static void start_mkcol(struct transfer_request *request) | ||||||
| 	strcpy(posn, "/"); | 	strcpy(posn, "/"); | ||||||
|  |  | ||||||
| 	slot = get_active_slot(); | 	slot = get_active_slot(); | ||||||
|  | 	slot->callback_func = process_response; | ||||||
|  | 	slot->callback_data = request; | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */ | 	curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */ | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_URL, request->url); | 	curl_easy_setopt(slot->curl, CURLOPT_URL, request->url); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr); | 	curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr); | ||||||
|  | @ -553,6 +255,8 @@ static void start_put(struct transfer_request *request) | ||||||
| 	strcpy(posn, request->lock->token); | 	strcpy(posn, request->lock->token); | ||||||
|  |  | ||||||
| 	slot = get_active_slot(); | 	slot = get_active_slot(); | ||||||
|  | 	slot->callback_func = process_response; | ||||||
|  | 	slot->callback_data = request; | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_INFILE, &request->buffer); | 	curl_easy_setopt(slot->curl, CURLOPT_INFILE, &request->buffer); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, request->buffer.size); | 	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, request->buffer.size); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); | 	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); | ||||||
|  | @ -578,6 +282,8 @@ static void start_move(struct transfer_request *request) | ||||||
| 	struct curl_slist *dav_headers = NULL; | 	struct curl_slist *dav_headers = NULL; | ||||||
|  |  | ||||||
| 	slot = get_active_slot(); | 	slot = get_active_slot(); | ||||||
|  | 	slot->callback_func = process_response; | ||||||
|  | 	slot->callback_data = request; | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */ | 	curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */ | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MOVE); | 	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MOVE); | ||||||
| 	dav_headers = curl_slist_append(dav_headers, request->dest); | 	dav_headers = curl_slist_append(dav_headers, request->dest); | ||||||
|  | @ -725,48 +431,7 @@ static void release_request(struct transfer_request *request) | ||||||
| 	free(request); | 	free(request); | ||||||
| } | } | ||||||
|  |  | ||||||
| #ifdef USE_CURL_MULTI | void fill_active_slots(void) | ||||||
| static void process_curl_messages(void) |  | ||||||
| { |  | ||||||
| 	int num_messages; |  | ||||||
| 	struct active_request_slot *slot; |  | ||||||
| 	struct transfer_request *request = NULL; |  | ||||||
| 	CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages); |  | ||||||
|  |  | ||||||
| 	while (curl_message != NULL) { |  | ||||||
| 		if (curl_message->msg == CURLMSG_DONE) { |  | ||||||
| 			slot = active_queue_head; |  | ||||||
| 			while (slot != NULL && |  | ||||||
| 			       slot->curl != curl_message->easy_handle) |  | ||||||
| 				slot = slot->next; |  | ||||||
| 			if (slot != NULL) { |  | ||||||
| 				int curl_result = curl_message->data.result; |  | ||||||
| 				curl_multi_remove_handle(curlm, slot->curl); |  | ||||||
| 				active_requests--; |  | ||||||
| 				slot->done = 1; |  | ||||||
| 				slot->in_use = 0; |  | ||||||
| 				slot->curl_result = curl_result; |  | ||||||
| 				curl_easy_getinfo(slot->curl, |  | ||||||
| 						  CURLINFO_HTTP_CODE, |  | ||||||
| 						  &slot->http_code); |  | ||||||
| 				request = request_queue_head; |  | ||||||
| 				while (request != NULL && |  | ||||||
| 				       request->slot != slot) |  | ||||||
| 					request = request->next; |  | ||||||
| 				if (request != NULL) |  | ||||||
| 					finish_request(request); |  | ||||||
| 			} else { |  | ||||||
| 				fprintf(stderr, "Received DONE message for unknown request!\n"); |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			fprintf(stderr, "Unknown CURL message received: %d\n", |  | ||||||
| 				(int)curl_message->msg); |  | ||||||
| 		} |  | ||||||
| 		curl_message = curl_multi_info_read(curlm, &num_messages); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void process_request_queue(void) |  | ||||||
| { | { | ||||||
| 	struct transfer_request *request = request_queue_head; | 	struct transfer_request *request = request_queue_head; | ||||||
| 	struct active_request_slot *slot = active_queue_head; | 	struct active_request_slot *slot = active_queue_head; | ||||||
|  | @ -797,20 +462,6 @@ static void process_request_queue(void) | ||||||
| 		slot = slot->next; | 		slot = slot->next; | ||||||
| 	}				 | 	}				 | ||||||
| } | } | ||||||
| #endif |  | ||||||
|  |  | ||||||
| static void process_waiting_requests(void) |  | ||||||
| { |  | ||||||
| 	struct active_request_slot *slot = active_queue_head; |  | ||||||
|  |  | ||||||
| 	while (slot != NULL) |  | ||||||
| 		if (slot->in_use) { |  | ||||||
| 			run_active_slot(slot); |  | ||||||
| 			slot = active_queue_head; |  | ||||||
| 		} else { |  | ||||||
| 			slot = slot->next; |  | ||||||
| 		} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void add_request(unsigned char *sha1, struct active_lock *lock) | static void add_request(unsigned char *sha1, struct active_lock *lock) | ||||||
| { | { | ||||||
|  | @ -834,10 +485,9 @@ static void add_request(unsigned char *sha1, struct active_lock *lock) | ||||||
| 	request->state = NEED_CHECK; | 	request->state = NEED_CHECK; | ||||||
| 	request->next = request_queue_head; | 	request->next = request_queue_head; | ||||||
| 	request_queue_head = request; | 	request_queue_head = request; | ||||||
| #ifdef USE_CURL_MULTI |  | ||||||
| 	process_request_queue(); | 	fill_active_slots(); | ||||||
| 	process_curl_messages(); | 	step_active_slots(); | ||||||
| #endif |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int fetch_index(unsigned char *sha1) | static int fetch_index(unsigned char *sha1) | ||||||
|  | @ -963,8 +613,7 @@ static int fetch_indices(void) | ||||||
|  |  | ||||||
| 	slot = get_active_slot(); | 	slot = get_active_slot(); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); | 	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, | 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); | ||||||
| 			 fwrite_buffer_dynamic); |  | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_URL, url); | 	curl_easy_setopt(slot->curl, CURLOPT_URL, url); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL); | 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL); | ||||||
| 	if (start_active_slot(slot)) { | 	if (start_active_slot(slot)) { | ||||||
|  | @ -1068,8 +717,7 @@ int fetch_ref(char *ref, unsigned char *sha1) | ||||||
| 	url = quote_ref_url(base, ref); | 	url = quote_ref_url(base, ref); | ||||||
| 	slot = get_active_slot(); | 	slot = get_active_slot(); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); | 	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, | 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); | ||||||
| 			 fwrite_buffer_dynamic); |  | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL); | 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_URL, url); | 	curl_easy_setopt(slot->curl, CURLOPT_URL, url); | ||||||
| 	if (start_active_slot(slot)) { | 	if (start_active_slot(slot)) { | ||||||
|  | @ -1261,8 +909,7 @@ static struct active_lock *lock_remote(char *file, long timeout) | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size); | 	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); | 	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); | 	curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, | 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); | ||||||
| 			 fwrite_buffer_dynamic); |  | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_URL, url); | 	curl_easy_setopt(slot->curl, CURLOPT_URL, url); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1); | 	curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK); | 	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK); | ||||||
|  | @ -1390,8 +1037,7 @@ static int check_locking(void) | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size); | 	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); | 	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); | 	curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, | 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); | ||||||
| 			 fwrite_buffer_dynamic); |  | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_URL, remote->url); | 	curl_easy_setopt(slot->curl, CURLOPT_URL, remote->url); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1); | 	curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1); | ||||||
| 	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND); | 	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND); | ||||||
|  | @ -1560,8 +1206,6 @@ static int update_remote(unsigned char *sha1, struct active_lock *lock) | ||||||
|  |  | ||||||
| int main(int argc, char **argv) | int main(int argc, char **argv) | ||||||
| { | { | ||||||
| 	struct active_request_slot *slot; |  | ||||||
| 	struct active_request_slot *next_slot; |  | ||||||
| 	struct transfer_request *request; | 	struct transfer_request *request; | ||||||
| 	struct transfer_request *next_request; | 	struct transfer_request *next_request; | ||||||
| 	int nr_refspec = 0; | 	int nr_refspec = 0; | ||||||
|  | @ -1576,8 +1220,6 @@ int main(int argc, char **argv) | ||||||
| 	unsigned char remote_sha1[20]; | 	unsigned char remote_sha1[20]; | ||||||
| 	struct active_lock *remote_lock; | 	struct active_lock *remote_lock; | ||||||
| 	char *remote_path = NULL; | 	char *remote_path = NULL; | ||||||
| 	char *low_speed_limit; |  | ||||||
| 	char *low_speed_time; |  | ||||||
| 	int rc = 0; | 	int rc = 0; | ||||||
| 	int i; | 	int i; | ||||||
|  |  | ||||||
|  | @ -1617,50 +1259,7 @@ int main(int argc, char **argv) | ||||||
|  |  | ||||||
| 	memset(remote_dir_exists, 0, 256); | 	memset(remote_dir_exists, 0, 256); | ||||||
|  |  | ||||||
| 	curl_global_init(CURL_GLOBAL_ALL); | 	http_init(); | ||||||
|  |  | ||||||
| #ifdef USE_CURL_MULTI |  | ||||||
| 	{ |  | ||||||
| 		char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS"); |  | ||||||
| 		if (http_max_requests != NULL) |  | ||||||
| 			max_requests = atoi(http_max_requests); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	curlm = curl_multi_init(); |  | ||||||
| 	if (curlm == NULL) { |  | ||||||
| 		fprintf(stderr, "Error creating curl multi handle.\n"); |  | ||||||
| 		return 1; |  | ||||||
| 	} |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| 	if (getenv("GIT_SSL_NO_VERIFY")) |  | ||||||
| 		curl_ssl_verify = 0; |  | ||||||
|  |  | ||||||
| 	ssl_cert = getenv("GIT_SSL_CERT"); |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070902 |  | ||||||
| 	ssl_key = getenv("GIT_SSL_KEY"); |  | ||||||
| #endif |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x070908 |  | ||||||
| 	ssl_capath = getenv("GIT_SSL_CAPATH"); |  | ||||||
| #endif |  | ||||||
| 	ssl_cainfo = getenv("GIT_SSL_CAINFO"); |  | ||||||
|  |  | ||||||
| 	low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT"); |  | ||||||
| 	if (low_speed_limit != NULL) |  | ||||||
| 		curl_low_speed_limit = strtol(low_speed_limit, NULL, 10); |  | ||||||
| 	low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME"); |  | ||||||
| 	if (low_speed_time != NULL) |  | ||||||
| 		curl_low_speed_time = strtol(low_speed_time, NULL, 10); |  | ||||||
|  |  | ||||||
| 	git_config(http_options); |  | ||||||
|  |  | ||||||
| 	if (curl_ssl_verify == -1) |  | ||||||
| 		curl_ssl_verify = 1; |  | ||||||
|  |  | ||||||
| #ifdef USE_CURL_MULTI |  | ||||||
| 	if (max_requests < 1) |  | ||||||
| 		max_requests = DEFAULT_MAX_REQUESTS; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| 	no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:"); | 	no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:"); | ||||||
| 	default_headers = curl_slist_append(default_headers, "Range:"); | 	default_headers = curl_slist_append(default_headers, "Range:"); | ||||||
|  | @ -1669,10 +1268,6 @@ int main(int argc, char **argv) | ||||||
| 	default_headers = curl_slist_append(default_headers, | 	default_headers = curl_slist_append(default_headers, | ||||||
| 					    "Pragma: no-cache"); | 					    "Pragma: no-cache"); | ||||||
|  |  | ||||||
| #ifndef NO_CURL_EASY_DUPHANDLE |  | ||||||
| 	curl_default = get_curl_handle(); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| 	/* Verify DAV compliance/lock support */ | 	/* Verify DAV compliance/lock support */ | ||||||
| 	if (check_locking() != 0) { | 	if (check_locking() != 0) { | ||||||
| 		fprintf(stderr, "Error: no DAV locking support on remote repo %s\n", remote->url); | 		fprintf(stderr, "Error: no DAV locking support on remote repo %s\n", remote->url); | ||||||
|  | @ -1766,13 +1361,13 @@ int main(int argc, char **argv) | ||||||
| 			fetch_indices(); | 			fetch_indices(); | ||||||
| 		get_delta(push_all ? NULL : remote_sha1, | 		get_delta(push_all ? NULL : remote_sha1, | ||||||
| 			  local_object, remote_lock); | 			  local_object, remote_lock); | ||||||
| 		process_waiting_requests(); | 		finish_all_active_slots(); | ||||||
|  |  | ||||||
| 		/* Push missing objects to remote, this would be a | 		/* Push missing objects to remote, this would be a | ||||||
| 		   convenient time to pack them first if appropriate. */ | 		   convenient time to pack them first if appropriate. */ | ||||||
| 		pushing = 1; | 		pushing = 1; | ||||||
| 		process_request_queue(); | 		fill_active_slots(); | ||||||
| 		process_waiting_requests(); | 		finish_all_active_slots(); | ||||||
|  |  | ||||||
| 		/* Update the remote branch if all went well */ | 		/* Update the remote branch if all went well */ | ||||||
| 		if (do_remote_update) { | 		if (do_remote_update) { | ||||||
|  | @ -1802,14 +1397,7 @@ int main(int argc, char **argv) | ||||||
| 	curl_slist_free_all(no_pragma_header); | 	curl_slist_free_all(no_pragma_header); | ||||||
| 	curl_slist_free_all(default_headers); | 	curl_slist_free_all(default_headers); | ||||||
|  |  | ||||||
| 	slot = active_queue_head; | 	http_cleanup(); | ||||||
| 	while (slot != NULL) { |  | ||||||
| 		next_slot = slot->next; |  | ||||||
| 		if (slot->curl != NULL) |  | ||||||
| 			curl_easy_cleanup(slot->curl); |  | ||||||
| 		free(slot); |  | ||||||
| 		slot = next_slot; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	request = request_queue_head; | 	request = request_queue_head; | ||||||
| 	while (request != NULL) { | 	while (request != NULL) { | ||||||
|  | @ -1818,12 +1406,12 @@ int main(int argc, char **argv) | ||||||
| 		request = next_request; | 		request = next_request; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| #ifndef NO_CURL_EASY_DUPHANDLE |  | ||||||
| 	curl_easy_cleanup(curl_default); |  | ||||||
| #endif |  | ||||||
| #ifdef USE_CURL_MULTI |  | ||||||
| 	curl_multi_cleanup(curlm); |  | ||||||
| #endif |  | ||||||
| 	curl_global_cleanup(); |  | ||||||
| 	return rc; | 	return rc; | ||||||
| } | } | ||||||
|  | #else /* ifdef USE_CURL_MULTI */ | ||||||
|  | int main(int argc, char **argv) | ||||||
|  | { | ||||||
|  | 	fprintf(stderr, "http-push requires curl 7.9.8 or higher.\n"); | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | @ -0,0 +1,443 @@ | ||||||
|  | #include "http.h" | ||||||
|  |  | ||||||
|  | int data_received; | ||||||
|  | int active_requests = 0; | ||||||
|  |  | ||||||
|  | #ifdef USE_CURL_MULTI | ||||||
|  | int max_requests = -1; | ||||||
|  | CURLM *curlm; | ||||||
|  | #endif | ||||||
|  | #ifndef NO_CURL_EASY_DUPHANDLE | ||||||
|  | CURL *curl_default; | ||||||
|  | #endif | ||||||
|  | char curl_errorstr[CURL_ERROR_SIZE]; | ||||||
|  |  | ||||||
|  | int curl_ssl_verify = -1; | ||||||
|  | char *ssl_cert = NULL; | ||||||
|  | #if LIBCURL_VERSION_NUM >= 0x070902 | ||||||
|  | char *ssl_key = NULL; | ||||||
|  | #endif | ||||||
|  | #if LIBCURL_VERSION_NUM >= 0x070908 | ||||||
|  | char *ssl_capath = NULL; | ||||||
|  | #endif | ||||||
|  | char *ssl_cainfo = NULL; | ||||||
|  | long curl_low_speed_limit = -1; | ||||||
|  | long curl_low_speed_time = -1; | ||||||
|  |  | ||||||
|  | struct curl_slist *pragma_header; | ||||||
|  | struct curl_slist *no_range_header; | ||||||
|  |  | ||||||
|  | struct active_request_slot *active_queue_head = NULL; | ||||||
|  |  | ||||||
|  | size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, | ||||||
|  | 			   struct buffer *buffer) | ||||||
|  | { | ||||||
|  | 	size_t size = eltsize * nmemb; | ||||||
|  | 	if (size > buffer->size - buffer->posn) | ||||||
|  | 		size = buffer->size - buffer->posn; | ||||||
|  | 	memcpy(ptr, buffer->buffer + buffer->posn, size); | ||||||
|  | 	buffer->posn += size; | ||||||
|  | 	return size; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | size_t fwrite_buffer(const void *ptr, size_t eltsize, | ||||||
|  | 			    size_t nmemb, struct buffer *buffer) | ||||||
|  | { | ||||||
|  | 	size_t size = eltsize * nmemb; | ||||||
|  | 	if (size > buffer->size - buffer->posn) { | ||||||
|  | 		buffer->size = buffer->size * 3 / 2; | ||||||
|  | 		if (buffer->size < buffer->posn + size) | ||||||
|  | 			buffer->size = buffer->posn + size; | ||||||
|  | 		buffer->buffer = xrealloc(buffer->buffer, buffer->size); | ||||||
|  | 	} | ||||||
|  | 	memcpy(buffer->buffer + buffer->posn, ptr, size); | ||||||
|  | 	buffer->posn += size; | ||||||
|  | 	data_received++; | ||||||
|  | 	return size; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | size_t fwrite_null(const void *ptr, size_t eltsize, | ||||||
|  | 			  size_t nmemb, struct buffer *buffer) | ||||||
|  | { | ||||||
|  | 	data_received++; | ||||||
|  | 	return eltsize * nmemb; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void finish_active_slot(struct active_request_slot *slot); | ||||||
|  |  | ||||||
|  | #ifdef USE_CURL_MULTI | ||||||
|  | static void process_curl_messages(void) | ||||||
|  | { | ||||||
|  | 	int num_messages; | ||||||
|  | 	struct active_request_slot *slot; | ||||||
|  | 	CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages); | ||||||
|  |  | ||||||
|  | 	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) | ||||||
|  | 				slot = slot->next; | ||||||
|  | 			if (slot != NULL) { | ||||||
|  | 				curl_multi_remove_handle(curlm, slot->curl); | ||||||
|  | 				slot->curl_result = curl_result; | ||||||
|  | 				finish_active_slot(slot); | ||||||
|  | 			} else { | ||||||
|  | 				fprintf(stderr, "Received DONE message for unknown request!\n"); | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			fprintf(stderr, "Unknown CURL message received: %d\n", | ||||||
|  | 				(int)curl_message->msg); | ||||||
|  | 		} | ||||||
|  | 		curl_message = curl_multi_info_read(curlm, &num_messages); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | static int http_options(const char *var, const char *value) | ||||||
|  | { | ||||||
|  | 	if (!strcmp("http.sslverify", var)) { | ||||||
|  | 		if (curl_ssl_verify == -1) { | ||||||
|  | 			curl_ssl_verify = git_config_bool(var, value); | ||||||
|  | 		} | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!strcmp("http.sslcert", var)) { | ||||||
|  | 		if (ssl_cert == NULL) { | ||||||
|  | 			ssl_cert = xmalloc(strlen(value)+1); | ||||||
|  | 			strcpy(ssl_cert, value); | ||||||
|  | 		} | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | #if LIBCURL_VERSION_NUM >= 0x070902 | ||||||
|  | 	if (!strcmp("http.sslkey", var)) { | ||||||
|  | 		if (ssl_key == NULL) { | ||||||
|  | 			ssl_key = xmalloc(strlen(value)+1); | ||||||
|  | 			strcpy(ssl_key, value); | ||||||
|  | 		} | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | #if LIBCURL_VERSION_NUM >= 0x070908 | ||||||
|  | 	if (!strcmp("http.sslcapath", var)) { | ||||||
|  | 		if (ssl_capath == NULL) { | ||||||
|  | 			ssl_capath = xmalloc(strlen(value)+1); | ||||||
|  | 			strcpy(ssl_capath, value); | ||||||
|  | 		} | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | 	if (!strcmp("http.sslcainfo", var)) { | ||||||
|  | 		if (ssl_cainfo == NULL) { | ||||||
|  | 			ssl_cainfo = xmalloc(strlen(value)+1); | ||||||
|  | 			strcpy(ssl_cainfo, value); | ||||||
|  | 		} | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | #ifdef USE_CURL_MULTI	 | ||||||
|  | 	if (!strcmp("http.maxrequests", var)) { | ||||||
|  | 		if (max_requests == -1) | ||||||
|  | 			max_requests = git_config_int(var, value); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	if (!strcmp("http.lowspeedlimit", var)) { | ||||||
|  | 		if (curl_low_speed_limit == -1) | ||||||
|  | 			curl_low_speed_limit = (long)git_config_int(var, value); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 	if (!strcmp("http.lowspeedtime", var)) { | ||||||
|  | 		if (curl_low_speed_time == -1) | ||||||
|  | 			curl_low_speed_time = (long)git_config_int(var, value); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Fall back on the default ones */ | ||||||
|  | 	return git_default_config(var, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void http_init(void) | ||||||
|  | { | ||||||
|  | 	char *low_speed_limit; | ||||||
|  | 	char *low_speed_time; | ||||||
|  |  | ||||||
|  | 	curl_global_init(CURL_GLOBAL_ALL); | ||||||
|  |  | ||||||
|  | 	pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache"); | ||||||
|  | 	no_range_header = curl_slist_append(no_range_header, "Range:"); | ||||||
|  |  | ||||||
|  | #ifdef USE_CURL_MULTI | ||||||
|  | 	{ | ||||||
|  | 		char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS"); | ||||||
|  | 		if (http_max_requests != NULL) | ||||||
|  | 			max_requests = atoi(http_max_requests); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	curlm = curl_multi_init(); | ||||||
|  | 	if (curlm == NULL) { | ||||||
|  | 		fprintf(stderr, "Error creating curl multi handle.\n"); | ||||||
|  | 		exit(1); | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	if (getenv("GIT_SSL_NO_VERIFY")) | ||||||
|  | 		curl_ssl_verify = 0; | ||||||
|  |  | ||||||
|  | 	ssl_cert = getenv("GIT_SSL_CERT"); | ||||||
|  | #if LIBCURL_VERSION_NUM >= 0x070902 | ||||||
|  | 	ssl_key = getenv("GIT_SSL_KEY"); | ||||||
|  | #endif | ||||||
|  | #if LIBCURL_VERSION_NUM >= 0x070908 | ||||||
|  | 	ssl_capath = getenv("GIT_SSL_CAPATH"); | ||||||
|  | #endif | ||||||
|  | 	ssl_cainfo = getenv("GIT_SSL_CAINFO"); | ||||||
|  |  | ||||||
|  | 	low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT"); | ||||||
|  | 	if (low_speed_limit != NULL) | ||||||
|  | 		curl_low_speed_limit = strtol(low_speed_limit, NULL, 10); | ||||||
|  | 	low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME"); | ||||||
|  | 	if (low_speed_time != NULL) | ||||||
|  | 		curl_low_speed_time = strtol(low_speed_time, NULL, 10); | ||||||
|  |  | ||||||
|  | 	git_config(http_options); | ||||||
|  |  | ||||||
|  | 	if (curl_ssl_verify == -1) | ||||||
|  | 		curl_ssl_verify = 1; | ||||||
|  |  | ||||||
|  | #ifdef USE_CURL_MULTI | ||||||
|  | 	if (max_requests < 1) | ||||||
|  | 		max_requests = DEFAULT_MAX_REQUESTS; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifndef NO_CURL_EASY_DUPHANDLE | ||||||
|  | 	curl_default = get_curl_handle(); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void http_cleanup(void) | ||||||
|  | { | ||||||
|  | 	struct active_request_slot *slot = active_queue_head; | ||||||
|  | #ifdef USE_CURL_MULTI | ||||||
|  | 	char *wait_url; | ||||||
|  | 	CURLMcode curlm_result; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	while (slot != NULL) { | ||||||
|  | #ifdef USE_CURL_MULTI | ||||||
|  | 		if (slot->in_use) { | ||||||
|  | 			curl_easy_getinfo(slot->curl, | ||||||
|  | 					  CURLINFO_EFFECTIVE_URL, | ||||||
|  | 					  &wait_url); | ||||||
|  | 			fprintf(stderr, "Waiting for %s\n", wait_url); | ||||||
|  | 			run_active_slot(slot); | ||||||
|  | 		} | ||||||
|  | #endif | ||||||
|  | 		if (slot->curl != NULL) | ||||||
|  | 			curl_easy_cleanup(slot->curl); | ||||||
|  | 		slot = slot->next; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | #ifndef NO_CURL_EASY_DUPHANDLE | ||||||
|  | 	curl_easy_cleanup(curl_default); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef USE_CURL_MULTI | ||||||
|  | 	curl_multi_cleanup(curlm); | ||||||
|  | #endif | ||||||
|  | 	curl_global_cleanup(); | ||||||
|  | 	 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static CURL* get_curl_handle(void) | ||||||
|  | { | ||||||
|  | 	CURL* result = curl_easy_init(); | ||||||
|  |  | ||||||
|  | 	curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify); | ||||||
|  | #if LIBCURL_VERSION_NUM >= 0x070907 | ||||||
|  | 	curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	if (ssl_cert != NULL) | ||||||
|  | 		curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert); | ||||||
|  | #if LIBCURL_VERSION_NUM >= 0x070902 | ||||||
|  | 	if (ssl_key != NULL) | ||||||
|  | 		curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key); | ||||||
|  | #endif | ||||||
|  | #if LIBCURL_VERSION_NUM >= 0x070908 | ||||||
|  | 	if (ssl_capath != NULL) | ||||||
|  | 		curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath); | ||||||
|  | #endif | ||||||
|  | 	if (ssl_cainfo != NULL) | ||||||
|  | 		curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo); | ||||||
|  | 	curl_easy_setopt(result, CURLOPT_FAILONERROR, 1); | ||||||
|  |  | ||||||
|  | 	if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) { | ||||||
|  | 		curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT, | ||||||
|  | 				 curl_low_speed_limit); | ||||||
|  | 		curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME, | ||||||
|  | 				 curl_low_speed_time); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1); | ||||||
|  |  | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct active_request_slot *get_active_slot(void) | ||||||
|  | { | ||||||
|  | 	struct active_request_slot *slot = active_queue_head; | ||||||
|  | 	struct active_request_slot *newslot; | ||||||
|  |  | ||||||
|  | #ifdef USE_CURL_MULTI | ||||||
|  | 	int num_transfers; | ||||||
|  |  | ||||||
|  | 	/* Wait for a slot to open up if the queue is full */ | ||||||
|  | 	while (active_requests >= max_requests) { | ||||||
|  | 		curl_multi_perform(curlm, &num_transfers); | ||||||
|  | 		if (num_transfers < active_requests) { | ||||||
|  | 			process_curl_messages(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	while (slot != NULL && slot->in_use) { | ||||||
|  | 		slot = slot->next; | ||||||
|  | 	} | ||||||
|  | 	if (slot == NULL) { | ||||||
|  | 		newslot = xmalloc(sizeof(*newslot)); | ||||||
|  | 		newslot->curl = NULL; | ||||||
|  | 		newslot->in_use = 0; | ||||||
|  | 		newslot->next = NULL; | ||||||
|  |  | ||||||
|  | 		slot = active_queue_head; | ||||||
|  | 		if (slot == NULL) { | ||||||
|  | 			active_queue_head = newslot; | ||||||
|  | 		} else { | ||||||
|  | 			while (slot->next != NULL) { | ||||||
|  | 				slot = slot->next; | ||||||
|  | 			} | ||||||
|  | 			slot->next = newslot; | ||||||
|  | 		} | ||||||
|  | 		slot = newslot; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (slot->curl == NULL) { | ||||||
|  | #ifdef NO_CURL_EASY_DUPHANDLE | ||||||
|  | 		slot->curl = get_curl_handle(); | ||||||
|  | #else | ||||||
|  | 		slot->curl = curl_easy_duphandle(curl_default); | ||||||
|  | #endif | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	active_requests++; | ||||||
|  | 	slot->in_use = 1; | ||||||
|  | 	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); | ||||||
|  |  | ||||||
|  | 	return slot; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int start_active_slot(struct active_request_slot *slot) | ||||||
|  | { | ||||||
|  | #ifdef USE_CURL_MULTI | ||||||
|  | 	CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl); | ||||||
|  |  | ||||||
|  | 	if (curlm_result != CURLM_OK && | ||||||
|  | 	    curlm_result != CURLM_CALL_MULTI_PERFORM) { | ||||||
|  | 		active_requests--; | ||||||
|  | 		slot->in_use = 0; | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #ifdef USE_CURL_MULTI | ||||||
|  | void step_active_slots(void) | ||||||
|  | { | ||||||
|  | 	int num_transfers; | ||||||
|  | 	CURLMcode curlm_result; | ||||||
|  |  | ||||||
|  | 	do { | ||||||
|  | 		curlm_result = curl_multi_perform(curlm, &num_transfers); | ||||||
|  | 	} while (curlm_result == CURLM_CALL_MULTI_PERFORM); | ||||||
|  | 	if (num_transfers < active_requests) { | ||||||
|  | 		process_curl_messages(); | ||||||
|  | 		fill_active_slots(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | void run_active_slot(struct active_request_slot *slot) | ||||||
|  | { | ||||||
|  | #ifdef USE_CURL_MULTI | ||||||
|  | 	long last_pos = 0; | ||||||
|  | 	long current_pos; | ||||||
|  | 	fd_set readfds; | ||||||
|  | 	fd_set writefds; | ||||||
|  | 	fd_set excfds; | ||||||
|  | 	int max_fd; | ||||||
|  | 	struct timeval select_timeout; | ||||||
|  |  | ||||||
|  | 	while (slot->in_use) { | ||||||
|  | 		data_received = 0; | ||||||
|  | 		step_active_slots(); | ||||||
|  |  | ||||||
|  | 		if (!data_received && slot->local != NULL) { | ||||||
|  | 			current_pos = ftell(slot->local); | ||||||
|  | 			if (current_pos > last_pos) | ||||||
|  | 				data_received++; | ||||||
|  | 			last_pos = current_pos; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (slot->in_use && !data_received) { | ||||||
|  | 			max_fd = 0; | ||||||
|  | 			FD_ZERO(&readfds); | ||||||
|  | 			FD_ZERO(&writefds); | ||||||
|  | 			FD_ZERO(&excfds); | ||||||
|  | 			select_timeout.tv_sec = 0; | ||||||
|  | 			select_timeout.tv_usec = 50000; | ||||||
|  | 			select(max_fd, &readfds, &writefds, | ||||||
|  | 			       &excfds, &select_timeout); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | #else | ||||||
|  | 	while (slot->in_use) { | ||||||
|  | 		slot->curl_result = curl_easy_perform(slot->curl); | ||||||
|  | 		finish_active_slot(slot); | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void finish_active_slot(struct active_request_slot *slot) | ||||||
|  | { | ||||||
|  |         active_requests--; | ||||||
|  |         slot->in_use = 0; | ||||||
|  |         curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code); | ||||||
|  |   | ||||||
|  |         /* Run callback if appropriate */ | ||||||
|  |         if (slot->callback_func != NULL) { | ||||||
|  |                 slot->callback_func(slot->callback_data); | ||||||
|  |         } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void finish_all_active_slots(void) | ||||||
|  | { | ||||||
|  | 	struct active_request_slot *slot = active_queue_head; | ||||||
|  |  | ||||||
|  | 	while (slot != NULL) | ||||||
|  | 		if (slot->in_use) { | ||||||
|  | 			run_active_slot(slot); | ||||||
|  | 			slot = active_queue_head; | ||||||
|  | 		} else { | ||||||
|  | 			slot = slot->next; | ||||||
|  | 		} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,95 @@ | ||||||
|  | #ifndef HTTP_H | ||||||
|  | #define HTTP_H | ||||||
|  |  | ||||||
|  | #include "cache.h" | ||||||
|  |  | ||||||
|  | #include <curl/curl.h> | ||||||
|  | #include <curl/easy.h> | ||||||
|  |  | ||||||
|  | #if LIBCURL_VERSION_NUM >= 0x070908 | ||||||
|  | #define USE_CURL_MULTI | ||||||
|  | #define DEFAULT_MAX_REQUESTS 5 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #if LIBCURL_VERSION_NUM < 0x070704 | ||||||
|  | #define curl_global_cleanup() do { /* nothing */ } while(0) | ||||||
|  | #endif | ||||||
|  | #if LIBCURL_VERSION_NUM < 0x070800 | ||||||
|  | #define curl_global_init(a) do { /* nothing */ } while(0) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #if LIBCURL_VERSION_NUM < 0x070c04 | ||||||
|  | #define NO_CURL_EASY_DUPHANDLE | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | struct active_request_slot | ||||||
|  | { | ||||||
|  | 	CURL *curl; | ||||||
|  | 	FILE *local; | ||||||
|  | 	int in_use; | ||||||
|  | 	CURLcode curl_result; | ||||||
|  | 	long http_code; | ||||||
|  | 	void *callback_data; | ||||||
|  | 	void (*callback_func)(void *data); | ||||||
|  | 	struct active_request_slot *next; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct buffer | ||||||
|  | { | ||||||
|  |         size_t posn; | ||||||
|  |         size_t size; | ||||||
|  |         void *buffer; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* Curl request read/write callbacks */ | ||||||
|  | extern size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, | ||||||
|  | 			   struct buffer *buffer); | ||||||
|  | extern size_t fwrite_buffer(const void *ptr, size_t eltsize, | ||||||
|  | 			    size_t nmemb, struct buffer *buffer); | ||||||
|  | extern size_t fwrite_null(const void *ptr, size_t eltsize, | ||||||
|  | 			  size_t nmemb, struct buffer *buffer); | ||||||
|  |  | ||||||
|  | /* Slot lifecycle functions */ | ||||||
|  | extern struct active_request_slot *get_active_slot(void); | ||||||
|  | extern int start_active_slot(struct active_request_slot *slot); | ||||||
|  | extern void run_active_slot(struct active_request_slot *slot); | ||||||
|  | extern void finish_all_active_slots(void); | ||||||
|  |  | ||||||
|  | #ifdef USE_CURL_MULTI | ||||||
|  | extern void fill_active_slots(void); | ||||||
|  | extern void step_active_slots(void); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | extern void http_init(void); | ||||||
|  | extern void http_cleanup(void); | ||||||
|  |  | ||||||
|  | extern int data_received; | ||||||
|  | extern int active_requests; | ||||||
|  |  | ||||||
|  | #ifdef USE_CURL_MULTI | ||||||
|  | extern int max_requests; | ||||||
|  | extern CURLM *curlm; | ||||||
|  | #endif | ||||||
|  | #ifndef NO_CURL_EASY_DUPHANDLE | ||||||
|  | extern CURL *curl_default; | ||||||
|  | #endif | ||||||
|  | extern char curl_errorstr[CURL_ERROR_SIZE]; | ||||||
|  |  | ||||||
|  | extern int curl_ssl_verify; | ||||||
|  | extern char *ssl_cert; | ||||||
|  | #if LIBCURL_VERSION_NUM >= 0x070902 | ||||||
|  | extern char *ssl_key; | ||||||
|  | #endif | ||||||
|  | #if LIBCURL_VERSION_NUM >= 0x070908 | ||||||
|  | extern char *ssl_capath; | ||||||
|  | #endif | ||||||
|  | extern char *ssl_cainfo; | ||||||
|  | extern long curl_low_speed_limit; | ||||||
|  | extern long curl_low_speed_time; | ||||||
|  |  | ||||||
|  | extern struct curl_slist *pragma_header; | ||||||
|  | extern struct curl_slist *no_range_header; | ||||||
|  |  | ||||||
|  | extern struct active_request_slot *active_queue_head; | ||||||
|  |  | ||||||
|  | #endif /* HTTP_H */ | ||||||
		Loading…
	
		Reference in New Issue
	
	 Nick Hengeveld
						Nick Hengeveld