8 static const char content_type
[] = "Content-Type";
9 static const char content_length
[] = "Content-Length";
10 static const char last_modified
[] = "Last-Modified";
12 static void format_write(int fd
, const char *fmt
, ...)
14 static char buffer
[1024];
20 n
= vsnprintf(buffer
, sizeof(buffer
), fmt
, args
);
22 if (n
>= sizeof(buffer
))
23 die("protocol error: impossibly long line");
25 safe_write(fd
, buffer
, n
);
28 static void http_status(unsigned code
, const char *msg
)
30 format_write(1, "Status: %u %s\r\n", code
, msg
);
33 static void hdr_str(const char *name
, const char *value
)
35 format_write(1, "%s: %s\r\n", name
, value
);
38 static void hdr_int(const char *name
, size_t value
)
40 format_write(1, "%s: %" PRIuMAX
"\r\n", name
, value
);
43 static void hdr_date(const char *name
, unsigned long when
)
45 const char *value
= show_date(when
, 0, DATE_RFC2822
);
49 static void hdr_nocache(void)
51 hdr_str("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
52 hdr_str("Pragma", "no-cache");
53 hdr_str("Cache-Control", "no-cache, max-age=0, must-revalidate");
56 static void hdr_cache_forever(void)
58 unsigned long now
= time(NULL
);
59 hdr_date("Date", now
);
60 hdr_date("Expires", now
+ 31536000);
61 hdr_str("Cache-Control", "public, max-age=31536000");
64 static void end_headers(void)
66 safe_write(1, "\r\n", 2);
69 static NORETURN
void not_found(const char *err
, ...)
73 http_status(404, "Not Found");
77 va_start(params
, err
);
79 vfprintf(stderr
, err
, params
);
84 static void send_strbuf(const char *type
, struct strbuf
*buf
)
86 hdr_int(content_length
, buf
->len
);
87 hdr_str(content_type
, type
);
89 safe_write(1, buf
->buf
, buf
->len
);
92 static void send_file(const char *the_type
, const char *name
)
94 const char *p
= git_path("%s", name
);
95 size_t buf_alloc
= 8192;
96 char *buf
= xmalloc(buf_alloc
);
101 fd
= open(p
, O_RDONLY
);
103 not_found("Cannot open '%s': %s", p
, strerror(errno
));
104 if (fstat(fd
, &sb
) < 0)
105 die_errno("Cannot stat '%s'", p
);
107 size
= xsize_t(sb
.st_size
);
109 hdr_int(content_length
, size
);
110 hdr_str(content_type
, the_type
);
111 hdr_date(last_modified
, sb
.st_mtime
);
115 ssize_t n
= xread(fd
, buf
, buf_alloc
);
117 die_errno("Cannot read '%s'", p
);
120 safe_write(1, buf
, n
);
126 static void get_text_file(char *name
)
129 send_file("text/plain", name
);
132 static void get_loose_object(char *name
)
135 send_file("application/x-git-loose-object", name
);
138 static void get_pack_file(char *name
)
141 send_file("application/x-git-packed-objects", name
);
144 static void get_idx_file(char *name
)
147 send_file("application/x-git-packed-objects-toc", name
);
150 static int show_text_ref(const char *name
, const unsigned char *sha1
,
151 int flag
, void *cb_data
)
153 struct strbuf
*buf
= cb_data
;
154 struct object
*o
= parse_object(sha1
);
158 strbuf_addf(buf
, "%s\t%s\n", sha1_to_hex(sha1
), name
);
159 if (o
->type
== OBJ_TAG
) {
160 o
= deref_tag(o
, name
, 0);
163 strbuf_addf(buf
, "%s\t%s^{}\n", sha1_to_hex(o
->sha1
), name
);
168 static void get_info_refs(char *arg
)
170 struct strbuf buf
= STRBUF_INIT
;
172 for_each_ref(show_text_ref
, &buf
);
174 send_strbuf("text/plain", &buf
);
175 strbuf_release(&buf
);
178 static void get_info_packs(char *arg
)
180 size_t objdirlen
= strlen(get_object_directory());
181 struct strbuf buf
= STRBUF_INIT
;
182 struct packed_git
*p
;
185 prepare_packed_git();
186 for (p
= packed_git
; p
; p
= p
->next
) {
191 strbuf_grow(&buf
, cnt
* 53 + 2);
192 for (p
= packed_git
; p
; p
= p
->next
) {
194 strbuf_addf(&buf
, "P %s\n", p
->pack_name
+ objdirlen
+ 6);
196 strbuf_addch(&buf
, '\n');
199 send_strbuf("text/plain; charset=utf-8", &buf
);
200 strbuf_release(&buf
);
203 static NORETURN
void die_webcgi(const char *err
, va_list params
)
207 http_status(500, "Internal Server Error");
211 vsnprintf(buffer
, sizeof(buffer
), err
, params
);
212 fprintf(stderr
, "fatal: %s\n", buffer
);
216 static struct service_cmd
{
221 {"GET", "/HEAD$", get_text_file
},
222 {"GET", "/info/refs$", get_info_refs
},
223 {"GET", "/objects/info/alternates$", get_text_file
},
224 {"GET", "/objects/info/http-alternates$", get_text_file
},
225 {"GET", "/objects/info/packs$", get_info_packs
},
226 {"GET", "/objects/[0-9a-f]{2}/[0-9a-f]{38}$", get_loose_object
},
227 {"GET", "/objects/pack/pack-[0-9a-f]{40}\\.pack$", get_pack_file
},
228 {"GET", "/objects/pack/pack-[0-9a-f]{40}\\.idx$", get_idx_file
}
231 int main(int argc
, char **argv
)
233 char *method
= getenv("REQUEST_METHOD");
234 char *dir
= getenv("PATH_TRANSLATED");
235 struct service_cmd
*cmd
= NULL
;
236 char *cmd_arg
= NULL
;
239 git_extract_argv0_path(argv
[0]);
240 set_die_routine(die_webcgi
);
243 die("No REQUEST_METHOD from server");
244 if (!strcmp(method
, "HEAD"))
247 die("No PATH_TRANSLATED from server");
249 for (i
= 0; i
< ARRAY_SIZE(services
); i
++) {
250 struct service_cmd
*c
= &services
[i
];
254 if (regcomp(&re
, c
->pattern
, REG_EXTENDED
))
255 die("Bogus regex in service table: %s", c
->pattern
);
256 if (!regexec(&re
, dir
, 1, out
, 0)) {
257 size_t n
= out
[0].rm_eo
- out
[0].rm_so
;
259 if (strcmp(method
, c
->method
)) {
260 const char *proto
= getenv("SERVER_PROTOCOL");
261 if (proto
&& !strcmp(proto
, "HTTP/1.1"))
262 http_status(405, "Method Not Allowed");
264 http_status(400, "Bad Request");
271 cmd_arg
= xmalloc(n
);
272 strncpy(cmd_arg
, dir
+ out
[0].rm_so
+ 1, n
);
274 dir
[out
[0].rm_so
] = 0;
281 not_found("Request not supported: '%s'", dir
);
284 if (!enter_repo(dir
, 0))
285 not_found("Not a git repository: '%s'", dir
);