@ -103,6 +103,7 @@ import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.storage.pack.PackStatistics ;
import org.eclipse.jgit.storage.pack.PackStatistics ;
import org.eclipse.jgit.transport.GitProtocolConstants.MultiAck ;
import org.eclipse.jgit.transport.GitProtocolConstants.MultiAck ;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser ;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser ;
import org.eclipse.jgit.transport.TransferConfig.ProtocolVersion ;
import org.eclipse.jgit.util.io.InterruptTimer ;
import org.eclipse.jgit.util.io.InterruptTimer ;
import org.eclipse.jgit.util.io.NullOutputStream ;
import org.eclipse.jgit.util.io.NullOutputStream ;
import org.eclipse.jgit.util.io.TimeoutInputStream ;
import org.eclipse.jgit.util.io.TimeoutInputStream ;
@ -112,6 +113,12 @@ import org.eclipse.jgit.util.io.TimeoutOutputStream;
* Implements the server side of a fetch connection , transmitting objects .
* Implements the server side of a fetch connection , transmitting objects .
* /
* /
public class UploadPack {
public class UploadPack {
// UploadPack sends these lines as the first response to a client that
// supports protocol version 2.
private static final String [ ] v2CapabilityAdvertisement = {
"version 2" ,
} ;
/** Policy the server uses to validate client requests */
/** Policy the server uses to validate client requests */
public static enum RequestPolicy {
public static enum RequestPolicy {
/** Client may only ask for objects the server advertised a reference for. */
/** Client may only ask for objects the server advertised a reference for. */
@ -238,6 +245,12 @@ public class UploadPack {
/** Timer to manage {@link #timeout}. */
/** Timer to manage {@link #timeout}. */
private InterruptTimer timer ;
private InterruptTimer timer ;
/ * *
* Whether the client requested to use protocol V2 through a side
* channel ( such as the Git - Protocol HTTP header ) .
* /
private boolean clientRequestedV2 ;
private InputStream rawIn ;
private InputStream rawIn ;
private ResponseBufferedOutputStream rawOut ;
private ResponseBufferedOutputStream rawOut ;
@ -656,9 +669,35 @@ public class UploadPack {
| | options . contains ( OPTION_SIDE_BAND_64K ) ) ;
| | options . contains ( OPTION_SIDE_BAND_64K ) ) ;
}
}
/ * *
* Set the Extra Parameters provided by the client .
*
* < p > These are parameters passed by the client through a side channel
* such as the Git - Protocol HTTP header , to allow a client to request
* a newer response format while remaining compatible with older servers
* that do not understand different request formats .
*
* @param params
* parameters supplied by the client , split at colons or NUL
* bytes .
* @since 5 . 0
* /
public void setExtraParameters ( Collection < String > params ) {
this . clientRequestedV2 = params . contains ( "version=2" ) ; // $NON-NLS-1$
}
private boolean useProtocolV2 ( ) {
return ProtocolVersion . V2 . equals ( transferConfig . protocolVersion )
& & clientRequestedV2 ;
}
/ * *
/ * *
* Execute the upload task on the socket .
* Execute the upload task on the socket .
*
*
* < p > If the client passed extra parameters ( e . g . , "version=2" ) through a
* side channel , the caller must call setExtraParameters first to supply
* them .
*
* @param input
* @param input
* raw input to read client commands from . Caller must ensure the
* raw input to read client commands from . Caller must ensure the
* input is buffered , otherwise read performance may suffer .
* input is buffered , otherwise read performance may suffer .
@ -699,7 +738,11 @@ public class UploadPack {
pckIn = new PacketLineIn ( rawIn ) ;
pckIn = new PacketLineIn ( rawIn ) ;
pckOut = new PacketLineOut ( rawOut ) ;
pckOut = new PacketLineOut ( rawOut ) ;
service ( ) ;
if ( useProtocolV2 ( ) ) {
serviceV2 ( ) ;
} else {
service ( ) ;
}
} finally {
} finally {
msgOut = NullOutputStream . INSTANCE ;
msgOut = NullOutputStream . INSTANCE ;
walk . close ( ) ;
walk . close ( ) ;
@ -821,6 +864,54 @@ public class UploadPack {
sendPack ( accumulator ) ;
sendPack ( accumulator ) ;
}
}
/ *
* Returns true if this is the last command and we should tear down the
* connection .
* /
private boolean serveOneCommandV2 ( ) throws IOException {
String command ;
try {
command = pckIn . readString ( ) ;
} catch ( EOFException eof ) {
/* EOF when awaiting command is fine */
return true ;
}
if ( command = = PacketLineIn . END ) {
// A blank request is valid according
// to the protocol; do nothing in this
// case.
return true ;
}
throw new PackProtocolException ( "unknown command " + command ) ;
}
private void serviceV2 ( ) throws IOException {
if ( biDirectionalPipe ) {
// Just like in service(), the capability advertisement
// is sent only if this is a bidirectional pipe. (If
// not, the client is expected to call
// sendAdvertisedRefs() on its own.)
for ( String s : v2CapabilityAdvertisement ) {
pckOut . writeString ( s + "\n" ) ;
}
pckOut . end ( ) ;
while ( ! serveOneCommandV2 ( ) ) {
// Repeat until an empty command or EOF.
}
return ;
}
try {
serveOneCommandV2 ( ) ;
} finally {
while ( 0 < rawIn . skip ( 2048 ) | | 0 < = rawIn . read ( ) ) {
// Discard until EOF.
}
rawOut . stopBuffering ( ) ;
}
}
private static Set < ObjectId > refIdSet ( Collection < Ref > refs ) {
private static Set < ObjectId > refIdSet ( Collection < Ref > refs ) {
Set < ObjectId > ids = new HashSet < > ( refs . size ( ) ) ;
Set < ObjectId > ids = new HashSet < > ( refs . size ( ) ) ;
for ( Ref ref : refs ) {
for ( Ref ref : refs ) {
@ -923,6 +1014,16 @@ public class UploadPack {
throw fail ;
throw fail ;
}
}
if ( useProtocolV2 ( ) ) {
// The equivalent in v2 is only the capabilities
// advertisement.
for ( String s : v2CapabilityAdvertisement ) {
adv . writeOne ( s ) ;
}
adv . end ( ) ;
return ;
}
adv . init ( db ) ;
adv . init ( db ) ;
adv . advertiseCapability ( OPTION_INCLUDE_TAG ) ;
adv . advertiseCapability ( OPTION_INCLUDE_TAG ) ;
adv . advertiseCapability ( OPTION_MULTI_ACK_DETAILED ) ;
adv . advertiseCapability ( OPTION_MULTI_ACK_DETAILED ) ;