summaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit.http.server
diff options
context:
space:
mode:
authorDave Borowitz <dborowitz@google.com>2012-03-07 11:56:56 -0800
committerDave Borowitz <dborowitz@google.com>2012-03-07 15:04:45 -0800
commit039c785d9f0eac3fcb78b9dc2bf61796b82d3401 (patch)
tree144caccb852cde0253564bdd2a87a6c46fc24614 /org.eclipse.jgit.http.server
parentd2787d481efcd5f883d52eed5029be380196f0ef (diff)
downloadjgit-039c785d9f0eac3fcb78b9dc2bf61796b82d3401.tar.gz
jgit-039c785d9f0eac3fcb78b9dc2bf61796b82d3401.zip
Try to send HTTP error messages over sideband
When a client POSTs to /git-{upload,receive}-pack, the first line includes their client capabilities. As soon as the C git client sends side-band(-64k), it goes into a state where it chokes on data not sent in a valid sideband channel. GitSmartHttpTools.sendError() is called early in the request, likely before a {Upload,Receive}Pack handler is assigned or, even so, before it has read the request. In some cases we must read the first line manually within sendError() to tell whether sideband is needed. Change-Id: I8277fd45a4ec3b71fa8f87404b4f5d1a09e0f384
Diffstat (limited to 'org.eclipse.jgit.http.server')
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java133
1 files changed, 121 insertions, 12 deletions
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 8bd1704bc4..e6b287ff06 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
@@ -43,6 +43,12 @@
package org.eclipse.jgit.http.server;
+import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER;
+import static org.eclipse.jgit.transport.BasePackFetchConnection.OPTION_SIDE_BAND;
+import static org.eclipse.jgit.transport.BasePackFetchConnection.OPTION_SIDE_BAND_64K;
+import static org.eclipse.jgit.transport.BasePackPushConnection.CAPABILITY_SIDE_BAND_64K;
+import static org.eclipse.jgit.transport.SideBandOutputStream.CH_ERROR;
+import static org.eclipse.jgit.transport.SideBandOutputStream.SMALL_BUF;
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
@@ -58,7 +64,12 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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.
@@ -141,6 +152,11 @@ public class GitSmartHttpTools {
* to a Git protocol client using an HTTP 200 OK response with the error
* embedded in the payload. If the request was not issued by a Git client,
* an HTTP response code is returned instead.
+ * <p>
+ * This method may only be called before handing off the request to
+ * {@link UploadPack#upload(java.io.InputStream, OutputStream, OutputStream)}
+ * or
+ * {@link ReceivePack#receive(java.io.InputStream, OutputStream, OutputStream)}.
*
* @param req
* current request.
@@ -176,21 +192,12 @@ public class GitSmartHttpTools {
}
}
- ByteArrayOutputStream buf = new ByteArrayOutputStream(128);
- PacketLineOut pck = new PacketLineOut(buf);
-
if (isInfoRefs(req)) {
- String svc = req.getParameter("service");
- pck.writeString("# service=" + svc + "\n");
- pck.end();
- pck.writeString("ERR " + textForGit);
- send(req, res, infoRefsResultType(svc), buf.toByteArray());
+ sendInfoRefsError(req, res, textForGit);
} else if (isUploadPack(req)) {
- pck.writeString("ERR " + textForGit);
- send(req, res, UPLOAD_PACK_RESULT_TYPE, buf.toByteArray());
+ sendUploadPackError(req, res, textForGit);
} else if (isReceivePack(req)) {
- pck.writeString("ERR " + textForGit);
- send(req, res, RECEIVE_PACK_RESULT_TYPE, buf.toByteArray());
+ sendReceivePackError(req, res, textForGit);
} else {
if (httpStatus < 400)
ServletUtils.consumeRequestBody(req);
@@ -198,6 +205,108 @@ public class GitSmartHttpTools {
}
}
+ private static void sendInfoRefsError(HttpServletRequest req,
+ HttpServletResponse res, String textForGit) throws IOException {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream(128);
+ PacketLineOut pck = new PacketLineOut(buf);
+ String svc = req.getParameter("service");
+ pck.writeString("# service=" + svc + "\n");
+ pck.end();
+ pck.writeString("ERR " + textForGit);
+ send(req, res, infoRefsResultType(svc), buf.toByteArray());
+ }
+
+ private static void sendUploadPackError(HttpServletRequest req,
+ HttpServletResponse res, String textForGit) throws IOException {
+ 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);
+ 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()).readStringRaw();
+ UploadPack.FirstLine parsed = new UploadPack.FirstLine(line);
+ return (parsed.getOptions().contains(OPTION_SIDE_BAND)
+ || parsed.getOptions().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);
+ PacketLineOut pckOut = new PacketLineOut(buf);
+
+ boolean sideband;
+ ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER);
+ if (rp != null) {
+ try {
+ sideband = rp.isSideBand();
+ } catch (RequestNotYetReadException e) {
+ sideband = isReceivePackSideBand(req);
+ }
+ } else
+ sideband = isReceivePackSideBand(req);
+
+ if (sideband)
+ writeSideBand(buf, textForGit);
+ else
+ writePacket(pckOut, textForGit);
+ send(req, res, RECEIVE_PACK_RESULT_TYPE, buf.toByteArray());
+ }
+
+ private static boolean isReceivePackSideBand(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 a ReceivePack, or it might not have read any of the request.
+ // So, cheat and read the first line.
+ String line = new PacketLineIn(req.getInputStream()).readStringRaw();
+ ReceivePack.FirstLine parsed = new ReceivePack.FirstLine(line);
+ return parsed.getCapabilities().contains(CAPABILITY_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 writeSideBand(OutputStream out, String textForGit)
+ throws IOException {
+ OutputStream msg = new SideBandOutputStream(CH_ERROR, SMALL_BUF, out);
+ msg.write(Constants.encode("error: " + textForGit));
+ msg.flush();
+ }
+
+ private static void writePacket(PacketLineOut pckOut, String textForGit)
+ throws IOException {
+ pckOut.writeString("error: " + textForGit);
+ }
+
private static void send(HttpServletRequest req, HttpServletResponse res,
String type, byte[] buf) throws IOException {
ServletUtils.consumeRequestBody(req);