diff options
context:
space:
mode:
authorBryce Kwon <bryce@brycekwon.com>
committerBryce Kwon <bryce@brycekwon.com>
commit
parent
tree
download
Send a clone file with its size in whole batches
The dumb transport read a pack four kilobytes at a time through stdio and sent it without a length, so a client could not tell a cut-off transfer from a complete one.
Diffstat (limited to 'source/ui-clone.c')
-rw-r--r--source/ui-clone.c22
1 file changed, 18 insertions, 4 deletions
diff --git a/source/ui-clone.c b/source/ui-clone.c
index 2544e9f..833c170 100644
--- a/source/ui-clone.c
+++ b/source/ui-clone.c
@@ -95,11 +95,20 @@ static int path_is_safe(const char *path)
return 1;
}
+/*
+ * The file is read in batches the size of the output buffer, so each one
+ * goes to the client in a single write, and its size is sent ahead so the
+ * client can tell a cut-off transfer from a complete one.
+ */
static void send_file(const char *path)
{
+ static char buf[HTML_BATCH];
struct stat st;
+ ssize_t got;
+ int fd;
- if (stat(path, &st)) {
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
switch (errno) {
case ENOENT:
cgit_print_error_page(404, "Not Found", "Not found");
@@ -112,8 +121,10 @@ static void send_file(const char *path)
}
return;
}
- // fopen opens a directory on most systems and reads nothing from it.
- if (!S_ISREG(st.st_mode)) {
+ // open succeeds on a directory on most systems and reads nothing from
+ // it.
+ if (fstat(fd, &st) || !S_ISREG(st.st_mode)) {
+ close(fd);
cgit_print_error_page(404, "Not Found", "Not found");
return;
}
@@ -123,8 +134,11 @@ static void send_file(const char *path)
ctx.page.filename = path;
skip_prefix(path, ctx.repo->path, &ctx.page.filename);
skip_prefix(ctx.page.filename, "/", &ctx.page.filename);
+ ctx.page.size = st.st_size;
cgit_print_http_headers();
- html_include(path);
+ while ((got = xread(fd, buf, sizeof(buf))) > 0)
+ html_raw(buf, got);
+ close(fd);
}
void cgit_clone_info(void)