Skip to content

Commit ab31b93

Browse files
committed
SSH protocol support, via IPC::Open2
1 parent e098d50 commit ab31b93

File tree

4 files changed

+121
-30
lines changed

4 files changed

+121
-30
lines changed

lib/Git/PurePerl.pm

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -426,10 +426,20 @@ sub checkout {
426426
}
427427

428428
sub clone {
429-
my ( $self, $hostname, $project ) = @_;
429+
my $self = shift;
430+
431+
my $remote;
432+
if (@_ == 2) {
433+
# For backwards compatibility
434+
$remote = "git://$_[0]";
435+
$remote .= "/" unless $_[1] =~ m{^/};
436+
$remote .= $_[1];
437+
} else {
438+
$remote = shift;
439+
}
440+
430441
my $protocol = Git::PurePerl::Protocol->new(
431-
hostname => $hostname,
432-
project => $project,
442+
remote => $remote,
433443
);
434444

435445
my $sha1s = $protocol->connect;

lib/Git/PurePerl/Protocol.pm

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,33 @@ use Moose;
33
use MooseX::StrictConstructor;
44
use Moose::Util::TypeConstraints;
55

6-
has 'hostname' => ( is => 'ro', isa => 'Str', required => 1 );
7-
has 'port' => ( is => 'ro', isa => 'Int', required => 0, default => 9418 );
8-
has 'project' => ( is => 'ro', isa => 'Str', required => 1 );
9-
has 'socket' => ( is => 'rw', isa => 'IO::Socket', required => 0 );
6+
use Git::PurePerl::Protocol::Git;
7+
use Git::PurePerl::Protocol::SSH;
8+
use Git::PurePerl::Protocol::SSH;
9+
10+
has 'remote' => ( is => 'ro', isa => 'Str', required => 1 );
11+
has 'read_socket' => ( is => 'rw', required => 0 );
12+
has 'write_socket' => ( is => 'rw', required => 0 );
1013

1114
sub connect {
1215
my $self = shift;
1316

14-
my $socket = IO::Socket::INET->new(
15-
PeerAddr => $self->hostname,
16-
PeerPort => $self->port,
17-
Proto => 'tcp'
18-
) || die $! . ' ' . $self->hostname . ':' . $self->port;
19-
$socket->autoflush(1) || die $!;
20-
$self->socket($socket);
17+
if ($self->remote =~ m{^git://(.*?@)?(.*?)(/.*)}) {
18+
Git::PurePerl::Protocol::Git->meta->rebless_instance(
19+
$self,
20+
hostname => $2,
21+
project => $3,
22+
);
23+
} elsif ($self->remote =~ m{^(?:(.*?)@)?(.*?):(.*)}) {
24+
Git::PurePerl::Protocol::SSH->meta->rebless_instance(
25+
$self,
26+
$1 ? (username => $1) : (),
27+
hostname => $2,
28+
path => $3,
29+
);
30+
}
2131

22-
$self->send_line( "git-upload-pack "
23-
. $self->project
24-
. "\0host="
25-
. $self->hostname
26-
. "\0" );
32+
$self->connect_socket;
2733

2834
my %sha1s;
2935
while ( my $line = $self->read_line() ) {
@@ -77,19 +83,34 @@ sub send_line {
7783
my $text = $prefix . $line;
7884

7985
# warn "$text";
80-
$self->socket->print($text) || die $!;
86+
$self->write_socket->print($text) || die $!;
87+
}
88+
89+
sub read {
90+
my $self = shift;
91+
my $len = shift;
92+
93+
my $ret = "";
94+
use bytes;
95+
while (1) {
96+
my $got = $self->read_socket->read( my $data, $len - length($ret));
97+
if (not defined $got) {
98+
die "error: $!";
99+
} elsif ( $got == 0) {
100+
die "EOF"
101+
}
102+
$ret .= $data;
103+
if (length($ret) == $len) {
104+
return $ret;
105+
}
106+
}
81107
}
82108

83109
sub read_line {
84110
my $self = shift;
85-
my $socket = $self->socket;
111+
my $socket = $self->read_socket;
86112

87-
my $ret = $socket->read( my $prefix, 4 );
88-
if ( not defined $ret ) {
89-
die "error: $!";
90-
} elsif ( $ret == 0 ) {
91-
die "EOF";
92-
}
113+
my $prefix = $self->read( 4 );
93114

94115
return if $prefix eq '0000';
95116

@@ -109,9 +130,7 @@ sub read_line {
109130
}
110131
}
111132

112-
#say "len $len";
113-
$socket->read( my $data, $len - 4 ) || die $!;
114-
return $data;
133+
return $self->read( $len - 4 );
115134
}
116135

117136
1;

lib/Git/PurePerl/Protocol/Git.pm

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package Git::PurePerl::Protocol::Git;
2+
use Moose;
3+
use MooseX::StrictConstructor;
4+
use Moose::Util::TypeConstraints;
5+
6+
extends 'Git::PurePerl::Protocol';
7+
8+
has 'hostname' => ( is => 'ro', isa => 'Str', required => 1 );
9+
has 'port' => ( is => 'ro', isa => 'Int', required => 0, default => 9418 );
10+
has 'project' => ( is => 'rw', isa => 'Str', required => 1 );
11+
12+
sub connect_socket {
13+
my $self = shift;
14+
15+
my $socket = IO::Socket::INET->new(
16+
PeerAddr => $self->hostname,
17+
PeerPort => $self->port,
18+
Proto => 'tcp'
19+
) || die $! . ' ' . $self->hostname . ':' . $self->port;
20+
$socket->autoflush(1) || die $!;
21+
$self->read_socket($socket);
22+
$self->write_socket($socket);
23+
24+
$self->send_line( "git-upload-pack "
25+
. $self->project
26+
. "\0host="
27+
. $self->hostname
28+
. "\0" );
29+
}
30+
31+
1;

lib/Git/PurePerl/Protocol/SSH.pm

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package Git::PurePerl::Protocol::SSH;
2+
use Moose;
3+
use MooseX::StrictConstructor;
4+
use Moose::Util::TypeConstraints;
5+
use IPC::Open2;
6+
7+
extends 'Git::PurePerl::Protocol';
8+
9+
has 'hostname' => ( is => 'ro', isa => 'Str', required => 1 );
10+
has 'username' => ( is => 'ro', isa => 'Str', required => 0 );
11+
has 'path' => ( is => 'ro', isa => 'Str', required => 1 );
12+
13+
sub connect_socket {
14+
my $self = shift;
15+
16+
my ($read, $write);
17+
my $connect = join('@', grep {defined} $self->username, $self->hostname);
18+
my $pid = open2(
19+
$read, $write,
20+
"ssh", $connect,
21+
"-o", "BatchMode yes",
22+
"git-upload-pack", $self->path,
23+
);
24+
25+
$read->autoflush(1);
26+
$write->autoflush(1);
27+
$self->read_socket($read);
28+
$self->write_socket($write);
29+
}
30+
31+
1;

0 commit comments

Comments
 (0)