]> source.dussan.org Git - jgit.git/commitdiff
Add utilities for smart HTTP error handling 67/4667/1
authorShawn O. Pearce <spearce@spearce.org>
Tue, 22 Nov 2011 23:51:11 +0000 (15:51 -0800)
committerShawn O. Pearce <spearce@spearce.org>
Tue, 22 Nov 2011 23:51:14 +0000 (15:51 -0800)
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

org.eclipse.jgit.http.server/resources/org/eclipse/jgit/http/server/HttpServerText.properties
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java [new file with mode: 0644]
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/HttpServerText.java
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/RepositoryFilter.java
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java

index a7643c5f9e2c77fb74639b23979de9855c08245f..e8118919043df7e1d0abe6f0893bed597294ff75 100644 (file)
@@ -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.
index 7b88ae346a6d0d92440b952efc0adc65993799f3..980d246d2dd5ef130d21d3749ae3c6aebb55b43f 100644 (file)
@@ -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 (file)
index 0000000..3d2aff1
--- /dev/null
@@ -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<String> 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<String> 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.
+        * <p>
+        * 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.
+        * <p>
+        * 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.
+        * <p>
+        * 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() {
+       }
+}
index 18743989bad9a402481baaddd6f9e5d4a4b7941d..2342fea3ca1f3b3f1f1631add9cc3e2f34b331fd 100644 (file)
@@ -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;
index 0c856d4aa5f8faa1a349a5adf5519f1fcd67579c..6af28ba0d3489901f090dadbddc2e9bbfd95a5a1 100644 (file)
@@ -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<HttpServletRequest> receivePackFactory,
                                List<Filter> 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;
                }
index d1bd9024e9d62a7fb9ec839496826bf86676be4d..571183682f4eec6eb5ba0601fffb0a044d8ad02b 100644 (file)
@@ -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);
-               }
-       }
 }
index 935867cef3889371a793c8586dce9754bd79f8e1..c0f1f6335b2510e7343bcc77f9b54d04d84f546c 100644 (file)
@@ -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());
                }
        }
 
index 178473c401b6f41b34ca0b01132b26902ccb5bb8..c7891dfc770a10c5a487eac95bac608b24b3b4e9 100644 (file)
@@ -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<HttpServletRequest> uploadPackFactory,
                                List<Filter> 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;
                }