Skip to content

Commit 563b061

Browse files
committed
Merge branch 'cc/git-packet-pm'
Parts of a test to drive the long-running content filter interface has been split into its own module, hopefully to eventually become reusable. * cc/git-packet-pm: Git/Packet.pm: extract parts of t0021/rot13-filter.pl for reuse t0021/rot13-filter: add capability functions t0021/rot13-filter: refactor checking final lf t0021/rot13-filter: add packet_initialize() t0021/rot13-filter: improve error message t0021/rot13-filter: improve 'if .. elsif .. else' style t0021/rot13-filter: refactor packet reading functions t0021/rot13-filter: fix list comparison
2 parents b50d82b + 0fe8d51 commit 563b061

File tree

3 files changed

+202
-94
lines changed

3 files changed

+202
-94
lines changed

perl/Git/Packet.pm

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
package Git::Packet;
2+
use 5.008;
3+
use strict;
4+
use warnings;
5+
BEGIN {
6+
require Exporter;
7+
if ($] < 5.008003) {
8+
*import = \&Exporter::import;
9+
} else {
10+
# Exporter 5.57 which supports this invocation was
11+
# released with perl 5.8.3
12+
Exporter->import('import');
13+
}
14+
}
15+
16+
our @EXPORT = qw(
17+
packet_compare_lists
18+
packet_bin_read
19+
packet_txt_read
20+
packet_required_key_val_read
21+
packet_bin_write
22+
packet_txt_write
23+
packet_flush
24+
packet_initialize
25+
packet_read_capabilities
26+
packet_read_and_check_capabilities
27+
packet_check_and_write_capabilities
28+
);
29+
our @EXPORT_OK = @EXPORT;
30+
31+
sub packet_compare_lists {
32+
my ($expect, @result) = @_;
33+
my $ix;
34+
if (scalar @$expect != scalar @result) {
35+
return undef;
36+
}
37+
for ($ix = 0; $ix < $#result; $ix++) {
38+
if ($expect->[$ix] ne $result[$ix]) {
39+
return undef;
40+
}
41+
}
42+
return 1;
43+
}
44+
45+
sub packet_bin_read {
46+
my $buffer;
47+
my $bytes_read = read STDIN, $buffer, 4;
48+
if ( $bytes_read == 0 ) {
49+
# EOF - Git stopped talking to us!
50+
return ( -1, "" );
51+
} elsif ( $bytes_read != 4 ) {
52+
die "invalid packet: '$buffer'";
53+
}
54+
my $pkt_size = hex($buffer);
55+
if ( $pkt_size == 0 ) {
56+
return ( 1, "" );
57+
} elsif ( $pkt_size > 4 ) {
58+
my $content_size = $pkt_size - 4;
59+
$bytes_read = read STDIN, $buffer, $content_size;
60+
if ( $bytes_read != $content_size ) {
61+
die "invalid packet ($content_size bytes expected; $bytes_read bytes read)";
62+
}
63+
return ( 0, $buffer );
64+
} else {
65+
die "invalid packet size: $pkt_size";
66+
}
67+
}
68+
69+
sub remove_final_lf_or_die {
70+
my $buf = shift;
71+
unless ( $buf =~ s/\n$// ) {
72+
die "A non-binary line MUST be terminated by an LF.\n"
73+
. "Received: '$buf'";
74+
}
75+
return $buf;
76+
}
77+
78+
sub packet_txt_read {
79+
my ( $res, $buf ) = packet_bin_read();
80+
unless ( $res == -1 or $buf eq '' ) {
81+
$buf = remove_final_lf_or_die($buf);
82+
}
83+
return ( $res, $buf );
84+
}
85+
86+
sub packet_required_key_val_read {
87+
my ( $key ) = @_;
88+
my ( $res, $buf ) = packet_txt_read();
89+
unless ( $res == -1 or ( $buf =~ s/^$key=// and $buf ne '' ) ) {
90+
die "bad $key: '$buf'";
91+
}
92+
return ( $res, $buf );
93+
}
94+
95+
sub packet_bin_write {
96+
my $buf = shift;
97+
print STDOUT sprintf( "%04x", length($buf) + 4 );
98+
print STDOUT $buf;
99+
STDOUT->flush();
100+
}
101+
102+
sub packet_txt_write {
103+
packet_bin_write( $_[0] . "\n" );
104+
}
105+
106+
sub packet_flush {
107+
print STDOUT sprintf( "%04x", 0 );
108+
STDOUT->flush();
109+
}
110+
111+
sub packet_initialize {
112+
my ($name, $version) = @_;
113+
114+
packet_compare_lists([0, $name . "-client"], packet_txt_read()) ||
115+
die "bad initialize";
116+
packet_compare_lists([0, "version=" . $version], packet_txt_read()) ||
117+
die "bad version";
118+
packet_compare_lists([1, ""], packet_bin_read()) ||
119+
die "bad version end";
120+
121+
packet_txt_write( $name . "-server" );
122+
packet_txt_write( "version=" . $version );
123+
packet_flush();
124+
}
125+
126+
sub packet_read_capabilities {
127+
my @cap;
128+
while (1) {
129+
my ( $res, $buf ) = packet_bin_read();
130+
if ( $res == -1 ) {
131+
die "unexpected EOF when reading capabilities";
132+
}
133+
return ( $res, @cap ) if ( $res != 0 );
134+
$buf = remove_final_lf_or_die($buf);
135+
unless ( $buf =~ s/capability=// ) {
136+
die "bad capability buf: '$buf'";
137+
}
138+
push @cap, $buf;
139+
}
140+
}
141+
142+
# Read remote capabilities and check them against capabilities we require
143+
sub packet_read_and_check_capabilities {
144+
my @required_caps = @_;
145+
my ($res, @remote_caps) = packet_read_capabilities();
146+
my %remote_caps = map { $_ => 1 } @remote_caps;
147+
foreach (@required_caps) {
148+
unless (exists($remote_caps{$_})) {
149+
die "required '$_' capability not available from remote" ;
150+
}
151+
}
152+
return %remote_caps;
153+
}
154+
155+
# Check our capabilities we want to advertise against the remote ones
156+
# and then advertise our capabilities
157+
sub packet_check_and_write_capabilities {
158+
my ($remote_caps, @our_caps) = @_;
159+
foreach (@our_caps) {
160+
unless (exists($remote_caps->{$_})) {
161+
die "our capability '$_' is not available from remote"
162+
}
163+
packet_txt_write( "capability=" . $_ );
164+
}
165+
packet_flush();
166+
}
167+
168+
1;

perl/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ instdir_SQ = $(subst ','\'',$(prefix)/lib)
3030
modules += Git
3131
modules += Git/I18N
3232
modules += Git/IndexInfo
33+
modules += Git/Packet
3334
modules += Git/SVN
3435
modules += Git/SVN/Memoize/YAML
3536
modules += Git/SVN/Fetcher

t/t0021/rot13-filter.pl

Lines changed: 33 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,12 @@
3030
# to the "list_available_blobs" response.
3131
#
3232

33+
use 5.008;
34+
use lib (split(/:/, $ENV{GITPERLLIB}));
3335
use strict;
3436
use warnings;
3537
use IO::File;
38+
use Git::Packet;
3639

3740
my $MAX_PACKET_CONTENT_SIZE = 65516;
3841
my $log_file = shift @ARGV;
@@ -55,89 +58,30 @@ sub rot13 {
5558
return $str;
5659
}
5760

58-
sub packet_bin_read {
59-
my $buffer;
60-
my $bytes_read = read STDIN, $buffer, 4;
61-
if ( $bytes_read == 0 ) {
62-
# EOF - Git stopped talking to us!
63-
print $debug "STOP\n";
64-
exit();
65-
}
66-
elsif ( $bytes_read != 4 ) {
67-
die "invalid packet: '$buffer'";
68-
}
69-
my $pkt_size = hex($buffer);
70-
if ( $pkt_size == 0 ) {
71-
return ( 1, "" );
72-
}
73-
elsif ( $pkt_size > 4 ) {
74-
my $content_size = $pkt_size - 4;
75-
$bytes_read = read STDIN, $buffer, $content_size;
76-
if ( $bytes_read != $content_size ) {
77-
die "invalid packet ($content_size bytes expected; $bytes_read bytes read)";
78-
}
79-
return ( 0, $buffer );
80-
}
81-
else {
82-
die "invalid packet size: $pkt_size";
83-
}
84-
}
85-
86-
sub packet_txt_read {
87-
my ( $res, $buf ) = packet_bin_read();
88-
unless ( $buf eq '' or $buf =~ s/\n$// ) {
89-
die "A non-binary line MUST be terminated by an LF.";
90-
}
91-
return ( $res, $buf );
92-
}
93-
94-
sub packet_bin_write {
95-
my $buf = shift;
96-
print STDOUT sprintf( "%04x", length($buf) + 4 );
97-
print STDOUT $buf;
98-
STDOUT->flush();
99-
}
100-
101-
sub packet_txt_write {
102-
packet_bin_write( $_[0] . "\n" );
103-
}
104-
105-
sub packet_flush {
106-
print STDOUT sprintf( "%04x", 0 );
107-
STDOUT->flush();
108-
}
109-
11061
print $debug "START\n";
11162
$debug->flush();
11263

113-
( packet_txt_read() eq ( 0, "git-filter-client" ) ) || die "bad initialize";
114-
( packet_txt_read() eq ( 0, "version=2" ) ) || die "bad version";
115-
( packet_bin_read() eq ( 1, "" ) ) || die "bad version end";
64+
packet_initialize("git-filter", 2);
11665

117-
packet_txt_write("git-filter-server");
118-
packet_txt_write("version=2");
119-
packet_flush();
66+
my %remote_caps = packet_read_and_check_capabilities("clean", "smudge", "delay");
67+
packet_check_and_write_capabilities(\%remote_caps, @capabilities);
12068

121-
( packet_txt_read() eq ( 0, "capability=clean" ) ) || die "bad capability";
122-
( packet_txt_read() eq ( 0, "capability=smudge" ) ) || die "bad capability";
123-
( packet_txt_read() eq ( 0, "capability=delay" ) ) || die "bad capability";
124-
( packet_bin_read() eq ( 1, "" ) ) || die "bad capability end";
125-
126-
foreach (@capabilities) {
127-
packet_txt_write( "capability=" . $_ );
128-
}
129-
packet_flush();
13069
print $debug "init handshake complete\n";
13170
$debug->flush();
13271

13372
while (1) {
134-
my ( $command ) = packet_txt_read() =~ /^command=(.+)$/;
73+
my ( $res, $command ) = packet_required_key_val_read("command");
74+
if ( $res == -1 ) {
75+
print $debug "STOP\n";
76+
exit();
77+
}
13578
print $debug "IN: $command";
13679
$debug->flush();
13780

13881
if ( $command eq "list_available_blobs" ) {
13982
# Flush
140-
packet_bin_read();
83+
packet_compare_lists([1, ""], packet_bin_read()) ||
84+
die "bad list_available_blobs end";
14185

14286
foreach my $pathname ( sort keys %DELAY ) {
14387
if ( $DELAY{$pathname}{"requested"} >= 1 ) {
@@ -161,16 +105,14 @@ sub packet_flush {
161105
$debug->flush();
162106
packet_txt_write("status=success");
163107
packet_flush();
164-
}
165-
else {
166-
my ( $pathname ) = packet_txt_read() =~ /^pathname=(.+)$/;
108+
} else {
109+
my ( $res, $pathname ) = packet_required_key_val_read("pathname");
110+
if ( $res == -1 ) {
111+
die "unexpected EOF while expecting pathname";
112+
}
167113
print $debug " $pathname";
168114
$debug->flush();
169115

170-
if ( $pathname eq "" ) {
171-
die "bad pathname '$pathname'";
172-
}
173-
174116
# Read until flush
175117
my ( $done, $buffer ) = packet_txt_read();
176118
while ( $buffer ne '' ) {
@@ -184,6 +126,9 @@ sub packet_flush {
184126

185127
( $done, $buffer ) = packet_txt_read();
186128
}
129+
if ( $done == -1 ) {
130+
die "unexpected EOF after pathname '$pathname'";
131+
}
187132

188133
my $input = "";
189134
{
@@ -194,24 +139,23 @@ sub packet_flush {
194139
( $done, $buffer ) = packet_bin_read();
195140
$input .= $buffer;
196141
}
142+
if ( $done == -1 ) {
143+
die "unexpected EOF while reading input for '$pathname'";
144+
}
197145
print $debug " " . length($input) . " [OK] -- ";
198146
$debug->flush();
199147
}
200148

201149
my $output;
202150
if ( exists $DELAY{$pathname} and exists $DELAY{$pathname}{"output"} ) {
203151
$output = $DELAY{$pathname}{"output"}
204-
}
205-
elsif ( $pathname eq "error.r" or $pathname eq "abort.r" ) {
152+
} elsif ( $pathname eq "error.r" or $pathname eq "abort.r" ) {
206153
$output = "";
207-
}
208-
elsif ( $command eq "clean" and grep( /^clean$/, @capabilities ) ) {
154+
} elsif ( $command eq "clean" and grep( /^clean$/, @capabilities ) ) {
209155
$output = rot13($input);
210-
}
211-
elsif ( $command eq "smudge" and grep( /^smudge$/, @capabilities ) ) {
156+
} elsif ( $command eq "smudge" and grep( /^smudge$/, @capabilities ) ) {
212157
$output = rot13($input);
213-
}
214-
else {
158+
} else {
215159
die "bad command '$command'";
216160
}
217161

@@ -220,25 +164,21 @@ sub packet_flush {
220164
$debug->flush();
221165
packet_txt_write("status=error");
222166
packet_flush();
223-
}
224-
elsif ( $pathname eq "abort.r" ) {
167+
} elsif ( $pathname eq "abort.r" ) {
225168
print $debug "[ABORT]\n";
226169
$debug->flush();
227170
packet_txt_write("status=abort");
228171
packet_flush();
229-
}
230-
elsif ( $command eq "smudge" and
172+
} elsif ( $command eq "smudge" and
231173
exists $DELAY{$pathname} and
232-
$DELAY{$pathname}{"requested"} == 1
233-
) {
174+
$DELAY{$pathname}{"requested"} == 1 ) {
234175
print $debug "[DELAYED]\n";
235176
$debug->flush();
236177
packet_txt_write("status=delayed");
237178
packet_flush();
238179
$DELAY{$pathname}{"requested"} = 2;
239180
$DELAY{$pathname}{"output"} = $output;
240-
}
241-
else {
181+
} else {
242182
packet_txt_write("status=success");
243183
packet_flush();
244184

@@ -258,8 +198,7 @@ sub packet_flush {
258198
print $debug ".";
259199
if ( length($output) > $MAX_PACKET_CONTENT_SIZE ) {
260200
$output = substr( $output, $MAX_PACKET_CONTENT_SIZE );
261-
}
262-
else {
201+
} else {
263202
$output = "";
264203
}
265204
}

0 commit comments

Comments
 (0)