From 2d8f83a3072970a51c45d2f72724696781ef6de8 Mon Sep 17 00:00:00 2001 From: Wolfgang Müller Date: Fri, 28 May 2021 16:03:01 +0200 Subject: Support Git over HTTP by proxying git-http-backend(1) cgit lacks an easy way to support the "smart" HTTP protocol out of the box. A patch [1] has been proposed in the past, but was never merged. A few years later there was a short discussion at [2] which did not go anywhere. The majority of users who want to support the "smart" HTTP protocol right now seem to conditionally point their web server to git-http-backend(1) directly. This relies on proper path-matching and regular expression support in the web server as seen in the EXMAPLES section of git-http-backend(1). As proposed in [3], it is possible to have cgit interface with the necessary functionality in Git directly, but that would require more work. It does not seem that this is something forthcoming in cgit. Instead, for now, use a modified version of the patch suggested in [1], which simply executes git-http-backend(1) when needed. This removes the need for any additional plumbing in the web server. Notable changes to the original patch: * Remove automatic handling of GIT_PROJECT_ROOT in favour of having the user set the variable correctly themselves. This reduces complexity. * Use the correct function to generate error pages. The original patch used html_status(), which does not exist anymore. * In cmd.c, adjust the struct entries to fit. Sadly we cannot use the def_cmd() macro directly, as the endpoints "git-upload-pack" and "git-receive-pack" contain dashes. [1] https://lists.zx2c4.com/pipermail/cgit/2014-December/002312.html [2] https://lists.zx2c4.com/pipermail/cgit/2016-February/002907.html [3] https://lists.zx2c4.com/pipermail/cgit/2016-February/002926.html --- cgit.c | 3 +++ cgit.h | 1 + cgitrc.5.txt | 8 ++++++++ cmd.c | 13 +++++++++++++ ui-clone.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ ui-clone.h | 2 ++ 6 files changed, 71 insertions(+) diff --git a/cgit.c b/cgit.c index c4320f0..fc85c18 100644 --- a/cgit.c +++ b/cgit.c @@ -151,6 +151,8 @@ static void config_cb(const char *name, const char *value) ctx.cfg.head_include = xstrdup(value); else if (!strcmp(name, "header")) ctx.cfg.header = xstrdup(value); + else if (!strcmp(name, "http-backend-path")) + ctx.cfg.http_backend_path = xstrdup(value); else if (!strcmp(name, "logo")) ctx.cfg.logo = xstrdup(value); else if (!strcmp(name, "logo-link")) @@ -379,6 +381,7 @@ static void prepare_context(void) ctx.cfg.css = "/cgit.css"; ctx.cfg.logo = "/cgit.png"; ctx.cfg.favicon = "/favicon.ico"; + ctx.cfg.http_backend_path = NULL; ctx.cfg.local_time = 0; ctx.cfg.enable_http_clone = 1; ctx.cfg.enable_index_owner = 1; diff --git a/cgit.h b/cgit.h index 7ec46b4..cd29892 100644 --- a/cgit.h +++ b/cgit.h @@ -200,6 +200,7 @@ struct cgit_config { char *footer; char *head_include; char *header; + char *http_backend_path; char *logo; char *logo_link; char *mimetype_file; diff --git a/cgitrc.5.txt b/cgitrc.5.txt index 33a6a8c..820cfa6 100644 --- a/cgitrc.5.txt +++ b/cgitrc.5.txt @@ -234,6 +234,11 @@ header:: The content of the file specified with this option will be included verbatim at the top of all pages. Default value: none. +http-backend-path:: + Path to the git-http-backend binary. Setting this allows the git clone to + fetch/push via Git over HTTP. You'll also need to set enable-http-clone + for this to work. See git-http-backend(1). Default value: none. + include:: Name of a configfile to include before the rest of the current config- file is parsed. Default value: none. See also: "MACRO EXPANSION". @@ -824,6 +829,9 @@ enable-index-owner=1 # Allow http transport git clone enable-http-clone=1 +# Use git-http-backend to serve Git over HTTP +http-backend-path=/usr/lib/git-core/git-http-backend + # Show extra links for each repository on the index page enable-index-links=1 diff --git a/cmd.c b/cmd.c index bf6d8f5..7f1ca98 100644 --- a/cmd.c +++ b/cmd.c @@ -164,6 +164,17 @@ static void tree_fn(void) cgit_print_tree(ctx.qry.sha1, ctx.qry.path); } +static void git_upload_pack_fn(void) +{ + cgit_clone_git_upload_pack(); +} + +static void git_receive_pack_fn(void) +{ + cgit_clone_git_receive_pack(); +} + + #define def_cmd(name, want_repo, want_vpath, is_clone) \ {#name, name##_fn, want_repo, want_vpath, is_clone} @@ -191,6 +202,8 @@ struct cgit_cmd *cgit_get_cmd(void) def_cmd(summary, 1, 0, 0), def_cmd(tag, 1, 0, 0), def_cmd(tree, 1, 1, 0), + {"git-upload-pack", git_upload_pack_fn, 1, 0, 1}, + {"git-receive-pack", git_receive_pack_fn, 1, 0, 1}, }; int i; diff --git a/ui-clone.c b/ui-clone.c index 5dccb63..3a9cfeb 100644 --- a/ui-clone.c +++ b/ui-clone.c @@ -77,8 +77,22 @@ static void send_file(const char *path) html_include(path); } +static void dispatch_to_git_http_backend(void) +{ + if (execl(ctx.cfg.http_backend_path, "git-http-backend", NULL) == -1) { + fprintf(stderr, "[cgit] http-backend-path (%s) could not be spawned: %s\n", + ctx.cfg.http_backend_path, strerror(errno)); + cgit_print_error_page(500, "Internal Server Error", "Internal Server Error"); + } +} + void cgit_clone_info(void) { + if (ctx.cfg.http_backend_path) { + dispatch_to_git_http_backend(); + return; + } + if (!ctx.qry.path || strcmp(ctx.qry.path, "refs")) { cgit_print_error_page(400, "Bad request", "Bad request"); return; @@ -94,6 +108,11 @@ void cgit_clone_objects(void) { char *p; + if (ctx.cfg.http_backend_path) { + dispatch_to_git_http_backend(); + return; + } + if (!ctx.qry.path) goto err; @@ -122,5 +141,30 @@ err: void cgit_clone_head(void) { + if (ctx.cfg.http_backend_path) { + dispatch_to_git_http_backend(); + return; + } + send_file(git_path("%s", "HEAD")); } + +void cgit_clone_git_upload_pack(void) +{ + if (ctx.cfg.http_backend_path) { + dispatch_to_git_http_backend(); + return; + } + + cgit_print_error_page(404, "Not found", "Not found"); +} + +void cgit_clone_git_receive_pack(void) +{ + if (ctx.cfg.http_backend_path) { + dispatch_to_git_http_backend(); + return; + } + + cgit_print_error_page(404, "Not found", "Not found"); +} diff --git a/ui-clone.h b/ui-clone.h index 3e460a3..b27087e 100644 --- a/ui-clone.h +++ b/ui-clone.h @@ -4,5 +4,7 @@ void cgit_clone_info(void); void cgit_clone_objects(void); void cgit_clone_head(void); +void cgit_clone_git_upload_pack(void); +void cgit_clone_git_receive_pack(void); #endif /* UI_CLONE_H */ -- cgit v1.2.3-2-gb3c3