about summary refs log tree commit homepage
path: root/lib/PublicInbox/RepoGitBlob.pm
blob: ca8a3c2f5bd5cbd9c1499565d8f0b6795e1dba15 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# Copyright (C) 2015-2016 all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>

# Show a blob as-is
package PublicInbox::RepoGitBlob;
use strict;
use warnings;
use base qw(PublicInbox::RepoBase);
use base qw(Exporter);
our @EXPORT = qw(git_blob_mime_type git_blob_stream_response);

sub call_git_blob {
	my ($self, $req) = @_;
	my $git = $req->{-repo}->{git};
	my $id = $req->{-tip} . ':' . $req->{expath};

	my ($cat, $hex, $type, $size) = $git->cat_file_begin($id);
	return unless defined $cat;

	my ($r, $buf);
	my $left = $size;
	if ($type eq 'blob') {
		$type = git_blob_mime_type($self, $req, $cat, \$buf, \$left);
	} elsif ($type eq 'commit' || $type eq 'tag') {
		$type = 'text/plain; charset=UTF-8';
	} else {
		$type = 'application/octet-stream';
	}
	git_blob_stream_response($git, $cat, $size, $type, $buf, $left);
}

sub git_blob_mime_type {
	my ($self, $req, $cat, $buf, $left) = @_;
	my $base = $req->{extra}->[-1];
	my $type = $self->mime_type($base) if defined $base;
	return $type if $type;

	my $to_read = 8000; # git uses this size to detect binary files
	$to_read = $$left if $to_read > $$left;
	my $r = read($cat, $$buf, $to_read);
	if (!defined $r || $r <= 0) {
		my $git = $req->{-repo}->{git};
		$git->cat_file_finish($$left);
		return;
	}
	$$left -= $r;
	(index($buf, "\0") < 0) ? 'text/plain; charset=UTF-8'
				: 'application/octet-stream';
}

sub git_blob_stream_response {
	my ($git, $cat, $size, $type, $buf, $left) = @_;

	sub {
		my ($res) = @_;
		my $to_read = 8192;
		eval {
			my $fh = $res->([ 200, ['Content-Length' => $size,
						'Content-Type' => $type]]);
			$fh->write($buf) if defined $buf;
			while ($left > 0) {
				$to_read = $left if $to_read > $left;
				my $r = read($cat, $buf, $to_read);
				last if (!defined $r || $r <= 0);
				$left -= $r;
				$fh->write($buf);
			}
			$fh->close;
		};
		$git->cat_file_finish($left);
	}
}

1;