diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java index 8fdf386ec..26b4d88f5 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java @@ -45,6 +45,7 @@ package org.eclipse.jgit.transport; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -52,6 +53,8 @@ import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.EOFException; import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; import org.eclipse.jgit.errors.PackProtocolException; import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription; @@ -60,6 +63,7 @@ import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.PushCertificate.NonceStatus; import org.junit.Before; import org.junit.Test; @@ -274,6 +278,62 @@ public class PushCertificateParserTest { assertEquals("line 2\nline 3\n", concatPacketLines(input, 1, 4)); } + @Test + public void testParseReader() throws Exception { + Reader reader = new InputStreamReader( + new ByteArrayInputStream( + Constants.encode(concatPacketLines(INPUT, 0, 18)))); + PushCertificate streamCert = PushCertificateParser.fromReader(reader); + + PacketLineIn pckIn = newPacketLineIn(INPUT); + PushCertificateParser pckParser = + new PushCertificateParser(db, newEnabledConfig()); + pckParser.receiveHeader(pckIn, false); + pckParser.addCommand(pckIn.readString()); + assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readString()); + pckParser.receiveSignature(pckIn); + PushCertificate pckCert = pckParser.build(); + + // Nonce status is unsolicited since this was not parsed in the context of + // the wire protocol; as a result, certs are not actually equal. + assertEquals(NonceStatus.UNSOLICITED, streamCert.getNonceStatus()); + + assertEquals(pckCert.getVersion(), streamCert.getVersion()); + assertEquals(pckCert.getPusherIdent().getName(), + streamCert.getPusherIdent().getName()); + assertEquals(pckCert.getPusherIdent().getEmailAddress(), + streamCert.getPusherIdent().getEmailAddress()); + assertEquals(pckCert.getPusherIdent().getWhen().getTime(), + streamCert.getPusherIdent().getWhen().getTime()); + assertEquals(pckCert.getPusherIdent().getTimeZoneOffset(), + streamCert.getPusherIdent().getTimeZoneOffset()); + assertEquals(pckCert.getPushee(), streamCert.getPushee()); + assertEquals(pckCert.getNonce(), streamCert.getNonce()); + assertEquals(pckCert.getSignature(), streamCert.getSignature()); + assertEquals(pckCert.toText(), streamCert.toText()); + + assertEquals(pckCert.getCommands().size(), streamCert.getCommands().size()); + ReceiveCommand pckCmd = pckCert.getCommands().get(0); + ReceiveCommand streamCmd = streamCert.getCommands().get(0); + assertEquals(pckCmd.getRefName(), streamCmd.getRefName()); + assertEquals(pckCmd.getOldId(), streamCmd.getOldId()); + assertEquals(pckCmd.getNewId().name(), streamCmd.getNewId().name()); + } + + @Test + public void testParseMultipleFromStream() throws Exception { + String input = concatPacketLines(INPUT, 0, 17); + assertFalse(input.contains(PushCertificateParser.END_CERT)); + input += input; + Reader reader = new InputStreamReader( + new ByteArrayInputStream(Constants.encode(input))); + + assertNotNull(PushCertificateParser.fromReader(reader)); + assertNotNull(PushCertificateParser.fromReader(reader)); + assertEquals(-1, reader.read()); + assertNull(PushCertificateParser.fromReader(reader)); + } + private static String concatPacketLines(String input, int begin, int end) throws IOException { StringBuilder result = new StringBuilder(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java index b34fb9fc6..93a1e54b0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java @@ -40,6 +40,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package org.eclipse.jgit.transport; import static org.eclipse.jgit.transport.BaseReceivePack.parseCommand; @@ -47,6 +48,7 @@ import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_PUSH_CE import java.io.EOFException; import java.io.IOException; +import java.io.Reader; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; @@ -57,6 +59,7 @@ import org.eclipse.jgit.errors.PackProtocolException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.transport.PushCertificate.NonceStatus; +import org.eclipse.jgit.util.IO; /** * Parser for signed push certificates. @@ -77,9 +80,97 @@ public class PushCertificateParser { static final String NONCE = "nonce"; //$NON-NLS-1$ + static final String END_CERT = "push-cert-end"; //$NON-NLS-1$ + private static final String VERSION_0_1 = "0.1"; //$NON-NLS-1$ - private static final String END_CERT = "push-cert-end"; //$NON-NLS-1$ + private static interface StringReader { + /** + * @return the next string from the input, up to an optional newline, with + * newline stripped if present + * + * @throws EOFException + * if EOF was reached. + * @throws IOException + * if an error occurred during reading. + */ + String read() throws EOFException, IOException; + } + + private static class PacketLineReader implements StringReader { + private final PacketLineIn pckIn; + + private PacketLineReader(PacketLineIn pckIn) { + this.pckIn = pckIn; + } + + @Override + public String read() throws IOException { + return pckIn.readString(); + } + } + + private static class StreamReader implements StringReader { + private final Reader reader; + + private StreamReader(Reader reader) { + this.reader = reader; + } + + @Override + public String read() throws IOException { + // Presize for a command containing 2 SHA-1s and some refname. + String line = IO.readLine(reader, 41 * 2 + 64); + if (line.isEmpty()) { + throw new EOFException(); + } else if (line.charAt(line.length() - 1) == '\n') { + line = line.substring(0, line.length() - 1); + } + return line; + } + } + + /** + * Parse a push certificate from a reader. + *
+ * Differences from the {@link PacketLineIn} receiver methods: + *