git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [RFC PATCH 2/4] Git-aware CGI to provide dumb HTTP transport

From
J.H. <warthog9@kernel.org>
Date
Oct 9, 2009, 05:52 UTC
Message-ID
<4ACECFAA.9090109@kernel.org>
In-Reply-To
<1255065768-10428-3-git-send-email-spearce@spearce.org>

I dunno I kinda object to it being called http-backend, personally I'd rather it be called git-smart since this is the smart http protocol ;-)

- John 'Warthog9' Hawley
Shawn O. Pearce wrote:
Show 312 quoted lines
> The git-http-backend CGI can be configured into any Apache server
> using ScriptAlias, such as with the following configuration:
> 
>   LoadModule cgi_module /usr/libexec/apache2/mod_cgi.so
>   LoadModule alias_module /usr/libexec/apache2/mod_alias.so
>   ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
> 
> Repositories are accessed via the translated PATH_INFO.
> 
> The CGI is backwards compatible with the dumb client, allowing all
> older HTTP clients to continue to download repositories which are
> managed by the CGI.
> 
> Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
> ---
>  .gitignore     |    1 +
>  Makefile       |    1 +
>  http-backend.c |  261 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 263 insertions(+), 0 deletions(-)
>  create mode 100644 http-backend.c
> 
> diff --git a/.gitignore b/.gitignore
> index 51a37b1..353d22f 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -55,6 +55,7 @@ git-get-tar-commit-id
>  git-grep
>  git-hash-object
>  git-help
> +git-http-backend
>  git-http-fetch
>  git-http-push
>  git-imap-send
> diff --git a/Makefile b/Makefile
> index dd3d520..c80fb56 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -361,6 +361,7 @@ PROGRAMS += git-show-index$X
>  PROGRAMS += git-unpack-file$X
>  PROGRAMS += git-upload-pack$X
>  PROGRAMS += git-var$X
> +PROGRAMS += git-http-backend$X
>  
>  # List built-in command $C whose implementation cmd_$C() is not in
>  # builtin-$C.o but is linked in as part of some other command.
> diff --git a/http-backend.c b/http-backend.c
> new file mode 100644
> index 0000000..39cfd25
> --- /dev/null
> +++ b/http-backend.c
> @@ -0,0 +1,261 @@
> +#include "cache.h"
> +#include "refs.h"
> +#include "pkt-line.h"
> +#include "object.h"
> +#include "tag.h"
> +#include "exec_cmd.h"
> +#include "run-command.h"
> +
> +static const char content_type[] = "Content-Type";
> +static const char content_length[] = "Content-Length";
> +
> +static char buffer[1024];
> +
> +static const char *http_date(unsigned long time)
> +{
> +	return show_date(time, 0, DATE_RFC2822);
> +}
> +
> +static void format_write(const char *fmt, ...)
> +{
> +	va_list args;
> +	unsigned n;
> +
> +	va_start(args, fmt);
> +	n = vsnprintf(buffer, sizeof(buffer), fmt, args);
> +	va_end(args);
> +	if (n >= sizeof(buffer))
> +		die("protocol error: impossibly long line");
> +
> +	safe_write(1, buffer, n);
> +}
> +
> +static void write_status(unsigned code, const char *msg)
> +{
> +	format_write("Status: %u %s\r\n", code, msg);
> +}
> +
> +static void write_header(const char *name, const char *value)
> +{
> +	format_write("%s: %s\r\n", name, value);
> +}
> +
> +static void end_headers(void)
> +{
> +	safe_write(1, "\r\n", 2);
> +}
> +
> +static void write_nocache(void)
> +{
> +	write_header("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
> +	write_header("Pragma", "no-cache");
> +	write_header("Cache-Control", "no-cache, max-age=0, must-revalidate");
> +}
> +
> +static void write_cache_forever(void)
> +{
> +	unsigned long now = time(NULL);
> +	write_header("Date", http_date(now));
> +	write_header("Expires", http_date(now + 31536000));
> +	write_header("Cache-Control", "public, max-age=31536000");
> +}
> +
> +static NORETURN void not_found(const char *err, ...)
> +{
> +	va_list params;
> +
> +	write_status(404, "Not Found");
> +	write_nocache();
> +	end_headers();
> +
> +	va_start(params, err);
> +	if (err && *err) {
> +		vsnprintf(buffer, sizeof(buffer), err, params);
> +		fprintf(stderr, "%s\n", buffer);
> +	}
> +	va_end(params);
> +	exit(0);
> +}
> +
> +static void write_file(const char *the_type, const char *name)
> +{
> +	const char *p = git_path("%s", name);
> +	int fd;
> +	struct stat sb;
> +	uintmax_t remaining;
> +
> +	fd = open(p, O_RDONLY);
> +	if (fd < 0)
> +		not_found("Cannot open '%s': %s", p, strerror(errno));
> +	if (fstat(fd, &sb) < 0)
> +		die_errno("Cannot stat '%s'", p);
> +	remaining = (uintmax_t)sb.st_size;
> +
> +	write_header(content_type, the_type);
> +	write_header("Last-Modified", http_date(sb.st_mtime));
> +	format_write("Content-Length: %" PRIuMAX "\r\n", remaining);
> +	end_headers();
> +
> +	while (remaining) {
> +		ssize_t n = xread(fd, buffer, sizeof(buffer));
> +		if (n < 0)
> +			die_errno("Cannot read '%s'", p);
> +		n = safe_write(1, buffer, n);
> +		if (n <= 0)
> +			break;
> +	}
> +	close(fd);
> +}
> +
> +static void get_text_file(char *name)
> +{
> +	write_nocache();
> +	write_file("text/plain; charset=utf-8", name);
> +}
> +
> +static void get_loose_object(char *name)
> +{
> +	write_cache_forever();
> +	write_file("application/x-git-loose-object", name);
> +}
> +
> +static void get_pack_file(char *name)
> +{
> +	write_cache_forever();
> +	write_file("application/x-git-packed-objects", name);
> +}
> +
> +static void get_idx_file(char *name)
> +{
> +	write_cache_forever();
> +	write_file("application/x-git-packed-objects-toc", name);
> +}
> +
> +static int show_text_ref(const char *name, const unsigned char *sha1,
> +	int flag, void *cb_data)
> +{
> +	struct object *o = parse_object(sha1);
> +	if (!o)
> +		return 0;
> +
> +	format_write("%s\t%s\n", sha1_to_hex(sha1), name);
> +	if (o->type == OBJ_TAG) {
> +		o = deref_tag(o, name, 0);
> +		if (!o)
> +			return 0;
> +		format_write("%s\t%s^{}\n", sha1_to_hex(o->sha1), name);
> +	}
> +
> +	return 0;
> +}
> +
> +static void get_info_refs(char *arg)
> +{
> +	write_nocache();
> +	write_header(content_type, "text/plain; charset=utf-8");
> +	end_headers();
> +
> +	for_each_ref(show_text_ref, NULL);
> +}
> +
> +static void get_info_packs(char *arg)
> +{
> +	size_t objdirlen = strlen(get_object_directory());
> +	struct packed_git *p;
> +
> +	write_nocache();
> +	write_header(content_type, "text/plain; charset=utf-8");
> +	end_headers();
> +
> +	prepare_packed_git();
> +	for (p = packed_git; p; p = p->next) {
> +		if (!p->pack_local)
> +			continue;
> +		format_write("P %s\n", p->pack_name + objdirlen + 6);
> +	}
> +	safe_write(1, "\n", 1);
> +}
> +
> +static NORETURN void die_webcgi(const char *err, va_list params)
> +{
> +	write_status(500, "Internal Server Error");
> +	write_nocache();
> +	end_headers();
> +
> +	vsnprintf(buffer, sizeof(buffer), err, params);
> +	fprintf(stderr, "fatal: %s\n", buffer);
> +	exit(0);
> +}
> +
> +static struct service_cmd {
> +	const char *method;
> +	const char *pattern;
> +	void (*imp)(char *);
> +} services[] = {
> +	{"GET", "/HEAD$", get_text_file},
> +	{"GET", "/info/refs$", get_info_refs},
> +	{"GET", "/objects/info/packs$", get_info_packs},
> +	{"GET", "/objects/info/[^/]*$", get_text_file},
> +	{"GET", "/objects/[0-9a-f]{2}/[0-9a-f]{38}$", get_loose_object},
> +	{"GET", "/objects/pack/pack-[0-9a-f]{40}\\.pack$", get_pack_file},
> +	{"GET", "/objects/pack/pack-[0-9a-f]{40}\\.idx$", get_idx_file}
> +};
> +
> +int main(int argc, char **argv)
> +{
> +	char *dir = getenv("PATH_TRANSLATED");
> +	char *input_method = getenv("REQUEST_METHOD");
> +	struct service_cmd *cmd = NULL;
> +	char *cmd_arg = NULL;
> +	int i;
> +
> +	set_die_routine(die_webcgi);
> +
> +	if (!dir)
> +		die("No PATH_TRANSLATED from server");
> +	if (!input_method)
> +		die("No REQUEST_METHOD from server");
> +	if (!strcmp(input_method, "HEAD"))
> +		input_method = "GET";
> +
> +	for (i = 0; i < ARRAY_SIZE(services); i++) {
> +		struct service_cmd *c = &services[i];
> +		regex_t re;
> +		regmatch_t out[1];
> +
> +		if (regcomp(&re, c->pattern, REG_EXTENDED))
> +			die("Bogus regex in service table: %s", c->pattern);
> +		if (!regexec(&re, dir, 1, out, 0)) {
> +			size_t n = out[0].rm_eo - out[0].rm_so;
> +
> +			if (strcmp(input_method, c->method)) {
> +				const char *proto = getenv("SERVER_PROTOCOL");
> +				if (proto && !strcmp(proto, "HTTP/1.1"))
> +					write_status(405, "Method Not Allowed");
> +				else
> +					write_status(400, "Bad Request");
> +				write_nocache();
> +				end_headers();
> +				return 0;
> +			}
> +
> +			cmd = c;
> +			cmd_arg = xmalloc(n);
> +			strncpy(cmd_arg, dir + out[0].rm_so + 1, n);
> +			cmd_arg[n] = '\0';
> +			dir[out[0].rm_so] = 0;
> +			break;
> +		}
> +		regfree(&re);
> +	}
> +
> +	if (!cmd)
> +		not_found("Request not supported: '%s'", dir);
> +
> +	setup_path();
> +	if (!enter_repo(dir, 0))
> +		not_found("Not a git repository: '%s'", dir);
> +
> +	cmd->imp(cmd_arg);
> +	return 0;
> +}
Previous: Shawn O. PearceNext: Sverre Rabbelier
Message 6 of 46 in “Return of smart HTTP”
  1. 0/4 Return of smart HTTPShawn O. Pearce, Oct 9, 2009
  2. 1/4 Document the HTTP transport protocolShawn O. Pearce, Oct 9, 2009
  3. 2/4 Git-aware CGI to provide dumb HTTP transportShawn O. Pearce, Oct 9, 2009
  4. 3/4 Add smart-http options to upload-pack, receive-packShawn O. Pearce, Oct 9, 2009
  5. 4/4 Smart fetch and push over HTTP: server sideShawn O. Pearce, Oct 9, 2009
  6. J.H.Oct 9, 2009
  7. Sverre RabbelierOct 9, 2009
  8. Sverre RabbelierOct 9, 2009
  9. Alex BlewittOct 9, 2009
  10. Shawn O. PearceOct 15, 2009
  11. Jakub NarebskiOct 9, 2009
  12. Jeff KingOct 9, 2009
  13. Shawn O. PearceOct 15, 2009
  14. Jeff KingOct 15, 2009
  15. Junio C HamanoOct 9, 2009
  16. Antti-Juhani KaijanahoOct 10, 2009
  17. H. Peter AnvinOct 16, 2009
  18. Mike HommeyOct 16, 2009
  19. Shawn O. PearceOct 16, 2009
  20. Antti-Juhani KaijanahoOct 16, 2009
  21. Tay Ray ChuanApr 7, 2010
  22. Tay Ray ChuanApr 7, 2010
  23. (resend v2) Re: [RFC PATCH 1/4] Document the HTTP transport protocolTay Ray Chuan, Apr 7, 2010
  24. Junio C HamanoApr 7, 2010
  25. Tay Ray ChuanApr 8, 2010
  26. (resend v2) Re: [RFC PATCH 1/4] Document the HTTP transport protocolTay Ray Chuan, Apr 7, 2010
  27. Tay Ray ChuanOct 10, 2009
  28. Scott ChaconApr 6, 2010
  29. Junio C HamanoApr 6, 2010
  30. 00/14 document edits to original http protocol documentationTay Ray Chuan, Sep 10, 2013
  31. 01/14 Document the HTTP transport protocolTay Ray Chuan, Sep 10, 2013
  32. 02/14 normalize indentation with protcol-common.txtTay Ray Chuan, Sep 10, 2013
  33. 03/14 capitalize key words according to RFC 2119Tay Ray Chuan, Sep 10, 2013
  34. 04/14 normalize rules with RFC 5234Tay Ray Chuan, Sep 10, 2013
  35. 05/14 drop rules, etc. common to the pack protocolTay Ray Chuan, Sep 10, 2013
  36. 06/14 reword behaviour on missing repository or objectsTay Ray Chuan, Sep 10, 2013
  37. 07/14 weaken specification over cookies for authenticationTay Ray Chuan, Sep 10, 2013
  38. 08/14 mention different variations around $GIT_URLTay Ray Chuan, Sep 10, 2013
  39. 09/14 reduce ambiguity over '?' in $GIT_URL for dumb clientsTay Ray Chuan, Sep 10, 2013
  40. 10/14 fix example request/responsesTay Ray Chuan, Sep 10, 2013
  41. 11/14 be clearer in place of 'remote repository' phraseTay Ray Chuan, Sep 10, 2013
  42. 12/14 reduce confusion over smart server response behaviourTay Ray Chuan, Sep 10, 2013
  43. 13/14 shift dumb server response detailsTay Ray Chuan, Sep 10, 2013
  44. 14/14 mention effect of "allow-tip-sha1-in-want" capability on git-upload-packTay Ray Chuan, Sep 10, 2013
  45. 1/4 Document the HTTP transport protocolScott Chacon, Apr 6, 2010
  46. Junio C HamanoApr 6, 2010

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.