# Copyright (C) 2015-2016 all contributors # License: AGPL-3.0+ # 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;