Analizar una respuesta inteligente de request de carga de git de http con una imprimación final temprana (?)

Estoy tratando de download / leer un repository simple de git sobre el protocolo de transferencia http inteligente, pero el file resultante contiene una secuencia final antes de que se haga reference a cualquier object. Comencé descargando una copy de un repository aleatorio en Github (Bitbucket produjo un file similar) usando la siguiente url: https://github.com/user/repo.git/info/refs?service=git-upload-pack . Esto dio como resultado un file como (Este file fue tomado y acortado del Repositorio de Github Git ):

 001e# service=git-upload-pack 000000fabe5a750939c212bc0781ffa04fabcfd2b2bd744e HEAD multi_ack thin-pack side-band side-band-64k ofs-delta shallow no-progress include-tag multi_ack_detailed no-done symref=HEAD:refs/heads/master agent=git/2:2.6.5~peff-attributes-nofollow-1622-gbbc42c6 003eac84098b7e32406a982ac01cc76a663d5605224b refs/heads/maint 003fbe5a750939c212bc0781ffa04fabcfd2b2bd744e refs/heads/master 003db27dc33dac678b815097aa6e3a4b5db354285f57 refs/heads/next 003b0962616cb70317a1ca3e4b03a22b51a0095e2326 refs/heads/pu 003d0b3e657f530ecba0206e6c07437d492592e43210 refs/heads/todo 003ff0d0fd3a5985d5e588da1e1d11c85fba0ae132f8 refs/pull/10/head 00403fed6331a38d9bb19f3ab72c91d651388026e98c refs/pull/10/merge ... 004549fa3dc76179e04b0833542fa52d0f287a4955ac refs/tags/v2.9.0-rc2^{} 003e47e8b7c56a5504d463ee624f6ffeeef1b6d007c1 refs/tags/v2.9.1 00415c9159de87e41cf14ec5f2132afb5a06f35c26b3 refs/tags/v2.9.1^{} 003ee6eeb1a62fdd0ac7b66951c45803e35f17b2b980 refs/tags/v2.9.2 0041e634160bf457f8b3a91125307681c9493f11afb2 refs/tags/v2.9.2^{} 003ef883596e997fe5bcbc5e89bee01b869721326109 refs/tags/v2.9.3 0041e0c1ceafc5bece92d35773a75fff59497e1d9bd5 refs/tags/v2.9.3^{} 0000 

Utilicé las siguientes fonts para get información con respecto al análisis:

Protocolo Doc

Git Book

Implementación de reference JGit

No encontré ninguna reference en las fonts anteriores que me lleve a creer que la secuencia "0000" debe documentarse, pero el cliente de git puede clonar de todos modos.

En una breve inspección del código fuente original de git, "pkt-line. (C | h)" no produjo ningún hallazgo nuevo. El siguiente progtwig (Java) ilustra el problema, porque imprimirá "verdadero" en la segunda instrucción println. Aparentemente, 0000 se analizó, se trató como el final y en la siguiente statement 00fa se evaluará e imprimirá la siguiente línea. Como resultado de mi observación, solo puedo suponer que me faltan algunos detalles, los clientes / serveres populares de GIT tienen un error de implementación o la documentation del protocolo no está clara. ¡Cualquier ayuda es apreciada!

PD: Soy consciente de que "0000" puede significar "flush", pero eso no está especificado en esta request de service.

 import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; /** * Read Git style pkt-line formatting from an input stream. * <p> * This class is not thread safe and may issue multiple reads to the underlying * stream for each method call made. * <p> * This class performs no buffering on its own. This makes it suitable to * interleave reads performed by this class with reads performed directly * against the underlying InputStream. */ public class PacketLineIn { /** * Magic return from {@link #readString()} when a flush packet is found. */ public static final String END = new StringBuilder(0).toString(); /* must not string pool */ private final InputStream in; private final byte[] lineBuffer; /** * Create a new packet line reader. * * @param i the input stream to consume. */ public PacketLineIn(final InputStream i) { in = i; lineBuffer = new byte[1000]; } /** * Read a single UTF-8 encoded string packet from the input stream. * <p> * If the string ends with an LF, it will be removed before returning the * value to the caller. If this automatic trimming behavior is not desinetworking, * use {@link #readStringRaw()} instead. * * @return the string. {@link #END} if the string was the magic flush * packet. * @throws IOException the stream cannot be read. */ public String readString() throws IOException { int len = readLength(); if (len == 0) { return END; } len -= 4; // length header (4 bytes) if (len == 0) { return ""; //$NON-NLS-1$ } byte[] raw; if (len <= lineBuffer.length) { raw = lineBuffer; } else { raw = new byte[len]; } readFully(in, raw, 0, len); if (raw[len - 1] == '\n') { len--; } return decodeNoFallback(StandardCharsets.UTF_8, raw, 0, len); } /** * Read a single UTF-8 encoded string packet from the input stream. * <p> * Unlike {@link #readString()} a trailing LF will be retained. * * @return the string. {@link #END} if the string was the magic flush * packet. * @throws IOException the stream cannot be read. */ public String readStringRaw() throws IOException { int len = readLength(); if (len == 0) { return END; } len -= 4; // length header (4 bytes) byte[] raw; if (len <= lineBuffer.length) { raw = lineBuffer; } else { raw = new byte[len]; } readFully(in, raw, 0, len); return decodeNoFallback(StandardCharsets.UTF_8, raw, 0, len); } int readLength() throws IOException { readFully(in, lineBuffer, 0, 4); try { final int len = parseInt16(lineBuffer, 0); if (len != 0 && len < 4) { throw new ArrayIndexOutOfBoundsException(); } return len; } catch (ArrayIndexOutOfBoundsException err) { throw new IOException("FUCK U JGIT"); } } private static void readFully(InputStream in, byte[] buffer, int off, int length) throws IOException { if (in.read(buffer, off, length) != length) { throw new IllegalArgumentException("Not enough spaaaaaaaace!"); } } private static int parseInt16(final byte[] args, int start) { final byte[] data = { args[start], args[start + 1], args[start + 2], args[start + 3] }; return Integer.parseInt(new String(data), 16); } public static String decodeNoFallback(final Charset cs, final byte[] buffer, final int start, final int end) throws CharacterCodingException { ByteBuffer b = ByteBuffer.wrap(buffer, start, end - start); b.mark(); // Try our built-in favorite. The assumption here is that // decoding will fail if the data is not actually encoded // using that encoder. try { return decode(b, StandardCharsets.UTF_8); } catch (CharacterCodingException e) { b.reset(); } if (!cs.equals(StandardCharsets.UTF_8)) { // Try the suggested encoding, it might be right since it was // provided by the caller. try { return decode(b, cs); } catch (CharacterCodingException e) { b.reset(); } } // Try the default character set. A small group of people // might actually use the same (or very similar) locale. Charset defcs = Charset.defaultCharset(); if (!defcs.equals(cs) && !defcs.equals(StandardCharsets.UTF_8)) { try { return decode(b, defcs); } catch (CharacterCodingException e) { b.reset(); } } throw new CharacterCodingException(); } private static String decode(final ByteBuffer b, final Charset charset) throws CharacterCodingException { final CharsetDecoder d = charset.newDecoder(); d.onMalformedInput(CodingErrorAction.REPORT); d.onUnmappableCharacter(CodingErrorAction.REPORT); return d.decode(b).toString(); } public static void main(String[] args) throws IOException { final Path input = Paths.get("refs"); final PacketLineIn line = new PacketLineIn(new FileInputStream(input.toFile())); System.out.println(line.readString()); System.out.println(line.readString() == PacketLineIn.END); System.out.println(line.readString()); } } 

Este es un flush-pkt y está definido en Documentación común a los protocolos Pack y Http .

Una línea pkt con un campo de longitud de 0 ("0000"), llamado flush-pkt, es un caso especial y DEBE manejarse de forma diferente que una línea pkt vacía ("0004").

Se usa en los protocolos de transferencia de Packfile . También se menciona, aunque no por su nombre, en los protocolos de transferencia HTTP .

 smart_reply = PKT-LINE("# service=$servicename" LF) ref_list "0000" 

y

 compute_request = want_list have_list request_end request_end = "0000" / "done"