Browse Source

dump HTTP: Avoid being confused by Content-Length of a gzipped stream

TransportHttp sets 'Accept-Encoding: gzip' to allow the server to
compress HTTP responses. When fetching a loose object over HTTP, it
uses the following code to read the response:

       InputStream in = openInputStream(c);
       int len = c.getContentLength();
       return new FileStream(in, len);

If the content is gzipped, openInputStream decompresses it and produces
the correct content for the object. Unfortunately the Content-Length
header contains the length of the compressed stream instead of the
actual content length. Use a length of -1 instead since we don't know
the actual length.

Loose objects are already compressed, so the gzip encoding typically
produces a longer compressed payload. The value from the Content-Length
is too high, producing EOFException: Short read of block.

Change-Id: I8d5284dad608e3abd8217823da2b365e8cd998b0
Signed-off-by: Zhen Chen <czhen@google.com>
Helped-by: Jonathan Nieder <jrn@google.com>
tags/v4.6.0.201612231935-r
Zhen Chen 7 years ago
parent
commit
8803718493

+ 2
- 1
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java View File

package org.eclipse.jgit.http.server; package org.eclipse.jgit.http.server;


import static org.eclipse.jgit.util.HttpSupport.ENCODING_GZIP; import static org.eclipse.jgit.util.HttpSupport.ENCODING_GZIP;
import static org.eclipse.jgit.util.HttpSupport.ENCODING_X_GZIP;
import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT_ENCODING; import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT_ENCODING;
import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING; import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING;
import static org.eclipse.jgit.util.HttpSupport.HDR_ETAG; import static org.eclipse.jgit.util.HttpSupport.HDR_ETAG;
throws IOException { throws IOException {
InputStream in = req.getInputStream(); InputStream in = req.getInputStream();
final String enc = req.getHeader(HDR_CONTENT_ENCODING); final String enc = req.getHeader(HDR_CONTENT_ENCODING);
if (ENCODING_GZIP.equals(enc) || "x-gzip".equals(enc)) //$NON-NLS-1$
if (ENCODING_GZIP.equals(enc) || ENCODING_X_GZIP.equals(enc)) //$NON-NLS-1$
in = new GZIPInputStream(in); in = new GZIPInputStream(in);
else if (enc != null) else if (enc != null)
throw new IOException(MessageFormat.format(HttpServerText.get().encodingNotSupportedByThisLibrary throw new IOException(MessageFormat.format(HttpServerText.get().encodingNotSupportedByThisLibrary

+ 15
- 3
org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java View File

package org.eclipse.jgit.transport; package org.eclipse.jgit.transport;


import static org.eclipse.jgit.util.HttpSupport.ENCODING_GZIP; import static org.eclipse.jgit.util.HttpSupport.ENCODING_GZIP;
import static org.eclipse.jgit.util.HttpSupport.ENCODING_X_GZIP;
import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT; import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT;
import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT_ENCODING; import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT_ENCODING;
import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING; import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING;
final InputStream openInputStream(HttpConnection conn) final InputStream openInputStream(HttpConnection conn)
throws IOException { throws IOException {
InputStream input = conn.getInputStream(); InputStream input = conn.getInputStream();
if (ENCODING_GZIP.equals(conn.getHeaderField(HDR_CONTENT_ENCODING)))
if (isGzipContent(conn))
input = new GZIPInputStream(input); input = new GZIPInputStream(input);
return input; return input;
} }
return expType.equals(actType); return expType.equals(actType);
} }


private boolean isGzipContent(final HttpConnection c) {
return ENCODING_GZIP.equals(c.getHeaderField(HDR_CONTENT_ENCODING))
|| ENCODING_X_GZIP.equals(c.getHeaderField(HDR_CONTENT_ENCODING));
}

private void readSmartHeaders(final InputStream in, final String service) private void readSmartHeaders(final InputStream in, final String service)
throws IOException { throws IOException {
// A smart reply will have a '#' after the first 4 bytes, but // A smart reply will have a '#' after the first 4 bytes, but
switch (HttpSupport.response(c)) { switch (HttpSupport.response(c)) {
case HttpConnection.HTTP_OK: case HttpConnection.HTTP_OK:
final InputStream in = openInputStream(c); final InputStream in = openInputStream(c);
final int len = c.getContentLength();
return new FileStream(in, len);
// If content is being gzipped and then transferred, the content
// length in the header is the zipped content length, not the
// actual content length.
if (!isGzipContent(c)) {
final int len = c.getContentLength();
return new FileStream(in, len);
}
return new FileStream(in);
case HttpConnection.HTTP_NOT_FOUND: case HttpConnection.HTTP_NOT_FOUND:
throw new FileNotFoundException(u.toString()); throw new FileNotFoundException(u.toString());
default: default:

+ 3
- 0
org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java View File

/** The {@code gzip} encoding value for {@link #HDR_ACCEPT_ENCODING}. */ /** The {@code gzip} encoding value for {@link #HDR_ACCEPT_ENCODING}. */
public static final String ENCODING_GZIP = "gzip"; //$NON-NLS-1$ public static final String ENCODING_GZIP = "gzip"; //$NON-NLS-1$


/** The {@code x-gzip} encoding value for {@link #HDR_ACCEPT_ENCODING}. */
public static final String ENCODING_X_GZIP = "x-gzip"; //$NON-NLS-1$

/** The standard {@code text/plain} MIME type. */ /** The standard {@code text/plain} MIME type. */
public static final String TEXT_PLAIN = "text/plain"; //$NON-NLS-1$ public static final String TEXT_PLAIN = "text/plain"; //$NON-NLS-1$



Loading…
Cancel
Save