From: Shawn O. Pearce Date: Tue, 22 Nov 2011 23:51:11 +0000 (-0800) Subject: Add utilities for smart HTTP error handling X-Git-Tag: v1.2.0.201112221803-r~21^2 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=867087bb1119aa0ed52c01095c121229929c007e;p=jgit.git Add utilities for smart HTTP error handling The GitSmartHttpTools class started as utility functions to help report useful error messages to users of the android.googlesource.com service. Now that the GitServlet and GitFilter classes support filters before a git-upload-pack or git-receive-pack request, server implementors may these routines helpful to report custom messages to clients. Using the sendError() method to return an HTTP 200 OK with error text embedded in the payload prevents native Git clients from retrying the action with a dumb Git or WebDAV HTTP request. Refactor some of the existing code to use these new error functions and protocol constants. The new sendError() function is very close to being identical to the old error handling code in RepositoryFilter, however we now use the POST Content-Type rather than the Accept HTTP header to check if the client will accept the error data in the response body rather than using the HTTP status code. This is a more reliable way of checking for native Git clients, as the Accept header was not always populated with the correct string in older versions of Git smart HTTP. Change-Id: I828ac2deb085af12b6689c10f86662ddd39bd1a2 --- diff --git a/org.eclipse.jgit.http.server/resources/org/eclipse/jgit/http/server/HttpServerText.properties b/org.eclipse.jgit.http.server/resources/org/eclipse/jgit/http/server/HttpServerText.properties index a7643c5f9e..e811891904 100644 --- a/org.eclipse.jgit.http.server/resources/org/eclipse/jgit/http/server/HttpServerText.properties +++ b/org.eclipse.jgit.http.server/resources/org/eclipse/jgit/http/server/HttpServerText.properties @@ -3,6 +3,7 @@ cannotGetLengthOf=Cannot get length of {0} encodingNotSupportedByThisLibrary={0} "{1}": not supported by this library. expectedRepositoryAttribute=Expected Repository attribute filterMustNotBeNull=filter must not be null +internalServerError=Internal server error internalErrorDuringReceivePack=Internal error during receive-pack internalErrorDuringUploadPack=Internal error during upload-pack internalServerErrorRequestAttributeWasAlreadySet=Internal server error, request attribute {0} was already set when {1} was invoked. 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 7b88ae346a..980d246d2d 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 @@ -196,7 +196,7 @@ public class GitFilter extends MetaFilter { initialized = true; if (uploadPackFactory != UploadPackFactory.DISABLED) { - ServletBinder b = serve("*/git-upload-pack"); + ServletBinder b = serve("*/" + GitSmartHttpTools.UPLOAD_PACK); b = b.through(new UploadPackServlet.Factory(uploadPackFactory)); for (Filter f : uploadPackFilters) b = b.through(f); @@ -204,7 +204,7 @@ public class GitFilter extends MetaFilter { } if (receivePackFactory != ReceivePackFactory.DISABLED) { - ServletBinder b = serve("*/git-receive-pack"); + ServletBinder b = serve("*/" + GitSmartHttpTools.RECEIVE_PACK); b = b.through(new ReceivePackServlet.Factory(receivePackFactory)); for (Filter f : receivePackFilters) b = b.through(f); 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 new file mode 100644 index 0000000000..3d2aff174d --- /dev/null +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2011, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.http.server; + +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; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.transport.PacketLineOut; + +/** + * Utility functions for handling the Git-over-HTTP protocol. + */ +public class GitSmartHttpTools { + private static final String INFO_REFS = Constants.INFO_REFS; + + /** Name of the git-upload-pack service. */ + public static final String UPLOAD_PACK = "git-upload-pack"; + + /** Name of the git-receive-pack service. */ + public static final String RECEIVE_PACK = "git-receive-pack"; + + /** Content type supplied by the client to the /git-upload-pack handler. */ + public static final String UPLOAD_PACK_REQUEST_TYPE = + "application/x-git-upload-pack-request"; + + /** Content type returned from the /git-upload-pack handler. */ + public static final String UPLOAD_PACK_RESULT_TYPE = + "application/x-git-upload-pack-result"; + + /** Content type supplied by the client to the /git-receive-pack handler. */ + public static final String RECEIVE_PACK_REQUEST_TYPE = + "application/x-git-receive-pack-request"; + + /** Content type returned from the /git-receive-pack handler. */ + public static final String RECEIVE_PACK_RESULT_TYPE = + "application/x-git-receive-pack-result"; + + /** Git service names accepted by the /info/refs?service= handler. */ + public static final List VALID_SERVICES = + Collections.unmodifiableList(Arrays.asList(new String[] { + UPLOAD_PACK, RECEIVE_PACK })); + + private static final String INFO_REFS_PATH = "/" + INFO_REFS; + private static final String UPLOAD_PACK_PATH = "/" + UPLOAD_PACK; + private static final String RECEIVE_PACK_PATH = "/" + RECEIVE_PACK; + + private static final List SERVICE_SUFFIXES = + Collections.unmodifiableList(Arrays.asList(new String[] { + INFO_REFS_PATH, UPLOAD_PACK_PATH, RECEIVE_PACK_PATH })); + + /** + * Check a request for Git-over-HTTP indicators. + * + * @param req + * the current HTTP request that may have been made by Git. + * @return true if the request is likely made by a Git client program. + */ + public static boolean isGitClient(HttpServletRequest req) { + return isInfoRefs(req) || isUploadPack(req) || isReceivePack(req); + } + + /** + * Send an error to the Git client or browser. + *

+ * Server implementors may use this method to send customized error messages + * 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. + * + * @param req + * current request. + * @param res + * current response. + * @param httpStatus + * HTTP status code to set if the client is not a Git client. + * @throws IOException + * the response cannot be sent. + */ + public static void sendError(HttpServletRequest req, + HttpServletResponse res, int httpStatus) throws IOException { + sendError(req, res, httpStatus, null); + } + + /** + * Send an error to the Git client or browser. + *

+ * Server implementors may use this method to send customized error messages + * 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. + * + * @param req + * current request. + * @param res + * current response. + * @param httpStatus + * HTTP status code to set if the client is not a Git client. + * @param textForGit + * plain text message to display on the user's console. This is + * shown only if the client is likely to be a Git client. If null + * or the empty string a default text is chosen based on the HTTP + * response code. + * @throws IOException + * the response cannot be sent. + */ + public static void sendError(HttpServletRequest req, + HttpServletResponse res, int httpStatus, String textForGit) + throws IOException { + if (textForGit == null || textForGit.length() == 0) { + switch (httpStatus) { + case SC_FORBIDDEN: + textForGit = HttpServerText.get().repositoryAccessForbidden; + break; + case SC_NOT_FOUND: + textForGit = HttpServerText.get().repositoryNotFound; + break; + case SC_INTERNAL_SERVER_ERROR: + textForGit = HttpServerText.get().internalServerError; + break; + default: + textForGit = "HTTP " + httpStatus; + break; + } + } + + 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(res, infoRefsResultType(svc), buf.toByteArray()); + } else if (isUploadPack(req)) { + pck.writeString("ERR " + textForGit); + send(res, UPLOAD_PACK_RESULT_TYPE, buf.toByteArray()); + } else if (isReceivePack(req)) { + pck.writeString("ERR " + textForGit); + send(res, RECEIVE_PACK_RESULT_TYPE, buf.toByteArray()); + } else { + res.sendError(httpStatus); + } + } + + private static void send(HttpServletResponse res, String type, byte[] buf) + throws IOException { + res.setStatus(HttpServletResponse.SC_OK); + res.setContentType(type); + res.setContentLength(buf.length); + ServletOutputStream os = res.getOutputStream(); + try { + os.write(buf); + } finally { + os.close(); + } + } + + /** + * Get the response Content-Type a client expects for the request. + *

+ * This method should only be invoked if + * {@link #isGitClient(HttpServletRequest)} is true. + * + * @param req + * current request. + * @return the Content-Type the client expects. + * @throws IllegalArgumentException + * the request is not a Git client request. See + * {@link #isGitClient(HttpServletRequest)}. + */ + public static String getResponseContentType(HttpServletRequest req) { + if (isInfoRefs(req)) + return infoRefsResultType(req.getParameter("service")); + else if (isUploadPack(req)) + return UPLOAD_PACK_RESULT_TYPE; + else if (isReceivePack(req)) + return RECEIVE_PACK_RESULT_TYPE; + else + throw new IllegalArgumentException(); + } + + static String infoRefsResultType(String svc) { + return "application/x-" + svc + "-advertisement"; + } + + /** + * Strip the Git service suffix from a request path. + * + * Generally the suffix is stripped by the {@code SuffixPipeline} handling + * the request, so this method is rarely needed. + * + * @param path + * the path of the request. + * @return the path up to the last path component before the service suffix; + * the path as-is if it contains no service suffix. + */ + public static String stripServiceSuffix(String path) { + for (String suffix : SERVICE_SUFFIXES) { + if (path.endsWith(suffix)) + return path.substring(0, path.length() - suffix.length()); + } + return path; + } + + /** + * Check if the HTTP request was for the /info/refs?service= Git handler. + * + * @param req + * current request. + * @return true if the request is for the /info/refs service. + */ + public static boolean isInfoRefs(HttpServletRequest req) { + return req.getRequestURI().endsWith(INFO_REFS_PATH) + && VALID_SERVICES.contains(req.getParameter("service")); + } + + /** + * Check if the HTTP request path ends with the /git-upload-pack handler. + * + * @param pathOrUri + * path or URI of the request. + * @return true if the request is for the /git-upload-pack handler. + */ + public static boolean isUploadPack(String pathOrUri) { + return pathOrUri != null && pathOrUri.endsWith(UPLOAD_PACK_PATH); + } + + /** + * Check if the HTTP request was for the /git-upload-pack Git handler. + * + * @param req + * current request. + * @return true if the request is for the /git-upload-pack handler. + */ + public static boolean isUploadPack(HttpServletRequest req) { + return isUploadPack(req.getRequestURI()) + && UPLOAD_PACK_REQUEST_TYPE.equals(req.getContentType()); + } + + /** + * Check if the HTTP request was for the /git-receive-pack Git handler. + * + * @param req + * current request. + * @return true if the request is for the /git-receive-pack handler. + */ + public static boolean isReceivePack(HttpServletRequest req) { + String uri = req.getRequestURI(); + return uri != null && uri.endsWith(RECEIVE_PACK_PATH) + && RECEIVE_PACK_REQUEST_TYPE.equals(req.getContentType()); + } + + private GitSmartHttpTools() { + } +} diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/HttpServerText.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/HttpServerText.java index 18743989ba..2342fea3ca 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/HttpServerText.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/HttpServerText.java @@ -65,6 +65,7 @@ public class HttpServerText extends TranslationBundle { /***/ public String filterMustNotBeNull; /***/ public String internalErrorDuringReceivePack; /***/ public String internalErrorDuringUploadPack; + /***/ public String internalServerError; /***/ public String internalServerErrorRequestAttributeWasAlreadySet; /***/ public String invalidBoolean; /***/ public String invalidIndex; 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 0c856d4aa5..6af28ba0d3 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 @@ -47,6 +47,10 @@ 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_UNAUTHORIZED; import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE; +import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK; +import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK_REQUEST_TYPE; +import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK_RESULT_TYPE; +import static org.eclipse.jgit.http.server.GitSmartHttpTools.sendError; import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER; import static org.eclipse.jgit.http.server.ServletUtils.getInputStream; import static org.eclipse.jgit.http.server.ServletUtils.getRepository; @@ -74,10 +78,6 @@ import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; /** Server side implementation of smart push over HTTP. */ class ReceivePackServlet extends HttpServlet { - private static final String REQ_TYPE = "application/x-git-receive-pack-request"; - - private static final String RSP_TYPE = "application/x-git-receive-pack-result"; - private static final long serialVersionUID = 1L; static class InfoRefs extends SmartServiceInfoRefs { @@ -85,7 +85,7 @@ class ReceivePackServlet extends HttpServlet { InfoRefs(ReceivePackFactory receivePackFactory, List filters) { - super("git-receive-pack", filters); + super(RECEIVE_PACK, filters); this.receivePackFactory = receivePackFactory; } @@ -129,7 +129,7 @@ class ReceivePackServlet extends HttpServlet { return; } catch (ServiceNotEnabledException e) { - RepositoryFilter.sendError(SC_FORBIDDEN, req, rsp); + sendError(req, rsp, SC_FORBIDDEN); return; } @@ -153,7 +153,7 @@ class ReceivePackServlet extends HttpServlet { @Override public void doPost(final HttpServletRequest req, final HttpServletResponse rsp) throws IOException { - if (!REQ_TYPE.equals(req.getContentType())) { + if (!RECEIVE_PACK_REQUEST_TYPE.equals(req.getContentType())) { rsp.sendError(SC_UNSUPPORTED_MEDIA_TYPE); return; } @@ -161,7 +161,7 @@ class ReceivePackServlet extends HttpServlet { ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER); try { rp.setBiDirectionalPipe(false); - rsp.setContentType(RSP_TYPE); + rsp.setContentType(RECEIVE_PACK_RESULT_TYPE); final SmartOutputStream out = new SmartOutputStream(req, rsp) { @Override @@ -181,7 +181,7 @@ class ReceivePackServlet extends HttpServlet { getServletContext().log(HttpServerText.get().internalErrorDuringReceivePack, e); if (!rsp.isCommitted()) { rsp.reset(); - rsp.sendError(SC_INTERNAL_SERVER_ERROR); + sendError(req, rsp, SC_INTERNAL_SERVER_ERROR); } return; } diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/RepositoryFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/RepositoryFilter.java index d1bd9024e9..571183682f 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/RepositoryFilter.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/RepositoryFilter.java @@ -47,8 +47,8 @@ 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; import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; +import static org.eclipse.jgit.http.server.GitSmartHttpTools.sendError; import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_REPOSITORY; -import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT; import java.io.IOException; import java.text.MessageFormat; @@ -65,7 +65,6 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.transport.PacketLineOut; import org.eclipse.jgit.transport.resolver.RepositoryResolver; import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; @@ -109,23 +108,24 @@ public class RepositoryFilter implements Filter { } public void doFilter(final ServletRequest request, - final ServletResponse rsp, final FilterChain chain) + final ServletResponse response, final FilterChain chain) throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse res = (HttpServletResponse) response; + if (request.getAttribute(ATTRIBUTE_REPOSITORY) != null) { context.log(MessageFormat.format(HttpServerText.get().internalServerErrorRequestAttributeWasAlreadySet , ATTRIBUTE_REPOSITORY , getClass().getName())); - ((HttpServletResponse) rsp).sendError(SC_INTERNAL_SERVER_ERROR); + sendError(req, res, SC_INTERNAL_SERVER_ERROR); return; } - final HttpServletRequest req = (HttpServletRequest) request; - String name = req.getPathInfo(); while (name != null && 0 < name.length() && name.charAt(0) == '/') name = name.substring(1); if (name == null || name.length() == 0) { - ((HttpServletResponse) rsp).sendError(SC_NOT_FOUND); + sendError(req, res, SC_NOT_FOUND); return; } @@ -133,74 +133,21 @@ public class RepositoryFilter implements Filter { try { db = resolver.open(req, name); } catch (RepositoryNotFoundException e) { - sendError(SC_NOT_FOUND, req, (HttpServletResponse) rsp); + sendError(req, res, SC_NOT_FOUND); return; } catch (ServiceNotEnabledException e) { - sendError(SC_FORBIDDEN, req, (HttpServletResponse) rsp); + sendError(req, res, SC_FORBIDDEN); return; } catch (ServiceNotAuthorizedException e) { - ((HttpServletResponse) rsp).sendError(SC_UNAUTHORIZED); + res.sendError(SC_UNAUTHORIZED); return; } try { request.setAttribute(ATTRIBUTE_REPOSITORY, db); - chain.doFilter(request, rsp); + chain.doFilter(request, response); } finally { request.removeAttribute(ATTRIBUTE_REPOSITORY); db.close(); } } - - static void sendError(int statusCode, HttpServletRequest req, - HttpServletResponse rsp) throws IOException { - String svc = req.getParameter("service"); - - if (req.getRequestURI().endsWith("/info/refs") && isService(svc)) { - // Smart HTTP service request, use an ERR response. - rsp.setContentType("application/x-" + svc + "-advertisement"); - - SmartOutputStream buf = new SmartOutputStream(req, rsp); - PacketLineOut out = new PacketLineOut(buf); - out.writeString("# service=" + svc + "\n"); - out.end(); - out.writeString("ERR " + translate(statusCode)); - buf.close(); - return; - } - - String accept = req.getHeader(HDR_ACCEPT); - if (accept != null && accept.contains(UploadPackServlet.RSP_TYPE)) { - // An upload-pack wants ACK or NAK, return ERR - // and the client will print this instead. - rsp.setContentType(UploadPackServlet.RSP_TYPE); - SmartOutputStream buf = new SmartOutputStream(req, rsp); - PacketLineOut out = new PacketLineOut(buf); - out.writeString("ERR " + translate(statusCode)); - buf.close(); - return; - } - - // Otherwise fail with an HTTP error code instead of an - // application level message. This may not be as pretty - // of a result for the user, but its better than nothing. - // - rsp.sendError(statusCode); - } - - private static boolean isService(String svc) { - return "git-upload-pack".equals(svc) || "git-receive-pack".equals(svc); - } - - private static String translate(int statusCode) { - switch (statusCode) { - case SC_NOT_FOUND: - return HttpServerText.get().repositoryNotFound; - - case SC_FORBIDDEN: - return HttpServerText.get().repositoryAccessForbidden; - - default: - return String.valueOf(statusCode); - } - } } diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java index 935867cef3..c0f1f6335b 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java @@ -44,8 +44,9 @@ package org.eclipse.jgit.http.server; import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; -import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE; import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; +import static org.eclipse.jgit.http.server.GitSmartHttpTools.infoRefsResultType; +import static org.eclipse.jgit.http.server.GitSmartHttpTools.sendError; import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER; import static org.eclipse.jgit.http.server.ServletUtils.getRepository; @@ -90,17 +91,17 @@ abstract class SmartServiceInfoRefs implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { final HttpServletRequest req = (HttpServletRequest) request; - final HttpServletResponse rsp = (HttpServletResponse) response; + final HttpServletResponse res = (HttpServletResponse) response; if (svc.equals(req.getParameter("service"))) { final Repository db = getRepository(req); try { begin(req, db); } catch (ServiceNotAuthorizedException e) { - rsp.sendError(SC_UNAUTHORIZED); + res.sendError(SC_UNAUTHORIZED); return; } catch (ServiceNotEnabledException e) { - rsp.sendError(SC_FORBIDDEN); + sendError(req, res, SC_FORBIDDEN); return; } @@ -120,10 +121,10 @@ abstract class SmartServiceInfoRefs implements Filter { private void service(ServletRequest request, ServletResponse response) throws IOException { final HttpServletRequest req = (HttpServletRequest) request; - final HttpServletResponse rsp = (HttpServletResponse) response; - final SmartOutputStream buf = new SmartOutputStream(req, rsp); + final HttpServletResponse res = (HttpServletResponse) response; + final SmartOutputStream buf = new SmartOutputStream(req, res); try { - rsp.setContentType("application/x-" + svc + "-advertisement"); + res.setContentType(infoRefsResultType(svc)); final PacketLineOut out = new PacketLineOut(buf); out.writeString("# service=" + svc + "\n"); @@ -131,16 +132,16 @@ abstract class SmartServiceInfoRefs implements Filter { advertise(req, new PacketLineOutRefAdvertiser(out)); buf.close(); } catch (ServiceNotAuthorizedException e) { - rsp.sendError(SC_UNAUTHORIZED); + res.sendError(SC_UNAUTHORIZED); } catch (ServiceNotEnabledException e) { - rsp.sendError(SC_FORBIDDEN); + sendError(req, res, SC_FORBIDDEN); } catch (UploadPackMayNotContinueException e) { if (e.isOutput()) buf.close(); else - rsp.sendError(SC_SERVICE_UNAVAILABLE); + sendError(req, res, SC_FORBIDDEN, e.getMessage()); } } 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 178473c401..c7891dfc77 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 @@ -45,9 +45,12 @@ package org.eclipse.jgit.http.server; 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_SERVICE_UNAVAILABLE; import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE; +import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK; +import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK_REQUEST_TYPE; +import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK_RESULT_TYPE; +import static org.eclipse.jgit.http.server.GitSmartHttpTools.sendError; import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER; import static org.eclipse.jgit.http.server.ServletUtils.getInputStream; import static org.eclipse.jgit.http.server.ServletUtils.getRepository; @@ -76,10 +79,6 @@ import org.eclipse.jgit.transport.resolver.UploadPackFactory; /** Server side implementation of smart fetch over HTTP. */ class UploadPackServlet extends HttpServlet { - private static final String REQ_TYPE = "application/x-git-upload-pack-request"; - - static final String RSP_TYPE = "application/x-git-upload-pack-result"; - private static final long serialVersionUID = 1L; static class InfoRefs extends SmartServiceInfoRefs { @@ -87,7 +86,7 @@ class UploadPackServlet extends HttpServlet { InfoRefs(UploadPackFactory uploadPackFactory, List filters) { - super("git-upload-pack", filters); + super(UPLOAD_PACK, filters); this.uploadPackFactory = uploadPackFactory; } @@ -132,7 +131,7 @@ class UploadPackServlet extends HttpServlet { return; } catch (ServiceNotEnabledException e) { - RepositoryFilter.sendError(SC_FORBIDDEN, req, rsp); + sendError(req, rsp, SC_FORBIDDEN); return; } @@ -156,7 +155,7 @@ class UploadPackServlet extends HttpServlet { @Override public void doPost(final HttpServletRequest req, final HttpServletResponse rsp) throws IOException { - if (!REQ_TYPE.equals(req.getContentType())) { + if (!UPLOAD_PACK_REQUEST_TYPE.equals(req.getContentType())) { rsp.sendError(SC_UNSUPPORTED_MEDIA_TYPE); return; } @@ -164,7 +163,7 @@ class UploadPackServlet extends HttpServlet { UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER); try { up.setBiDirectionalPipe(false); - rsp.setContentType(RSP_TYPE); + rsp.setContentType(UPLOAD_PACK_RESULT_TYPE); final SmartOutputStream out = new SmartOutputStream(req, rsp) { @Override @@ -178,11 +177,12 @@ class UploadPackServlet extends HttpServlet { } catch (UploadPackMayNotContinueException e) { if (!e.isOutput() && !rsp.isCommitted()) { rsp.reset(); - rsp.sendError(SC_SERVICE_UNAVAILABLE); + sendError(req, rsp, SC_FORBIDDEN, e.getMessage()); } return; } catch (UploadPackInternalServerErrorException e) { + // Special case exception, error message was sent to client. getServletContext().log( HttpServerText.get().internalErrorDuringUploadPack, e.getCause()); @@ -191,7 +191,7 @@ class UploadPackServlet extends HttpServlet { getServletContext().log(HttpServerText.get().internalErrorDuringUploadPack, e); if (!rsp.isCommitted()) { rsp.reset(); - rsp.sendError(SC_INTERNAL_SERVER_ERROR); + sendError(req, rsp, SC_INTERNAL_SERVER_ERROR); } return; }