Browse Source

http: Allow specifying a custom error handler for UploadPack

By abstracting the error handler, this lets a user customize the error
handler for UploadPack. A customized error handler can show a custom
error message to the clients based on the exception thrown from the
hook, create a monitoring system for server errors, or do custom
logging.

Change-Id: Idd3b87d6bd471fef807c0cf1183e904b2886157e
Signed-off-by: Masaya Suzuki <masayasuzuki@google.com>
next
Masaya Suzuki 5 years ago
parent
commit
abedaf0d38
  1. 1
      org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
  2. 15
      org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java
  3. 59
      org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackErrorHandler.java
  4. 71
      org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java

1
org.eclipse.jgit.http.server/META-INF/MANIFEST.MF

@ -18,6 +18,7 @@ Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: javax.servlet;version="[2.5.0,3.2.0)", Import-Package: javax.servlet;version="[2.5.0,3.2.0)",
javax.servlet.http;version="[2.5.0,3.2.0)", javax.servlet.http;version="[2.5.0,3.2.0)",
org.eclipse.jgit.annotations;version="[5.5.0,5.7.0)",
org.eclipse.jgit.errors;version="[5.6.0,5.7.0)", org.eclipse.jgit.errors;version="[5.6.0,5.7.0)",
org.eclipse.jgit.internal.storage.dfs;version="[5.6.0,5.7.0)", org.eclipse.jgit.internal.storage.dfs;version="[5.6.0,5.7.0)",
org.eclipse.jgit.internal.storage.file;version="[5.6.0,5.7.0)", org.eclipse.jgit.internal.storage.file;version="[5.6.0,5.7.0)",

15
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java

@ -92,6 +92,8 @@ public class GitFilter extends MetaFilter {
private UploadPackFactory<HttpServletRequest> uploadPackFactory = new DefaultUploadPackFactory(); private UploadPackFactory<HttpServletRequest> uploadPackFactory = new DefaultUploadPackFactory();
private UploadPackErrorHandler uploadPackErrorHandler;
private ReceivePackFactory<HttpServletRequest> receivePackFactory = new DefaultReceivePackFactory(); private ReceivePackFactory<HttpServletRequest> receivePackFactory = new DefaultReceivePackFactory();
private final List<Filter> uploadPackFilters = new LinkedList<>(); private final List<Filter> uploadPackFilters = new LinkedList<>();
@ -149,6 +151,17 @@ public class GitFilter extends MetaFilter {
this.uploadPackFactory = f != null ? f : (UploadPackFactory<HttpServletRequest>)UploadPackFactory.DISABLED; this.uploadPackFactory = f != null ? f : (UploadPackFactory<HttpServletRequest>)UploadPackFactory.DISABLED;
} }
/**
* Set a custom error handler for git-upload-pack.
*
* @param h
* A custom error handler for git-upload-pack.
*/
public void setUploadPackErrorHandler(UploadPackErrorHandler h) {
assertNotInitialized();
this.uploadPackErrorHandler = h;
}
/** /**
* Add upload-pack filter * Add upload-pack filter
* *
@ -212,7 +225,7 @@ public class GitFilter extends MetaFilter {
b = b.through(new UploadPackServlet.Factory(uploadPackFactory)); b = b.through(new UploadPackServlet.Factory(uploadPackFactory));
for (Filter f : uploadPackFilters) for (Filter f : uploadPackFilters)
b = b.through(f); b = b.through(f);
b.with(new UploadPackServlet()); b.with(new UploadPackServlet(uploadPackErrorHandler));
} }
if (receivePackFactory != ReceivePackFactory.DISABLED) { if (receivePackFactory != ReceivePackFactory.DISABLED) {

59
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackErrorHandler.java

@ -0,0 +1,59 @@
/*
* 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.ServiceMayNotContinueException;
import org.eclipse.jgit.transport.UploadPack;
/**
* Handle git-upload-pack errors.
*
* <p>
* This is an entry point for customizing an error handler for git-upload-pack.
* Right before calling {@link UploadPack#uploadWithExceptionPropagation}, JGit
* will call this handler if specified through {@link GitFilter}. The
* implementation of this handler is responsible for calling
* {@link UploadPackRunnable} 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 UploadPackErrorHandler {
/**
* @param req
* The HTTP request
* @param rsp
* The HTTP response
* @param r
* A continuation that handles a git-upload-pack request.
* @throws IOException
*/
void upload(HttpServletRequest req, HttpServletResponse rsp,
UploadPackRunnable r) throws IOException;
/** Process a git-upload-pack request. */
public interface UploadPackRunnable {
/**
* See {@link UploadPack#uploadWithExceptionPropagation}.
*
* @throws ServiceMayNotContinueException
* @throws IOException
*/
void upload() throws ServiceMayNotContinueException, IOException;
}
}

71
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java

@ -70,7 +70,8 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.http.server.UploadPackErrorHandler.UploadPackRunnable;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.InternalHttpServerGlue; import org.eclipse.jgit.transport.InternalHttpServerGlue;
import org.eclipse.jgit.transport.PacketLineOut; import org.eclipse.jgit.transport.PacketLineOut;
@ -181,53 +182,71 @@ class UploadPackServlet extends HttpServlet {
} }
} }
private final UploadPackErrorHandler handler;
UploadPackServlet(@Nullable UploadPackErrorHandler handler) {
this.handler = handler != null ? handler
: this::defaultUploadPackHandler;
}
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public void doPost(final HttpServletRequest req, public void doPost(HttpServletRequest req, HttpServletResponse rsp)
final HttpServletResponse rsp) throws IOException { throws IOException {
if (!UPLOAD_PACK_REQUEST_TYPE.equals(req.getContentType())) { if (!UPLOAD_PACK_REQUEST_TYPE.equals(req.getContentType())) {
rsp.sendError(SC_UNSUPPORTED_MEDIA_TYPE); rsp.sendError(SC_UNSUPPORTED_MEDIA_TYPE);
return; return;
} }
SmartOutputStream out = new SmartOutputStream(req, rsp, false) { UploadPackRunnable r = () -> {
@Override UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
public void flush() throws IOException { @SuppressWarnings("resource")
doFlush(); SmartOutputStream out = new SmartOutputStream(req, rsp, false) {
} @Override
}; public void flush() throws IOException {
doFlush();
}
};
UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
try {
up.setBiDirectionalPipe(false); up.setBiDirectionalPipe(false);
rsp.setContentType(UPLOAD_PACK_RESULT_TYPE); rsp.setContentType(UPLOAD_PACK_RESULT_TYPE);
up.upload(getInputStream(req), out, null); try {
out.close(); up.upload(getInputStream(req), out, null);
out.close();
} catch (ServiceMayNotContinueException e) { } catch (ServiceMayNotContinueException e) {
if (e.isOutput()) { if (e.isOutput()) {
consumeRequestBody(req);
out.close();
}
throw e;
} catch (UploadPackInternalServerErrorException e) {
// Special case exception, error message was sent to client.
log(up.getRepository(), e.getCause());
consumeRequestBody(req); consumeRequestBody(req);
out.close(); out.close();
} else if (!rsp.isCommitted()) {
rsp.reset();
sendError(req, rsp, e.getStatusCode(), e.getMessage());
} }
return; };
} catch (UploadPackInternalServerErrorException e) { handler.upload(req, rsp, r);
// Special case exception, error message was sent to client. }
log(up.getRepository(), e.getCause());
consumeRequestBody(req);
out.close();
private void defaultUploadPackHandler(HttpServletRequest req,
HttpServletResponse rsp, UploadPackRunnable r) throws IOException {
try {
r.upload();
} catch (ServiceMayNotContinueException e) {
if (!e.isOutput() && !rsp.isCommitted()) {
rsp.reset();
sendError(req, rsp, e.getStatusCode(), e.getMessage());
}
} catch (Throwable e) { } catch (Throwable e) {
UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
log(up.getRepository(), e); log(up.getRepository(), e);
if (!rsp.isCommitted()) { if (!rsp.isCommitted()) {
rsp.reset(); rsp.reset();
sendError(req, rsp, SC_INTERNAL_SERVER_ERROR); sendError(req, rsp, SC_INTERNAL_SERVER_ERROR);
} }
return;
} }
} }

Loading…
Cancel
Save