Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java15
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java43
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackErrorHandler.java60
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java70
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java9
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommandErrorHandler.java83
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java372
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/UnpackErrorHandler.java29
10 files changed, 463 insertions, 223 deletions
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java
index e9462eeb4c..06970a7693 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java
@@ -96,6 +96,8 @@ public class GitFilter extends MetaFilter {
private ReceivePackFactory<HttpServletRequest> receivePackFactory = new DefaultReceivePackFactory();
+ private ReceivePackErrorHandler receivePackErrorHandler;
+
private final List<Filter> uploadPackFilters = new LinkedList<>();
private final List<Filter> receivePackFilters = new LinkedList<>();
@@ -190,6 +192,17 @@ public class GitFilter extends MetaFilter {
}
/**
+ * Set a custom error handler for git-receive-pack.
+ *
+ * @param h
+ * A custom error handler for git-receive-pack.
+ */
+ public void setReceivePackErrorHandler(ReceivePackErrorHandler h) {
+ assertNotInitialized();
+ this.receivePackErrorHandler = h;
+ }
+
+ /**
* Add receive-pack filter
*
* @param filter
@@ -233,7 +246,7 @@ public class GitFilter extends MetaFilter {
b = b.through(new ReceivePackServlet.Factory(receivePackFactory));
for (Filter f : receivePackFilters)
b = b.through(f);
- b.with(new ReceivePackServlet());
+ b.with(new ReceivePackServlet(receivePackErrorHandler));
}
ServletBinder refs = serve("*/" + Constants.INFO_REFS);
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java
index 5e09d012d7..5077e83b36 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java
@@ -48,8 +48,6 @@ import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
-import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND;
-import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
import static org.eclipse.jgit.transport.SideBandOutputStream.CH_ERROR;
import static org.eclipse.jgit.transport.SideBandOutputStream.SMALL_BUF;
@@ -64,14 +62,12 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.internal.transport.parser.FirstCommand;
-import org.eclipse.jgit.internal.transport.parser.FirstWant;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.transport.PacketLineIn;
import org.eclipse.jgit.transport.PacketLineOut;
import org.eclipse.jgit.transport.ReceivePack;
import org.eclipse.jgit.transport.RequestNotYetReadException;
import org.eclipse.jgit.transport.SideBandOutputStream;
-import org.eclipse.jgit.transport.UploadPack;
/**
* Utility functions for handling the Git-over-HTTP protocol.
@@ -220,44 +216,15 @@ public class GitSmartHttpTools {
private static void sendUploadPackError(HttpServletRequest req,
HttpServletResponse res, String textForGit) throws IOException {
+ // Do not use sideband. Sideband is acceptable only while packfile is
+ // being sent. Other places, like acknowledgement section, do not
+ // support sideband. Use an error packet.
ByteArrayOutputStream buf = new ByteArrayOutputStream(128);
PacketLineOut pckOut = new PacketLineOut(buf);
-
- boolean sideband;
- UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
- if (up != null) {
- try {
- sideband = up.isSideBand();
- } catch (RequestNotYetReadException e) {
- sideband = isUploadPackSideBand(req);
- }
- } else
- sideband = isUploadPackSideBand(req);
-
- if (sideband)
- writeSideBand(buf, textForGit);
- else
- writePacket(pckOut, textForGit);
+ writePacket(pckOut, textForGit);
send(req, res, UPLOAD_PACK_RESULT_TYPE, buf.toByteArray());
}
- private static boolean isUploadPackSideBand(HttpServletRequest req) {
- try {
- // The client may be in a state where they have sent the sideband
- // capability and are expecting a response in the sideband, but we might
- // not have an UploadPack, or it might not have read any of the request.
- // So, cheat and read the first line.
- String line = new PacketLineIn(req.getInputStream()).readString();
- FirstWant parsed = FirstWant.fromLine(line);
- return (parsed.getCapabilities().contains(OPTION_SIDE_BAND)
- || parsed.getCapabilities().contains(OPTION_SIDE_BAND_64K));
- } catch (IOException e) {
- // Probably the connection is closed and a subsequent write will fail, but
- // try it just in case.
- return false;
- }
- }
-
private static void sendReceivePackError(HttpServletRequest req,
HttpServletResponse res, String textForGit) throws IOException {
ByteArrayOutputStream buf = new ByteArrayOutputStream(128);
@@ -308,7 +275,7 @@ public class GitSmartHttpTools {
private static void writePacket(PacketLineOut pckOut, String textForGit)
throws IOException {
- pckOut.writeString("error: " + textForGit);
+ pckOut.writeString("ERR " + textForGit);
}
private static void send(HttpServletRequest req, HttpServletResponse res,
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackErrorHandler.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackErrorHandler.java
new file mode 100644
index 0000000000..ee66cb102f
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackErrorHandler.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.http.server;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jgit.transport.ReceivePack;
+import org.eclipse.jgit.transport.ServiceMayNotContinueException;
+
+/**
+ * Handle git-receive-pack errors.
+ *
+ * <p>
+ * This is an entry point for customizing an error handler for git-receive-pack.
+ * Right before calling {@link ReceivePack#receiveWithExceptionPropagation},
+ * JGit will call this handler if specified through {@link GitFilter}. The
+ * implementation of this handler is responsible for calling
+ * {@link ReceivePackRunnable} and handling exceptions for clients.
+ *
+ * <p>
+ * If a custom handler is not specified, JGit will use the default error
+ * handler.
+ *
+ * @since 5.6
+ */
+public interface ReceivePackErrorHandler {
+ /**
+ * @param req
+ * The HTTP request
+ * @param rsp
+ * The HTTP response
+ * @param r
+ * A continuation that handles a git-receive-pack request.
+ * @throws IOException
+ */
+ void receive(HttpServletRequest req, HttpServletResponse rsp,
+ ReceivePackRunnable r) throws IOException;
+
+ /** Process a git-receive-pack request. */
+ public interface ReceivePackRunnable {
+ /**
+ * See {@link ReceivePack#receiveWithExceptionPropagation}.
+ *
+ * @throws ServiceMayNotContinueException
+ * @throws IOException
+ */
+ void receive() throws ServiceMayNotContinueException, IOException;
+ }
+
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
index aed36560a8..eb130d0a28 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
@@ -71,6 +71,7 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.errors.UnpackException;
@@ -161,6 +162,13 @@ class ReceivePackServlet extends HttpServlet {
}
}
+ @Nullable
+ private final ReceivePackErrorHandler handler;
+
+ ReceivePackServlet(@Nullable ReceivePackErrorHandler handler) {
+ this.handler = handler;
+ }
+
/** {@inheritDoc} */
@Override
public void doPost(final HttpServletRequest req,
@@ -178,34 +186,42 @@ class ReceivePackServlet extends HttpServlet {
};
ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER);
- try {
- rp.setBiDirectionalPipe(false);
- rsp.setContentType(RECEIVE_PACK_RESULT_TYPE);
-
- rp.receive(getInputStream(req), out, null);
- out.close();
- } catch (CorruptObjectException e ) {
- // This should be already reported to the client.
- getServletContext().log(MessageFormat.format(
- HttpServerText.get().receivedCorruptObject,
- e.getMessage(),
- ServletUtils.identify(rp.getRepository())));
- consumeRequestBody(req);
- out.close();
-
- } catch (UnpackException | PackProtocolException e) {
- // This should be already reported to the client.
- log(rp.getRepository(), e.getCause());
- consumeRequestBody(req);
- out.close();
-
- } catch (Throwable e) {
- log(rp.getRepository(), e);
- if (!rsp.isCommitted()) {
- rsp.reset();
- sendError(req, rsp, SC_INTERNAL_SERVER_ERROR);
+ rp.setBiDirectionalPipe(false);
+ rsp.setContentType(RECEIVE_PACK_RESULT_TYPE);
+
+ if (handler != null) {
+ handler.receive(req, rsp, () -> {
+ rp.receiveWithExceptionPropagation(getInputStream(req), out,
+ null);
+ out.close();
+ });
+ } else {
+ try {
+ rp.receive(getInputStream(req), out, null);
+ out.close();
+ } catch (CorruptObjectException e ) {
+ // This should be already reported to the client.
+ getServletContext().log(MessageFormat.format(
+ HttpServerText.get().receivedCorruptObject,
+ e.getMessage(),
+ ServletUtils.identify(rp.getRepository())));
+ consumeRequestBody(req);
+ out.close();
+
+ } catch (UnpackException | PackProtocolException e) {
+ // This should be already reported to the client.
+ log(rp.getRepository(), e.getCause());
+ consumeRequestBody(req);
+ out.close();
+
+ } catch (Throwable e) {
+ log(rp.getRepository(), e);
+ if (!rsp.isCommitted()) {
+ rsp.reset();
+ sendError(req, rsp, SC_INTERNAL_SERVER_ERROR);
+ }
+ return;
}
- return;
}
}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
index 54561e0cfc..6baab5ddd7 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
@@ -70,7 +70,9 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+
import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.http.server.UploadPackErrorHandler.UploadPackRunnable;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.InternalHttpServerGlue;
@@ -212,7 +214,8 @@ class UploadPackServlet extends HttpServlet {
rsp.setContentType(UPLOAD_PACK_RESULT_TYPE);
try {
- up.upload(getInputStream(req), out, null);
+ up.uploadWithExceptionPropagation(getInputStream(req), out,
+ null);
out.close();
} catch (ServiceMayNotContinueException e) {
if (e.isOutput()) {
@@ -245,7 +248,9 @@ class UploadPackServlet extends HttpServlet {
log(up.getRepository(), e);
if (!rsp.isCommitted()) {
rsp.reset();
- sendError(req, rsp, SC_INTERNAL_SERVER_ERROR);
+ String msg = e instanceof PackProtocolException ? e.getMessage()
+ : null;
+ sendError(req, rsp, SC_INTERNAL_SERVER_ERROR, msg);
}
}
}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
index 99aa06b17c..b23fd28547 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
@@ -1214,7 +1214,7 @@ public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
Collections.<ObjectId> emptySet());
fail("Successfully served ref with value " + c.getRef(master));
} catch (TransportException err) {
- assertEquals("internal server error", err.getMessage());
+ assertEquals("Internal server error", err.getMessage());
}
} finally {
noRefServer.tearDown();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java
index 89ac2fe96d..daf7b9fb7a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java
@@ -495,8 +495,7 @@ public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCas
receive(rp, inBuf, outBuf);
fail("Expected UnpackException");
} catch (UnpackException failed) {
- Throwable err = failed.getCause();
- assertTrue(err instanceof IOException);
+ // Expected
}
final PacketLineIn r = asPacketLineIn(outBuf);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommandErrorHandler.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommandErrorHandler.java
new file mode 100644
index 0000000000..d9a148622b
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommandErrorHandler.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2019, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.transport;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.transport.ReceiveCommand.Result;
+
+/**
+ * Exception handler for processing {@link ReceiveCommand}.
+ *
+ * @since 5.7
+ */
+public interface ReceiveCommandErrorHandler {
+ /**
+ * Handle an exception thrown while validating the new commit ID.
+ *
+ * @param cmd
+ * offending command
+ * @param e
+ * exception thrown
+ */
+ default void handleNewIdValidationException(ReceiveCommand cmd,
+ IOException e) {
+ cmd.setResult(Result.REJECTED_MISSING_OBJECT, cmd.getNewId().name());
+ }
+
+ /**
+ * Handle an exception thrown while validating the old commit ID.
+ *
+ * @param cmd
+ * offending command
+ * @param e
+ * exception thrown
+ */
+ default void handleOldIdValidationException(ReceiveCommand cmd,
+ IOException e) {
+ cmd.setResult(Result.REJECTED_MISSING_OBJECT, cmd.getOldId().name());
+ }
+
+ /**
+ * Handle an exception thrown while checking if the update is fast-forward.
+ *
+ * @param cmd
+ * offending command
+ * @param e
+ * exception thrown
+ */
+ default void handleFastForwardCheckException(ReceiveCommand cmd,
+ IOException e) {
+ if (e instanceof MissingObjectException) {
+ cmd.setResult(Result.REJECTED_MISSING_OBJECT, e.getMessage());
+ } else {
+ cmd.setResult(Result.REJECTED_OTHER_REASON);
+ }
+ }
+
+ /**
+ * Handle an exception thrown while checking if the update is fast-forward.
+ *
+ * @param cmds
+ * commands being processed
+ * @param e
+ * exception thrown
+ */
+ default void handleBatchRefUpdateException(List<ReceiveCommand> cmds,
+ IOException e) {
+ for (ReceiveCommand cmd : cmds) {
+ if (cmd.getResult() == Result.NOT_ATTEMPTED) {
+ cmd.reject(e);
+ }
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
index 16fbbd42f3..6de3848d8e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
@@ -295,6 +295,12 @@ public class ReceivePack {
/** Hook to validate the update commands before execution. */
private PreReceiveHook preReceive;
+ private ReceiveCommandErrorHandler receiveCommandErrorHandler = new ReceiveCommandErrorHandler() {
+ // Use the default implementation.
+ };
+
+ private UnpackErrorHandler unpackErrorHandler = new DefaultUnpackErrorHandler();
+
/** Hook to report on the commands after execution. */
private PostReceiveHook postReceive;
@@ -1021,6 +1027,17 @@ public class ReceivePack {
}
/**
+ * Set an error handler for {@link ReceiveCommand}.
+ *
+ * @param receiveCommandErrorHandler
+ * @since 5.7
+ */
+ public void setReceiveCommandErrorHandler(
+ ReceiveCommandErrorHandler receiveCommandErrorHandler) {
+ this.receiveCommandErrorHandler = receiveCommandErrorHandler;
+ }
+
+ /**
* Send an error message to the client.
* <p>
* If any error messages are sent before the references are advertised to
@@ -1217,8 +1234,13 @@ public class ReceivePack {
*
* @throws java.io.IOException
* an error occurred during unpacking or connectivity checking.
+ * @throws LargeObjectException
+ * an large object needs to be opened for the check.
+ * @throws SubmoduleValidationException
+ * fails to validate the submodule.
*/
- protected void receivePackAndCheckConnectivity() throws IOException {
+ protected void receivePackAndCheckConnectivity() throws IOException,
+ LargeObjectException, SubmoduleValidationException {
receivePack();
if (needCheckConnectivity()) {
checkSubmodules();
@@ -1368,15 +1390,9 @@ public class ReceivePack {
if (hasCommands()) {
readPostCommands(pck);
}
- } catch (PackProtocolException e) {
+ } catch (Throwable t) {
discardCommands();
- fatalError(e.getMessage());
- throw e;
- } catch (InputOverLimitIOException e) {
- String msg = JGitText.get().tooManyCommands;
- discardCommands();
- fatalError(msg);
- throw new PackProtocolException(msg);
+ throw t;
}
}
@@ -1535,7 +1551,8 @@ public class ReceivePack {
|| !getClientShallowCommits().isEmpty();
}
- private void checkSubmodules() throws IOException {
+ private void checkSubmodules() throws IOException, LargeObjectException,
+ SubmoduleValidationException {
ObjectDatabase odb = db.getObjectDatabase();
if (objectChecker == null) {
return;
@@ -1544,12 +1561,8 @@ public class ReceivePack {
AnyObjectId blobId = entry.getBlobId();
ObjectLoader blob = odb.open(blobId, Constants.OBJ_BLOB);
- try {
- SubmoduleValidator.assertValidGitModulesFile(
- new String(blob.getBytes(), UTF_8));
- } catch (LargeObjectException | SubmoduleValidationException e) {
- throw new IOException(e);
- }
+ SubmoduleValidator.assertValidGitModulesFile(
+ new String(blob.getBytes(), UTF_8));
}
}
@@ -1730,16 +1743,16 @@ public class ReceivePack {
try {
oldObj = walk.parseAny(cmd.getOldId());
} catch (IOException e) {
- cmd.setResult(Result.REJECTED_MISSING_OBJECT,
- cmd.getOldId().name());
+ receiveCommandErrorHandler
+ .handleOldIdValidationException(cmd, e);
continue;
}
try {
newObj = walk.parseAny(cmd.getNewId());
} catch (IOException e) {
- cmd.setResult(Result.REJECTED_MISSING_OBJECT,
- cmd.getNewId().name());
+ receiveCommandErrorHandler
+ .handleNewIdValidationException(cmd, e);
continue;
}
@@ -1747,16 +1760,14 @@ public class ReceivePack {
&& newObj instanceof RevCommit) {
try {
if (walk.isMergedInto((RevCommit) oldObj,
- (RevCommit) newObj))
+ (RevCommit) newObj)) {
cmd.setTypeFastForwardUpdate();
- else
- cmd.setType(
- ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
- } catch (MissingObjectException e) {
- cmd.setResult(Result.REJECTED_MISSING_OBJECT,
- e.getMessage());
+ } else {
+ cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
+ }
} catch (IOException e) {
- cmd.setResult(Result.REJECTED_OTHER_REASON);
+ receiveCommandErrorHandler
+ .handleFastForwardCheckException(cmd, e);
}
} else {
cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
@@ -1835,109 +1846,122 @@ public class ReceivePack {
try {
batch.setPushCertificate(getPushCertificate());
batch.execute(walk, updating);
- } catch (IOException err) {
- for (ReceiveCommand cmd : toApply) {
- if (cmd.getResult() == Result.NOT_ATTEMPTED)
- cmd.reject(err);
- }
+ } catch (IOException e) {
+ receiveCommandErrorHandler.handleBatchRefUpdateException(toApply,
+ e);
}
}
/**
* Send a status report.
*
- * @param forClient
- * true if this report is for a Git client, false if it is for an
- * end-user.
* @param unpackError
* an error that occurred during unpacking, or {@code null}
- * @param out
- * the reporter for sending the status strings.
* @throws java.io.IOException
* an error occurred writing the status report.
* @since 5.6
*/
- private void sendStatusReport(final boolean forClient,
- final Throwable unpackError, final Reporter out)
- throws IOException {
- if (unpackError != null) {
- out.sendString("unpack error " + unpackError.getMessage()); //$NON-NLS-1$
- if (forClient) {
- for (ReceiveCommand cmd : commands) {
- out.sendString("ng " + cmd.getRefName() //$NON-NLS-1$
- + " n/a (unpacker error)"); //$NON-NLS-1$
+ private void sendStatusReport(Throwable unpackError) throws IOException {
+ Reporter out = new Reporter() {
+ @Override
+ void sendString(String s) throws IOException {
+ if (reportStatus) {
+ pckOut.writeString(s + "\n"); //$NON-NLS-1$
+ } else if (msgOut != null) {
+ msgOut.write(Constants.encode(s + "\n")); //$NON-NLS-1$
}
}
- return;
- }
+ };
- if (forClient)
- out.sendString("unpack ok"); //$NON-NLS-1$
- for (ReceiveCommand cmd : commands) {
- if (cmd.getResult() == Result.OK) {
- if (forClient)
- out.sendString("ok " + cmd.getRefName()); //$NON-NLS-1$
- continue;
+ try {
+ if (unpackError != null) {
+ out.sendString("unpack error " + unpackError.getMessage()); //$NON-NLS-1$
+ if (reportStatus) {
+ for (ReceiveCommand cmd : commands) {
+ out.sendString("ng " + cmd.getRefName() //$NON-NLS-1$
+ + " n/a (unpacker error)"); //$NON-NLS-1$
+ }
+ }
+ return;
}
- final StringBuilder r = new StringBuilder();
- if (forClient)
- r.append("ng ").append(cmd.getRefName()).append(" "); //$NON-NLS-1$ //$NON-NLS-2$
- else
- r.append(" ! [rejected] ").append(cmd.getRefName()) //$NON-NLS-1$
- .append(" ("); //$NON-NLS-1$
-
- switch (cmd.getResult()) {
- case NOT_ATTEMPTED:
- r.append("server bug; ref not processed"); //$NON-NLS-1$
- break;
-
- case REJECTED_NOCREATE:
- r.append("creation prohibited"); //$NON-NLS-1$
- break;
-
- case REJECTED_NODELETE:
- r.append("deletion prohibited"); //$NON-NLS-1$
- break;
-
- case REJECTED_NONFASTFORWARD:
- r.append("non-fast forward"); //$NON-NLS-1$
- break;
-
- case REJECTED_CURRENT_BRANCH:
- r.append("branch is currently checked out"); //$NON-NLS-1$
- break;
-
- case REJECTED_MISSING_OBJECT:
- if (cmd.getMessage() == null)
- r.append("missing object(s)"); //$NON-NLS-1$
- else if (cmd.getMessage()
- .length() == Constants.OBJECT_ID_STRING_LENGTH) {
- r.append("object "); //$NON-NLS-1$
- r.append(cmd.getMessage());
- r.append(" missing"); //$NON-NLS-1$
- } else
- r.append(cmd.getMessage());
- break;
-
- case REJECTED_OTHER_REASON:
- if (cmd.getMessage() == null)
- r.append("unspecified reason"); //$NON-NLS-1$
- else
- r.append(cmd.getMessage());
- break;
-
- case LOCK_FAILURE:
- r.append("failed to lock"); //$NON-NLS-1$
- break;
-
- case OK:
- // We shouldn't have reached this case (see 'ok' case above).
- continue;
+ if (reportStatus) {
+ out.sendString("unpack ok"); //$NON-NLS-1$
+ }
+ for (ReceiveCommand cmd : commands) {
+ if (cmd.getResult() == Result.OK) {
+ if (reportStatus) {
+ out.sendString("ok " + cmd.getRefName()); //$NON-NLS-1$
+ }
+ continue;
+ }
+
+ final StringBuilder r = new StringBuilder();
+ if (reportStatus) {
+ r.append("ng ").append(cmd.getRefName()).append(" "); //$NON-NLS-1$ //$NON-NLS-2$
+ } else {
+ r.append(" ! [rejected] ").append(cmd.getRefName()) //$NON-NLS-1$
+ .append(" ("); //$NON-NLS-1$
+ }
+
+ switch (cmd.getResult()) {
+ case NOT_ATTEMPTED:
+ r.append("server bug; ref not processed"); //$NON-NLS-1$
+ break;
+
+ case REJECTED_NOCREATE:
+ r.append("creation prohibited"); //$NON-NLS-1$
+ break;
+
+ case REJECTED_NODELETE:
+ r.append("deletion prohibited"); //$NON-NLS-1$
+ break;
+
+ case REJECTED_NONFASTFORWARD:
+ r.append("non-fast forward"); //$NON-NLS-1$
+ break;
+
+ case REJECTED_CURRENT_BRANCH:
+ r.append("branch is currently checked out"); //$NON-NLS-1$
+ break;
+
+ case REJECTED_MISSING_OBJECT:
+ if (cmd.getMessage() == null)
+ r.append("missing object(s)"); //$NON-NLS-1$
+ else if (cmd.getMessage()
+ .length() == Constants.OBJECT_ID_STRING_LENGTH) {
+ r.append("object "); //$NON-NLS-1$
+ r.append(cmd.getMessage());
+ r.append(" missing"); //$NON-NLS-1$
+ } else
+ r.append(cmd.getMessage());
+ break;
+
+ case REJECTED_OTHER_REASON:
+ if (cmd.getMessage() == null)
+ r.append("unspecified reason"); //$NON-NLS-1$
+ else
+ r.append(cmd.getMessage());
+ break;
+
+ case LOCK_FAILURE:
+ r.append("failed to lock"); //$NON-NLS-1$
+ break;
+
+ case OK:
+ // We shouldn't have reached this case (see 'ok' case
+ // above).
+ continue;
+ }
+ if (!reportStatus) {
+ r.append(")"); //$NON-NLS-1$
+ }
+ out.sendString(r.toString());
+ }
+ } finally {
+ if (reportStatus) {
+ pckOut.end();
}
- if (!forClient)
- r.append(")"); //$NON-NLS-1$
- out.sendString(r.toString());
}
}
@@ -2123,6 +2147,15 @@ public class ReceivePack {
}
/**
+ * @param unpackErrorHandler
+ * the unpackErrorHandler to set
+ * @since 5.7
+ */
+ public void setUnpackErrorHandler(UnpackErrorHandler unpackErrorHandler) {
+ this.unpackErrorHandler = unpackErrorHandler;
+ }
+
+ /**
* Set whether this class will report command failures as warning messages
* before sending the command results.
*
@@ -2161,6 +2194,49 @@ public class ReceivePack {
init(input, output, messages);
try {
service();
+ } catch (PackProtocolException e) {
+ fatalError(e.getMessage());
+ throw e;
+ } catch (InputOverLimitIOException e) {
+ String msg = JGitText.get().tooManyCommands;
+ fatalError(msg);
+ throw new PackProtocolException(msg);
+ } finally {
+ try {
+ close();
+ } finally {
+ release();
+ }
+ }
+ }
+
+ /**
+ * Execute the receive task on the socket.
+ *
+ * <p>
+ * Same as {@link #receive}, but the exceptions are not reported to the
+ * client yet.
+ *
+ * @param input
+ * raw input to read client commands and pack data from. Caller
+ * must ensure the input is buffered, otherwise read performance
+ * may suffer.
+ * @param output
+ * response back to the Git network client. Caller must ensure
+ * the output is buffered, otherwise write performance may
+ * suffer.
+ * @param messages
+ * secondary "notice" channel to send additional messages out
+ * through. When run over SSH this should be tied back to the
+ * standard error channel of the command execution. For most
+ * other network connections this should be null.
+ * @throws java.io.IOException
+ */
+ public void receiveWithExceptionPropagation(InputStream input,
+ OutputStream output, OutputStream messages) throws IOException {
+ init(input, output, messages);
+ try {
+ service();
} finally {
try {
close();
@@ -2178,19 +2254,23 @@ public class ReceivePack {
getAdvertisedOrDefaultRefs();
if (hasError())
return;
+
recvCommands();
+
if (hasCommands()) {
- Throwable unpackError = null;
- if (needPack()) {
- try {
- receivePackAndCheckConnectivity();
- } catch (IOException | RuntimeException | Error err) {
- unpackError = err;
+ try (PostReceiveExecutor e = new PostReceiveExecutor()) {
+ if (needPack()) {
+ try {
+ receivePackAndCheckConnectivity();
+ } catch (IOException | RuntimeException
+ | SubmoduleValidationException | Error err) {
+ unlockPack();
+ unpackErrorHandler.handleUnpackException(err);
+ throw new UnpackException(err);
+ }
}
- }
- try {
- if (unpackError == null) {
+ try {
setAtomic(isCapabilityEnabled(CAPABILITY_ATOMIC));
validateCommands();
@@ -2204,39 +2284,12 @@ public class ReceivePack {
failPendingCommands();
}
executeCommands();
+ } finally {
+ unlockPack();
}
- } finally {
- unlockPack();
- }
-
- if (reportStatus) {
- sendStatusReport(true, unpackError, new Reporter() {
- @Override
- void sendString(String s) throws IOException {
- pckOut.writeString(s + "\n"); //$NON-NLS-1$
- }
- });
- pckOut.end();
- } else if (msgOut != null) {
- sendStatusReport(false, unpackError, new Reporter() {
- @Override
- void sendString(String s) throws IOException {
- msgOut.write(Constants.encode(s + "\n")); //$NON-NLS-1$
- }
- });
- }
- if (unpackError != null) {
- // we already know which exception to throw. Ignore
- // potential additional exceptions raised in postReceiveHooks
- try {
- postReceive.onPostReceive(this, filterCommands(Result.OK));
- } catch (Throwable e) {
- // empty
- }
- throw new UnpackException(unpackError);
+ sendStatusReport(null);
}
- postReceive.onPostReceive(this, filterCommands(Result.OK));
autoGc();
}
}
@@ -2273,4 +2326,19 @@ public class ReceivePack {
}
return new ReceiveCommand(oldId, newId, name);
}
+
+ private class PostReceiveExecutor implements AutoCloseable {
+ @Override
+ public void close() {
+ postReceive.onPostReceive(ReceivePack.this,
+ filterCommands(Result.OK));
+ }
+ }
+
+ private class DefaultUnpackErrorHandler implements UnpackErrorHandler {
+ @Override
+ public void handleUnpackException(Throwable t) throws IOException {
+ sendStatusReport(t);
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UnpackErrorHandler.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UnpackErrorHandler.java
new file mode 100644
index 0000000000..12c9a76214
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UnpackErrorHandler.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.transport;
+
+import java.io.IOException;
+
+/**
+ * Exception handler for processing an incoming pack file.
+ *
+ * @since 5.7
+ */
+public interface UnpackErrorHandler {
+ /**
+ * Handle an exception thrown while unpacking the pack file.
+ *
+ * @param t
+ * exception thrown
+ * @throws IOException
+ * thrown when failed to write an error back to the client.
+ */
+ void handleUnpackException(Throwable t) throws IOException;
+}

Back to the top