{"thread":{"id":"3789","subject":"[PATCH] http-fetch: add optional DAV-based pack list","startedAt":"2006-04-04T12:33:18Z","lastAt":"2006-04-04T12:33:18Z","messageCount":1,"participants":["Nick Hengeveld"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"18341","messageId":"20060404123318.GC14967@reactrix.com","threadId":"3789","inReplyTo":null,"subject":"[PATCH] http-fetch: add optional DAV-based pack list","fromName":"Nick Hengeveld","fromEmail":"nickh@reactrix.com","sentAt":"2006-04-04T12:33:18Z","receivedAt":"2006-04-04T12:33:18Z","isPatch":true,"sender":{"key":"nickh@reactrix.com","avatar":null},"body":"If git is not built with NO_EXPAT, this patch changes git-http-fetch to\nattempt using DAV to get a list of remote packs and fall back to using\nobjects/info/packs if the DAV request fails.\n\nSigned-off-by: Nick Hengeveld <nickh@reactrix.com>\n\n\n---\n\n Makefile     |    7 +\n http-fetch.c |  278 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 284 insertions(+), 1 deletions(-)\n\n9aef9577cabbd96de20562902d2958108616a7e4\ndiff --git a/Makefile b/Makefile\nindex c79d646..19ce42c 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -510,6 +510,11 @@ git$X git.spec \\\n exec_cmd.o: exec_cmd.c\n \t$(CC) -o $*.o -c $(ALL_CFLAGS) '-DGIT_EXEC_PATH=\"$(gitexecdir_SQ)\"' $<\n \n+ifdef NO_EXPAT\n+http-fetch.o: http-fetch.c\n+\t$(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_EXPAT $<\n+endif\n+\n git-%$X: %.o $(GITLIBS)\n \t$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)\n \n@@ -532,7 +537,7 @@ git-imap-send$X: imap-send.o $(LIB_FILE)\n \n git-http-fetch$X: fetch.o http.o http-fetch.o $(LIB_FILE)\n \t$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n-\t\t$(LIBS) $(CURL_LIBCURL)\n+\t\t$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)\n \n git-http-push$X: revision.o http.o http-push.o $(LIB_FILE)\n \t$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\ndiff --git a/http-fetch.c b/http-fetch.c\nindex dc67218..71a7daf 100644\n--- a/http-fetch.c\n+++ b/http-fetch.c\n@@ -4,6 +4,35 @@ #include \"pack.h\"\n #include \"fetch.h\"\n #include \"http.h\"\n \n+#ifndef NO_EXPAT\n+#include <expat.h>\n+\n+/* Definitions for DAV requests */\n+#define DAV_PROPFIND \"PROPFIND\"\n+#define DAV_PROPFIND_RESP \".multistatus.response\"\n+#define DAV_PROPFIND_NAME \".multistatus.response.href\"\n+#define DAV_PROPFIND_COLLECTION \".multistatus.response.propstat.prop.resourcetype.collection\"\n+#define PROPFIND_ALL_REQUEST \"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\" ?>\\n<D:propfind xmlns:D=\\\"DAV:\\\">\\n<D:allprop/>\\n</D:propfind>\"\n+\n+/* Definitions for processing XML DAV responses */\n+#ifndef XML_STATUS_OK\n+enum XML_Status {\n+  XML_STATUS_OK = 1,\n+  XML_STATUS_ERROR = 0\n+};\n+#define XML_STATUS_OK    1\n+#define XML_STATUS_ERROR 0\n+#endif\n+\n+/* Flags that control remote_ls processing */\n+#define PROCESS_FILES (1u << 0)\n+#define PROCESS_DIRS  (1u << 1)\n+#define RECURSIVE     (1u << 2)\n+\n+/* Flags that remote_ls passes to callback functions */\n+#define IS_DIR (1u << 0)\n+#endif\n+\n #define PREV_BUF_SIZE 4096\n #define RANGE_HEADER_SIZE 30\n \n@@ -15,6 +44,7 @@ static struct curl_slist *no_pragma_head\n struct alt_base\n {\n \tchar *base;\n+\tint path_len;\n \tint got_indices;\n \tstruct packed_git *packs;\n \tstruct alt_base *next;\n@@ -56,8 +86,32 @@ struct alternates_request {\n \tstruct buffer *buffer;\n \tstruct active_request_slot *slot;\n \tint http_specific;\n+};\n+\n+#ifndef NO_EXPAT\n+struct xml_ctx\n+{\n+\tchar *name;\n+\tint len;\n+\tchar *cdata;\n+\tvoid (*userFunc)(struct xml_ctx *ctx, int tag_closed);\n+\tvoid *userData;\n };\n \n+struct remote_ls_ctx\n+{\n+\tstruct alt_base *repo;\n+\tchar *path;\n+\tvoid (*userFunc)(struct remote_ls_ctx *ls);\n+\tvoid *userData;\n+\tint flags;\n+\tchar *dentry_name;\n+\tint dentry_flags;\n+\tint rc;\n+\tstruct remote_ls_ctx *parent;\n+};\n+#endif\n+\n static struct object_request *object_queue_head = NULL;\n \n static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,\n@@ -500,6 +554,7 @@ static void process_alternates_response(\n \t\t\tint serverlen = 0;\n \t\t\tstruct alt_base *newalt;\n \t\t\tchar *target = NULL;\n+\t\t\tchar *path;\n \t\t\tif (data[i] == '/') {\n \t\t\t\tserverlen = strchr(base + 8, '/') - base;\n \t\t\t\tokay = 1;\n@@ -540,6 +595,13 @@ static void process_alternates_response(\n \t\t\t\tnewalt->base = target;\n \t\t\t\tnewalt->got_indices = 0;\n \t\t\t\tnewalt->packs = NULL;\n+\t\t\t\tpath = strstr(target, \"//\");\n+\t\t\t\tif (path) {\n+\t\t\t\t\tpath = index(path+2, '/');\n+\t\t\t\t\tif (path)\n+\t\t\t\t\t\tnewalt->path_len = strlen(path);\n+\t\t\t\t}\n+\n \t\t\t\twhile (tail->next != NULL)\n \t\t\t\t\ttail = tail->next;\n \t\t\t\ttail->next = newalt;\n@@ -608,9 +670,212 @@ #endif\n \t\tgot_alternates = -1;\n \n \tfree(data);\n+\tfree(url);\n+}\n+\n+#ifndef NO_EXPAT\n+static void\n+xml_start_tag(void *userData, const char *name, const char **atts)\n+{\n+\tstruct xml_ctx *ctx = (struct xml_ctx *)userData;\n+\tconst char *c = index(name, ':');\n+\tint new_len;\n+\n+\tif (c == NULL)\n+\t\tc = name;\n+\telse\n+\t\tc++;\n+\n+\tnew_len = strlen(ctx->name) + strlen(c) + 2;\n+\n+\tif (new_len > ctx->len) {\n+\t\tctx->name = xrealloc(ctx->name, new_len);\n+\t\tctx->len = new_len;\n+\t}\n+\tstrcat(ctx->name, \".\");\n+\tstrcat(ctx->name, c);\n+\n+\tif (ctx->cdata) {\n+\t\tfree(ctx->cdata);\n+\t\tctx->cdata = NULL;\n+\t}\n+\n+\tctx->userFunc(ctx, 0);\n+}\n+\n+static void\n+xml_end_tag(void *userData, const char *name)\n+{\n+\tstruct xml_ctx *ctx = (struct xml_ctx *)userData;\n+\tconst char *c = index(name, ':');\n+\tchar *ep;\n+\n+\tctx->userFunc(ctx, 1);\n+\n+\tif (c == NULL)\n+\t\tc = name;\n+\telse\n+\t\tc++;\n+\n+\tep = ctx->name + strlen(ctx->name) - strlen(c) - 1;\n+\t*ep = 0;\n+}\n+\n+static void\n+xml_cdata(void *userData, const XML_Char *s, int len)\n+{\n+\tstruct xml_ctx *ctx = (struct xml_ctx *)userData;\n+\tif (ctx->cdata)\n+\t\tfree(ctx->cdata);\n+\tctx->cdata = xcalloc(len+1, 1);\n+\tstrncpy(ctx->cdata, s, len);\n+}\n+\n+static int remote_ls(struct alt_base *repo, const char *path, int flags,\n+\t\t     void (*userFunc)(struct remote_ls_ctx *ls),\n+\t\t     void *userData);\n+\n+static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)\n+{\n+\tstruct remote_ls_ctx *ls = (struct remote_ls_ctx *)ctx->userData;\n+\n+\tif (tag_closed) {\n+\t\tif (!strcmp(ctx->name, DAV_PROPFIND_RESP) && ls->dentry_name) {\n+\t\t\tif (ls->dentry_flags & IS_DIR) {\n+\t\t\t\tif (ls->flags & PROCESS_DIRS) {\n+\t\t\t\t\tls->userFunc(ls);\n+\t\t\t\t}\n+\t\t\t\tif (strcmp(ls->dentry_name, ls->path) &&\n+\t\t\t\t    ls->flags & RECURSIVE) {\n+\t\t\t\t\tls->rc = remote_ls(ls->repo,\n+\t\t\t\t\t\t\t   ls->dentry_name,\n+\t\t\t\t\t\t\t   ls->flags,\n+\t\t\t\t\t\t\t   ls->userFunc,\n+\t\t\t\t\t\t\t   ls->userData);\n+\t\t\t\t}\n+\t\t\t} else if (ls->flags & PROCESS_FILES) {\n+\t\t\t\tls->userFunc(ls);\n+\t\t\t}\n+\t\t} else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) {\n+\t\t\tls->dentry_name = xmalloc(strlen(ctx->cdata) -\n+\t\t\t\t\t\t  ls->repo->path_len + 1);\n+\t\t\tstrcpy(ls->dentry_name, ctx->cdata + ls->repo->path_len);\n+\t\t} else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {\n+\t\t\tls->dentry_flags |= IS_DIR;\n+\t\t}\n+\t} else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) {\n+\t\tif (ls->dentry_name) {\n+\t\t\tfree(ls->dentry_name);\n+\t\t}\n+\t\tls->dentry_name = NULL;\n+\t\tls->dentry_flags = 0;\n+\t}\n+}\n+\n+static int remote_ls(struct alt_base *repo, const char *path, int flags,\n+\t\t     void (*userFunc)(struct remote_ls_ctx *ls),\n+\t\t     void *userData)\n+{\n+\tchar *url = xmalloc(strlen(repo->base) + strlen(path) + 1);\n+\tstruct active_request_slot *slot;\n+\tstruct slot_results results;\n+\tstruct buffer in_buffer;\n+\tstruct buffer out_buffer;\n+\tchar *in_data;\n+\tchar *out_data;\n+\tXML_Parser parser = XML_ParserCreate(NULL);\n+\tenum XML_Status result;\n+\tstruct curl_slist *dav_headers = NULL;\n+\tstruct xml_ctx ctx;\n+\tstruct remote_ls_ctx ls;\n+\n+\tls.flags = flags;\n+\tls.repo = repo;\n+\tls.path = strdup(path);\n+\tls.dentry_name = NULL;\n+\tls.dentry_flags = 0;\n+\tls.userData = userData;\n+\tls.userFunc = userFunc;\n+\tls.rc = 0;\n+\n+\tsprintf(url, \"%s%s\", repo->base, path);\n+\n+\tout_buffer.size = strlen(PROPFIND_ALL_REQUEST);\n+\tout_data = xmalloc(out_buffer.size + 1);\n+\tsnprintf(out_data, out_buffer.size + 1, PROPFIND_ALL_REQUEST);\n+\tout_buffer.posn = 0;\n+\tout_buffer.buffer = out_data;\n+\n+\tin_buffer.size = 4096;\n+\tin_data = xmalloc(in_buffer.size);\n+\tin_buffer.posn = 0;\n+\tin_buffer.buffer = in_data;\n+\n+\tdav_headers = curl_slist_append(dav_headers, \"Depth: 1\");\n+\tdav_headers = curl_slist_append(dav_headers, \"Content-Type: text/xml\");\n+\n+\tslot = get_active_slot();\n+\tslot->results = &results;\n+\tcurl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_URL, url);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);\n+\n+\tif (start_active_slot(slot)) {\n+\t\trun_active_slot(slot);\n+\t\tif (results.curl_result == CURLE_OK) {\n+\t\t\tctx.name = xcalloc(10, 1);\n+\t\t\tctx.len = 0;\n+\t\t\tctx.cdata = NULL;\n+\t\t\tctx.userFunc = handle_remote_ls_ctx;\n+\t\t\tctx.userData = &ls;\n+\t\t\tXML_SetUserData(parser, &ctx);\n+\t\t\tXML_SetElementHandler(parser, xml_start_tag,\n+\t\t\t\t\t      xml_end_tag);\n+\t\t\tXML_SetCharacterDataHandler(parser, xml_cdata);\n+\t\t\tresult = XML_Parse(parser, in_buffer.buffer,\n+\t\t\t\t\t   in_buffer.posn, 1);\n+\t\t\tfree(ctx.name);\n+\n+\t\t\tif (result != XML_STATUS_OK) {\n+\t\t\t\tls.rc = error(\"XML error: %s\",\n+\t\t\t\t\t      XML_ErrorString(\n+\t\t\t\t\t\t      XML_GetErrorCode(parser)));\n+\t\t\t}\n+\t\t} else {\n+\t\t\tls.rc = -1;\n+\t\t}\n+\t} else {\n+\t\tls.rc = error(\"Unable to start PROPFIND request\");\n+\t}\n+\n+\tfree(ls.path);\n \tfree(url);\n+\tfree(out_data);\n+\tfree(in_buffer.buffer);\n+\tcurl_slist_free_all(dav_headers);\n+\n+\treturn ls.rc;\n }\n \n+static void process_ls_pack(struct remote_ls_ctx *ls)\n+{\n+\tunsigned char sha1[20];\n+\n+\tif (strlen(ls->dentry_name) == 63 &&\n+\t    !strncmp(ls->dentry_name, \"objects/pack/pack-\", 18) &&\n+\t    !strncmp(ls->dentry_name+58, \".pack\", 5)) {\n+\t\tget_sha1_hex(ls->dentry_name + 18, sha1);\n+\t\tsetup_index(ls->repo, sha1);\n+\t}\n+}\n+#endif\n+\n static int fetch_indices(struct alt_base *repo)\n {\n \tunsigned char sha1[20];\n@@ -632,6 +897,12 @@ static int fetch_indices(struct alt_base\n \n \tif (get_verbosely)\n \t\tfprintf(stderr, \"Getting pack list for %s\\n\", repo->base);\n+\n+#ifndef NO_EXPAT\n+\tif (remote_ls(repo, \"objects/pack/\", PROCESS_FILES,\n+\t\t      process_ls_pack, NULL) == 0)\n+\t\treturn 0;\n+#endif\n \n \turl = xmalloc(strlen(repo->base) + 21);\n \tsprintf(url, \"%s/objects/info/packs\", repo->base);\n@@ -947,6 +1218,7 @@ int main(int argc, char **argv)\n {\n \tchar *commit_id;\n \tchar *url;\n+\tchar *path;\n \tint arg = 1;\n \tint rc = 0;\n \n@@ -987,6 +1259,12 @@ int main(int argc, char **argv)\n \talt->got_indices = 0;\n \talt->packs = NULL;\n \talt->next = NULL;\n+\tpath = strstr(url, \"//\");\n+\tif (path) {\n+\t\tpath = index(path+2, '/');\n+\t\tif (path)\n+\t\t\talt->path_len = strlen(path);\n+\t}\n \n \tif (pull(commit_id))\n \t\trc = 1;\n-- \n1.3.0.rc1.gd4df-dirty\n"}]}