X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=http-push.c;h=89fda42efb678d2f9f9e802454fe561b4452a167;hb=7141b3b78099941f25b17388f56a917d4f7af51d;hp=e85f1c17089a1da95b264909cfc50235c74471da;hpb=58e60dd203362ecb9fdea765dcc2eb573892dbaf;p=git.git diff --git a/http-push.c b/http-push.c index e85f1c17..89fda42e 100644 --- a/http-push.c +++ b/http-push.c @@ -7,7 +7,7 @@ #include #include -#include "expat.h" +#include static const char http_push_usage[] = "git-http-push [--complete] [--force] [--verbose] [...]\n"; @@ -40,6 +40,9 @@ static const char http_push_usage[] = #define PROPFIND_REQUEST "\n\n\n\n\n" #define LOCK_REQUEST "\n\n\n\n\nmailto:%s\n\n" +#define LOCK_TIME 600 +#define LOCK_REFRESH 30 + static int active_requests = 0; static int data_received; static int pushing = 0; @@ -55,7 +58,6 @@ static CURL *curl_default; static struct curl_slist *no_pragma_header; static struct curl_slist *default_headers; static char curl_errorstr[CURL_ERROR_SIZE]; -static char *lock_token = NULL; static int push_verbosely = 0; static int push_all = 0; @@ -92,7 +94,7 @@ struct transfer_request unsigned char sha1[20]; char *url; char *dest; - char *lock_token; + struct active_lock *lock; struct curl_slist *headers; struct buffer buffer; char filename[PATH_MAX]; @@ -136,6 +138,22 @@ static char *ssl_cainfo = NULL; static long curl_low_speed_limit = -1; static long curl_low_speed_time = -1; +struct active_lock +{ + int ctx_activelock; + int ctx_owner; + int ctx_owner_href; + int ctx_timeout; + int ctx_locktoken; + int ctx_locktoken_href; + char *url; + char *owner; + char *token; + time_t start_time; + long timeout; + int refreshing; +}; + struct lockprop { int supported_lock; @@ -509,7 +527,7 @@ static void start_put(struct transfer_request *request) if (request->url != NULL) free(request->url); request->url = xmalloc(strlen(remote->url) + - strlen(request->lock_token) + 51); + strlen(request->lock->token) + 51); strcpy(request->url, remote->url); posn = request->url + strlen(remote->url); strcpy(posn, "objects/"); @@ -522,7 +540,7 @@ static void start_put(struct transfer_request *request) sprintf(request->dest, "Destination: %s", request->url); posn += 38; *(posn++) = '.'; - strcpy(posn, request->lock_token); + strcpy(posn, request->lock->token); slot = get_active_slot(); curl_easy_setopt(slot->curl, CURLOPT_INFILE, &request->buffer); @@ -567,11 +585,65 @@ static void start_move(struct transfer_request *request) } } +int refresh_lock(struct active_lock *lock) +{ + struct active_request_slot *slot; + char *if_header; + char timeout_header[25]; + struct curl_slist *dav_headers = NULL; + int rc = 0; + + lock->refreshing = 1; + + if_header = xmalloc(strlen(lock->token) + 25); + sprintf(if_header, "If: ()", lock->token); + sprintf(timeout_header, "Timeout: Second-%ld", lock->timeout); + dav_headers = curl_slist_append(dav_headers, if_header); + dav_headers = curl_slist_append(dav_headers, timeout_header); + + slot = get_active_slot(); + curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); + curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null); + curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url); + curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK); + curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); + + if (start_active_slot(slot)) { + run_active_slot(slot); + if (slot->curl_result != CURLE_OK) { + fprintf(stderr, "Got HTTP error %ld\n", slot->http_code); + } else { + lock->start_time = time(NULL); + rc = 1; + } + } + + lock->refreshing = 0; + curl_slist_free_all(dav_headers); + free(if_header); + + return rc; +} + static void finish_request(struct transfer_request *request) { + time_t current_time = time(NULL); + int time_remaining; + request->curl_result = request->slot->curl_result; request->http_code = request->slot->http_code; request->slot = NULL; + + /* Refresh the lock if it is close to timing out */ + time_remaining = request->lock->start_time + request->lock->timeout + - current_time; + if (time_remaining < LOCK_REFRESH && !request->lock->refreshing) { + if (!refresh_lock(request->lock)) { + fprintf(stderr, "Unable to refresh remote lock\n"); + aborted = 1; + } + } + if (request->headers != NULL) curl_slist_free_all(request->headers); if (request->state == RUN_HEAD) { @@ -724,10 +796,9 @@ void process_waiting_requests(void) } } -void add_request(unsigned char *sha1, char *lock_token) +void add_request(unsigned char *sha1, struct active_lock *lock) { struct transfer_request *request = request_queue_head; - struct transfer_request *tail; struct packed_git *target; while (request != NULL && memcmp(request->sha1, sha1, 20)) @@ -742,20 +813,11 @@ void add_request(unsigned char *sha1, char *lock_token) request = xmalloc(sizeof(*request)); memcpy(request->sha1, sha1, 20); request->url = NULL; - request->lock_token = lock_token; + request->lock = lock; request->headers = NULL; request->state = NEED_CHECK; - request->next = NULL; - - if (request_queue_head == NULL) { - request_queue_head = request; - } else { - tail = request_queue_head; - while (tail->next != NULL) { - tail = tail->next; - } - tail->next = request; - } + request->next = request_queue_head; + request_queue_head = request; #ifdef USE_CURL_MULTI process_request_queue(); process_curl_messages(); @@ -775,13 +837,29 @@ static int fetch_index(unsigned char *sha1) FILE *indexfile; struct active_request_slot *slot; + /* Don't use the index if the pack isn't there */ + url = xmalloc(strlen(remote->url) + 65); + sprintf(url, "%s/objects/pack/pack-%s.pack", remote->url, hex); + slot = get_active_slot(); + curl_easy_setopt(slot->curl, CURLOPT_URL, url); + curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1); + if (start_active_slot(slot)) { + run_active_slot(slot); + if (slot->curl_result != CURLE_OK) { + free(url); + return error("Unable to verify pack %s is available", + hex); + } + } else { + return error("Unable to start request"); + } + if (has_pack_index(sha1)) return 0; if (push_verbosely) fprintf(stderr, "Getting index for pack %s\n", hex); - url = xmalloc(strlen(remote->url) + 64); sprintf(url, "%s/objects/pack/pack-%s.idx", remote->url, hex); filename = sha1_pack_index_name(sha1); @@ -792,6 +870,8 @@ static int fetch_index(unsigned char *sha1) filename); slot = get_active_slot(); + curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0); + curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); curl_easy_setopt(slot->curl, CURLOPT_FILE, indexfile); curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite); curl_easy_setopt(slot->curl, CURLOPT_URL, url); @@ -833,8 +913,6 @@ static int fetch_index(unsigned char *sha1) static int setup_index(unsigned char *sha1) { struct packed_git *new_pack; - if (has_pack_file(sha1)) - return 0; // don't list this as something we can get if (fetch_index(sha1)) return -1; @@ -993,6 +1071,68 @@ int fetch_ref(char *ref, unsigned char *sha1) } static void +start_activelock_element(void *userData, const char *name, const char **atts) +{ + struct active_lock *lock = (struct active_lock *)userData; + + if (lock->ctx_activelock && !strcmp(name, "D:timeout")) + lock->ctx_timeout = 1; + else if (lock->ctx_owner && strstr(name, "href")) + lock->ctx_owner_href = 1; + else if (lock->ctx_activelock && strstr(name, "owner")) + lock->ctx_owner = 1; + else if (lock->ctx_locktoken && !strcmp(name, "D:href")) + lock->ctx_locktoken_href = 1; + else if (lock->ctx_activelock && !strcmp(name, "D:locktoken")) + lock->ctx_locktoken = 1; + else if (!strcmp(name, "D:activelock")) + lock->ctx_activelock = 1; +} + +static void +end_activelock_element(void *userData, const char *name) +{ + struct active_lock *lock = (struct active_lock *)userData; + + if (lock->ctx_timeout && !strcmp(name, "D:timeout")) { + lock->ctx_timeout = 0; + } else if (lock->ctx_owner_href && strstr(name, "href")) { + lock->ctx_owner_href = 0; + } else if (lock->ctx_owner && strstr(name, "owner")) { + lock->ctx_owner = 0; + } else if (lock->ctx_locktoken_href && !strcmp(name, "D:href")) { + lock->ctx_locktoken_href = 0; + } else if (lock->ctx_locktoken && !strcmp(name, "D:locktoken")) { + lock->ctx_locktoken = 0; + } else if (lock->ctx_activelock && !strcmp(name, "D:activelock")) { + lock->ctx_activelock = 0; + } +} + +static void +activelock_cdata(void *userData, const XML_Char *s, int len) +{ + struct active_lock *lock = (struct active_lock *)userData; + char *this = malloc(len+1); + strncpy(this, s, len); + + if (lock->ctx_owner_href) { + lock->owner = malloc(len+1); + strcpy(lock->owner, this); + } else if (lock->ctx_locktoken_href) { + if (!strncmp(this, "opaquelocktoken:", 16)) { + lock->token = malloc(len-15); + strcpy(lock->token, this+16); + } + } else if (lock->ctx_timeout) { + if (!strncmp(this, "Second-", 7)) + lock->timeout = strtol(this+7, NULL, 10); + } + + free(this); +} + +static void start_lockprop_element(void *userData, const char *name, const char **atts) { struct lockprop *prop = (struct lockprop *)userData; @@ -1033,38 +1173,51 @@ end_lockprop_element(void *userData, const char *name) } } -size_t process_lock_header( void *ptr, size_t size, size_t nmemb, void *stream) -{ - size_t header_size = size*nmemb; - char *start; - char *end; - - if (!strncmp(ptr, "Lock-Token: '; - end--) {} - if (end > start) { - lock_token = xmalloc(end - start + 1); - memcpy(lock_token, start, end - start); - lock_token[end - start] = 0; - } - } - - return header_size; -} - -char *lock_remote(char *file, int timeout) +struct active_lock *lock_remote(char *file, long timeout) { struct active_request_slot *slot; struct buffer out_buffer; + struct buffer in_buffer; char *out_data; + char *in_data; char *url; + char *ep; char timeout_header[25]; + struct active_lock *new_lock; + XML_Parser parser = XML_ParserCreate(NULL); + enum XML_Status result; struct curl_slist *dav_headers = NULL; - if (lock_token != NULL) - free(lock_token); + url = xmalloc(strlen(remote->url) + strlen(file) + 1); + sprintf(url, "%s%s", remote->url, file); + + /* Make sure leading directories exist for the remote ref */ + ep = strchr(url + strlen(remote->url) + 11, '/'); + while (ep) { + *ep = 0; + slot = get_active_slot(); + curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); + curl_easy_setopt(slot->curl, CURLOPT_URL, url); + curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MKCOL); + curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null); + if (start_active_slot(slot)) { + run_active_slot(slot); + if (slot->curl_result != CURLE_OK && + slot->http_code != 405) { + fprintf(stderr, + "Unable to create branch path %s\n", + url); + free(url); + return NULL; + } + } else { + fprintf(stderr, "Unable to start request\n"); + free(url); + return NULL; + } + *ep = '/'; + ep = strchr(ep + 1, '/'); + } out_buffer.size = strlen(LOCK_REQUEST) + strlen(git_default_email) - 2; out_data = xmalloc(out_buffer.size + 1); @@ -1072,9 +1225,18 @@ char *lock_remote(char *file, int timeout) out_buffer.posn = 0; out_buffer.buffer = out_data; - sprintf(timeout_header, "Timeout: Second-%d", timeout); - url = xmalloc(strlen(remote->url) + strlen(file) + 1); - sprintf(url, "%s%s", remote->url, file); + in_buffer.size = 4096; + in_data = xmalloc(in_buffer.size); + in_buffer.posn = 0; + in_buffer.buffer = in_data; + + new_lock = xmalloc(sizeof(*new_lock)); + new_lock->owner = NULL; + new_lock->token = NULL; + new_lock->timeout = -1; + new_lock->refreshing = 0; + + sprintf(timeout_header, "Timeout: Second-%ld", timeout); dav_headers = curl_slist_append(dav_headers, timeout_header); dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml"); @@ -1082,9 +1244,9 @@ char *lock_remote(char *file, int timeout) curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer); 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_WRITEFUNCTION, fwrite_null); - curl_easy_setopt(slot->curl, CURLOPT_HEADERFUNCTION, - process_lock_header); + curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); + curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, + fwrite_buffer_dynamic); curl_easy_setopt(slot->curl, CURLOPT_URL, url); curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1); curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK); @@ -1092,42 +1254,69 @@ char *lock_remote(char *file, int timeout) if (start_active_slot(slot)) { run_active_slot(slot); - free(out_data); if (slot->curl_result != CURLE_OK) { fprintf(stderr, "Got HTTP error %ld\n", slot->http_code); + free(new_lock); + free(url); + free(out_data); + free(in_data); return NULL; } } else { + free(new_lock); + free(url); free(out_data); + free(in_data); fprintf(stderr, "Unable to start request\n"); + return NULL; + } + + free(out_data); + + XML_SetUserData(parser, new_lock); + XML_SetElementHandler(parser, start_activelock_element, + end_activelock_element); + XML_SetCharacterDataHandler(parser, activelock_cdata); + result = XML_Parse(parser, in_buffer.buffer, in_buffer.posn, 1); + free(in_data); + if (result != XML_STATUS_OK) { + fprintf(stderr, "%s", XML_ErrorString( + XML_GetErrorCode(parser))); + free(url); + free(new_lock); + return NULL; + } + + if (new_lock->token == NULL || new_lock->timeout <= 0) { + if (new_lock->token != NULL) + free(new_lock->token); + if (new_lock->owner != NULL) + free(new_lock->owner); + free(url); + free(new_lock); + return NULL; } - return strdup(lock_token); + new_lock->url = url; + new_lock->start_time = time(NULL); + return new_lock; } -int unlock_remote(char *file, char *lock_token) +int unlock_remote(struct active_lock *lock) { struct active_request_slot *slot; - char *url; char *lock_token_header; struct curl_slist *dav_headers = NULL; int rc = 0; - if (lock_token == NULL) { - fprintf(stderr, "Unable to unlock, no lock token"); - return 0; - } - - lock_token_header = xmalloc(strlen(lock_token) + 31); + lock_token_header = xmalloc(strlen(lock->token) + 31); sprintf(lock_token_header, "Lock-Token: ", - lock_token); - url = xmalloc(strlen(remote->url) + strlen(file) + 1); - sprintf(url, "%s%s", remote->url, file); + lock->token); dav_headers = curl_slist_append(dav_headers, lock_token_header); slot = get_active_slot(); curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null); - curl_easy_setopt(slot->curl, CURLOPT_URL, url); + curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url); curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_UNLOCK); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); @@ -1144,7 +1333,12 @@ int unlock_remote(char *file, char *lock_token) curl_slist_free_all(dav_headers); free(lock_token_header); - free(url); + + if (lock->owner != NULL) + free(lock->owner); + free(lock->url); + free(lock->token); + free(lock); return rc; } @@ -1236,7 +1430,8 @@ int is_ancestor(unsigned char *sha1, struct commit *commit) return 0; } -void get_delta(unsigned char *sha1, struct object *obj, char *lock_token) +void get_delta(unsigned char *sha1, struct object *obj, + struct active_lock *lock) { struct commit *commit; struct commit_list *parents; @@ -1252,7 +1447,7 @@ void get_delta(unsigned char *sha1, struct object *obj, char *lock_token) if (obj->type == commit_type) { if (push_verbosely) fprintf(stderr, "walk %s\n", sha1_to_hex(obj->sha1)); - add_request(obj->sha1, lock_token); + add_request(obj->sha1, lock); commit = (struct commit *)obj; if (parse_commit(commit)) { fprintf(stderr, "Error parsing commit %s\n", @@ -1265,12 +1460,12 @@ void get_delta(unsigned char *sha1, struct object *obj, char *lock_token) if (sha1 == NULL || memcmp(sha1, parents->item->object.sha1, 20)) get_delta(sha1, &parents->item->object, - lock_token); - get_delta(sha1, &commit->tree->object, lock_token); + lock); + get_delta(sha1, &commit->tree->object, lock); } else if (obj->type == tree_type) { if (push_verbosely) fprintf(stderr, "walk %s\n", sha1_to_hex(obj->sha1)); - add_request(obj->sha1, lock_token); + add_request(obj->sha1, lock); tree = (struct tree *)obj; if (parse_tree(tree)) { fprintf(stderr, "Error parsing tree %s\n", @@ -1282,31 +1477,27 @@ void get_delta(unsigned char *sha1, struct object *obj, char *lock_token) tree->entries = NULL; while (entry) { struct tree_entry_list *next = entry->next; - get_delta(sha1, entry->item.any, lock_token); + get_delta(sha1, entry->item.any, lock); free(entry->name); free(entry); entry = next; } } else if (obj->type == blob_type || obj->type == tag_type) { - add_request(obj->sha1, lock_token); + add_request(obj->sha1, lock); } } -int update_remote(char *remote_path, unsigned char *sha1, char *lock_token) +int update_remote(unsigned char *sha1, struct active_lock *lock) { struct active_request_slot *slot; - char *url; char *out_data; char *if_header; struct buffer out_buffer; struct curl_slist *dav_headers = NULL; int i; - url = xmalloc(strlen(remote->url) + strlen(remote_path) + 1); - sprintf(url, "%s%s", remote->url, remote_path); - - if_header = xmalloc(strlen(lock_token) + 25); - sprintf(if_header, "If: ()", lock_token); + if_header = xmalloc(strlen(lock->token) + 25); + sprintf(if_header, "If: ()", lock->token); dav_headers = curl_slist_append(dav_headers, if_header); out_buffer.size = 41; @@ -1328,13 +1519,12 @@ int update_remote(char *remote_path, unsigned char *sha1, char *lock_token) curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1); curl_easy_setopt(slot->curl, CURLOPT_PUT, 1); - curl_easy_setopt(slot->curl, CURLOPT_URL, url); + curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url); if (start_active_slot(slot)) { run_active_slot(slot); free(out_data); free(if_header); - free(url); if (slot->curl_result != CURLE_OK) { fprintf(stderr, "PUT error: curl result=%d, HTTP code=%ld\n", @@ -1345,7 +1535,6 @@ int update_remote(char *remote_path, unsigned char *sha1, char *lock_token) } else { free(out_data); free(if_header); - free(url); fprintf(stderr, "Unable to start PUT request\n"); return 0; } @@ -1369,7 +1558,7 @@ int main(int argc, char **argv) struct object *local_object = NULL; char *remote_ref = NULL; unsigned char remote_sha1[20]; - char *remote_lock = NULL; + struct active_lock *remote_lock; char *remote_path = NULL; char *low_speed_limit; char *low_speed_time; @@ -1497,7 +1686,7 @@ int main(int argc, char **argv) free(remote_path); remote_path = xmalloc(strlen(remote_ref) + 12); sprintf(remote_path, "refs/heads/%s", remote_ref); - remote_lock = lock_remote(remote_path, 3600); + remote_lock = lock_remote(remote_path, LOCK_TIME); if (remote_lock == NULL) { fprintf(stderr, "Unable to lock remote branch %s\n", remote_ref); @@ -1569,8 +1758,7 @@ int main(int argc, char **argv) /* Update the remote branch if all went well */ if (do_remote_update) { - if (!aborted && update_remote(remote_path, - local_sha1, + if (!aborted && update_remote(local_sha1, remote_lock)) { fprintf(stderr, "%s remote branch %s\n", new_branch ? "Created" : "Updated", @@ -1586,9 +1774,8 @@ int main(int argc, char **argv) } unlock: - unlock_remote(remote_path, remote_lock); + unlock_remote(remote_lock); free(remote_path); - free(remote_lock); } cleanup: @@ -1610,7 +1797,6 @@ int main(int argc, char **argv) while (request != NULL) { next_request = request->next; release_request(request); - free(request); request = next_request; }