--- /dev/null
+package Git::PurePerl::Protocol;
+use Moose;
+use MooseX::StrictConstructor;
+use Moose::Util::TypeConstraints;
+use namespace::autoclean;
+
+has 'hostname' => ( is => 'ro', isa => 'Str', required => 1 );
+has 'port' => ( is => 'ro', isa => 'Int', required => 0, default => 9418 );
+has 'project' => ( is => 'ro', isa => 'Str', required => 1 );
+has 'socket' => ( is => 'rw', isa => 'IO::Socket', required => 0 );
+
+sub connect {
+ my $self = shift;
+
+ my $socket = IO::Socket::INET->new(
+ PeerAddr => $self->hostname,
+ PeerPort => $self->port,
+ Proto => 'tcp'
+ ) || die $! . ' ' . $self->hostname . ':' . $self->port;
+ $socket->autoflush(1) || die $!;
+ $self->socket($socket);
+
+ $self->send_line( "git-upload-pack "
+ . $self->project
+ . "\0host="
+ . $self->hostname
+ . "\0" );
+
+ my %sha1s;
+ while ( my $line = $self->read_line() ) {
+
+ # warn "S $line";
+ my ( $sha1, $name ) = $line =~ /^([a-z0-9]+) ([^\0\n]+)/;
+
+ #use YAML; warn Dump $line;
+ $sha1s{$name} = $sha1;
+ }
+ return \%sha1s;
+}
+
+sub fetch_pack {
+ my ( $self, $sha1 ) = @_;
+ $self->send_line("want $sha1 side-band-64k\n");
+
+#send_line(
+# "want 0c7b3d23c0f821e58cd20e60d5e63f5ed12ef391 multi_ack side-band-64k ofs-delta\n"
+#);
+ $self->send_line('');
+ $self->send_line('done');
+
+ my $pack;
+
+ while ( my $line = $self->read_line() ) {
+ if ( $line =~ s/^\x02// ) {
+ print $line;
+ } elsif ( $line =~ /^NAK\n/ ) {
+ } elsif ( $line =~ s/^\x01// ) {
+ $pack .= $line;
+ } else {
+ die "Unknown line: $line";
+ }
+
+ #say "s $line";
+ }
+ return $pack;
+}
+
+sub send_line {
+ my ( $self, $line ) = @_;
+ my $length = length($line);
+ if ( $length == 0 ) {
+ } else {
+ $length += 4;
+ }
+
+ #warn "length $length";
+ my $prefix = sprintf( "%04X", $length );
+ my $text = $prefix . $line;
+
+ # warn "$text";
+ $self->socket->print($text) || die $!;
+}
+
+sub read_line {
+ my $self = shift;
+ my $socket = $self->socket;
+
+ my $ret = $socket->read( my $prefix, 4 );
+ if ( not defined $ret ) {
+ die "error: $!";
+ } elsif ( $ret == 0 ) {
+ die "EOF";
+ }
+
+ return if $prefix eq '0000';
+
+ # warn "read prefix [$prefix]";
+
+ my $len = 0;
+ foreach my $n ( 0 .. 3 ) {
+ my $c = substr( $prefix, $n, 1 );
+ $len <<= 4;
+
+ if ( $c ge '0' && $c le '9' ) {
+ $len += ord($c) - ord('0');
+ } elsif ( $c ge 'a' && $c le 'f' ) {
+ $len += ord($c) - ord('a') + 10;
+ } elsif ( $c ge 'A' && $c le 'F' ) {
+ $len += ord($c) - ord('A') + 10;
+ }
+ }
+
+ #say "len $len";
+ $socket->read( my $data, $len - 4 ) || die $!;
+ return $data;
+}
+
+__PACKAGE__->meta->make_immutable;