Browse Source

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
stable-1.2
Shawn O. Pearce 13 years ago
parent
commit
867087bb11
  1. 1
      org.eclipse.jgit.http.server/resources/org/eclipse/jgit/http/server/HttpServerText.properties
  2. 4
      org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java
  3. 309
      org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java
  4. 1
      org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/HttpServerText.java
  5. 18
      org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
  6. 75
      org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/RepositoryFilter.java
  7. 21
      org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java
  8. 22
      org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java

1
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. encodingNotSupportedByThisLibrary={0} "{1}": not supported by this library.
expectedRepositoryAttribute=Expected Repository attribute expectedRepositoryAttribute=Expected Repository attribute
filterMustNotBeNull=filter must not be null filterMustNotBeNull=filter must not be null
internalServerError=Internal server error
internalErrorDuringReceivePack=Internal error during receive-pack internalErrorDuringReceivePack=Internal error during receive-pack
internalErrorDuringUploadPack=Internal error during upload-pack internalErrorDuringUploadPack=Internal error during upload-pack
internalServerErrorRequestAttributeWasAlreadySet=Internal server error, request attribute {0} was already set when {1} was invoked. internalServerErrorRequestAttributeWasAlreadySet=Internal server error, request attribute {0} was already set when {1} was invoked.

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

@ -196,7 +196,7 @@ public class GitFilter extends MetaFilter {
initialized = true; initialized = true;
if (uploadPackFactory != UploadPackFactory.DISABLED) { if (uploadPackFactory != UploadPackFactory.DISABLED) {
ServletBinder b = serve("*/git-upload-pack"); ServletBinder b = serve("*/" + GitSmartHttpTools.UPLOAD_PACK);
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);
@ -204,7 +204,7 @@ public class GitFilter extends MetaFilter {
} }
if (receivePackFactory != ReceivePackFactory.DISABLED) { if (receivePackFactory != ReceivePackFactory.DISABLED) {
ServletBinder b = serve("*/git-receive-pack"); ServletBinder b = serve("*/" + GitSmartHttpTools.RECEIVE_PACK);
b = b.through(new ReceivePackServlet.Factory(receivePackFactory)); b = b.through(new ReceivePackServlet.Factory(receivePackFactory));
for (Filter f : receivePackFilters) for (Filter f : receivePackFilters)
b = b.through(f); b = b.through(f);

309
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<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() {
}
}

1
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 filterMustNotBeNull;
/***/ public String internalErrorDuringReceivePack; /***/ public String internalErrorDuringReceivePack;
/***/ public String internalErrorDuringUploadPack; /***/ public String internalErrorDuringUploadPack;
/***/ public String internalServerError;
/***/ public String internalServerErrorRequestAttributeWasAlreadySet; /***/ public String internalServerErrorRequestAttributeWasAlreadySet;
/***/ public String invalidBoolean; /***/ public String invalidBoolean;
/***/ public String invalidIndex; /***/ public String invalidIndex;

18
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_INTERNAL_SERVER_ERROR;
import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE; 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.ATTRIBUTE_HANDLER;
import static org.eclipse.jgit.http.server.ServletUtils.getInputStream; import static org.eclipse.jgit.http.server.ServletUtils.getInputStream;
import static org.eclipse.jgit.http.server.ServletUtils.getRepository; 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. */ /** Server side implementation of smart push over HTTP. */
class ReceivePackServlet extends HttpServlet { 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; private static final long serialVersionUID = 1L;
static class InfoRefs extends SmartServiceInfoRefs { static class InfoRefs extends SmartServiceInfoRefs {
@ -85,7 +85,7 @@ class ReceivePackServlet extends HttpServlet {
InfoRefs(ReceivePackFactory<HttpServletRequest> receivePackFactory, InfoRefs(ReceivePackFactory<HttpServletRequest> receivePackFactory,
List<Filter> filters) { List<Filter> filters) {
super("git-receive-pack", filters); super(RECEIVE_PACK, filters);
this.receivePackFactory = receivePackFactory; this.receivePackFactory = receivePackFactory;
} }
@ -129,7 +129,7 @@ class ReceivePackServlet extends HttpServlet {
return; return;
} catch (ServiceNotEnabledException e) { } catch (ServiceNotEnabledException e) {
RepositoryFilter.sendError(SC_FORBIDDEN, req, rsp); sendError(req, rsp, SC_FORBIDDEN);
return; return;
} }
@ -153,7 +153,7 @@ class ReceivePackServlet extends HttpServlet {
@Override @Override
public void doPost(final HttpServletRequest req, public void doPost(final HttpServletRequest req,
final HttpServletResponse rsp) throws IOException { 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); rsp.sendError(SC_UNSUPPORTED_MEDIA_TYPE);
return; return;
} }
@ -161,7 +161,7 @@ class ReceivePackServlet extends HttpServlet {
ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER); ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER);
try { try {
rp.setBiDirectionalPipe(false); rp.setBiDirectionalPipe(false);
rsp.setContentType(RSP_TYPE); rsp.setContentType(RECEIVE_PACK_RESULT_TYPE);
final SmartOutputStream out = new SmartOutputStream(req, rsp) { final SmartOutputStream out = new SmartOutputStream(req, rsp) {
@Override @Override
@ -181,7 +181,7 @@ class ReceivePackServlet extends HttpServlet {
getServletContext().log(HttpServerText.get().internalErrorDuringReceivePack, e); getServletContext().log(HttpServerText.get().internalErrorDuringReceivePack, e);
if (!rsp.isCommitted()) { if (!rsp.isCommitted()) {
rsp.reset(); rsp.reset();
rsp.sendError(SC_INTERNAL_SERVER_ERROR); sendError(req, rsp, SC_INTERNAL_SERVER_ERROR);
} }
return; return;
} }

75
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_INTERNAL_SERVER_ERROR;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; 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.http.server.ServletUtils.ATTRIBUTE_REPOSITORY;
import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT;
import java.io.IOException; import java.io.IOException;
import java.text.MessageFormat; import java.text.MessageFormat;
@ -65,7 +65,6 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Repository; 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.RepositoryResolver;
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
@ -109,23 +108,24 @@ public class RepositoryFilter implements Filter {
} }
public void doFilter(final ServletRequest request, public void doFilter(final ServletRequest request,
final ServletResponse rsp, final FilterChain chain) final ServletResponse response, final FilterChain chain)
throws IOException, ServletException { throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
if (request.getAttribute(ATTRIBUTE_REPOSITORY) != null) { if (request.getAttribute(ATTRIBUTE_REPOSITORY) != null) {
context.log(MessageFormat.format(HttpServerText.get().internalServerErrorRequestAttributeWasAlreadySet context.log(MessageFormat.format(HttpServerText.get().internalServerErrorRequestAttributeWasAlreadySet
, ATTRIBUTE_REPOSITORY , ATTRIBUTE_REPOSITORY
, getClass().getName())); , getClass().getName()));
((HttpServletResponse) rsp).sendError(SC_INTERNAL_SERVER_ERROR); sendError(req, res, SC_INTERNAL_SERVER_ERROR);
return; return;
} }
final HttpServletRequest req = (HttpServletRequest) request;
String name = req.getPathInfo(); String name = req.getPathInfo();
while (name != null && 0 < name.length() && name.charAt(0) == '/') while (name != null && 0 < name.length() && name.charAt(0) == '/')
name = name.substring(1); name = name.substring(1);
if (name == null || name.length() == 0) { if (name == null || name.length() == 0) {
((HttpServletResponse) rsp).sendError(SC_NOT_FOUND); sendError(req, res, SC_NOT_FOUND);
return; return;
} }
@ -133,74 +133,21 @@ public class RepositoryFilter implements Filter {
try { try {
db = resolver.open(req, name); db = resolver.open(req, name);
} catch (RepositoryNotFoundException e) { } catch (RepositoryNotFoundException e) {
sendError(SC_NOT_FOUND, req, (HttpServletResponse) rsp); sendError(req, res, SC_NOT_FOUND);
return; return;
} catch (ServiceNotEnabledException e) { } catch (ServiceNotEnabledException e) {
sendError(SC_FORBIDDEN, req, (HttpServletResponse) rsp); sendError(req, res, SC_FORBIDDEN);
return; return;
} catch (ServiceNotAuthorizedException e) { } catch (ServiceNotAuthorizedException e) {
((HttpServletResponse) rsp).sendError(SC_UNAUTHORIZED); res.sendError(SC_UNAUTHORIZED);
return; return;
} }
try { try {
request.setAttribute(ATTRIBUTE_REPOSITORY, db); request.setAttribute(ATTRIBUTE_REPOSITORY, db);
chain.doFilter(request, rsp); chain.doFilter(request, response);
} finally { } finally {
request.removeAttribute(ATTRIBUTE_REPOSITORY); request.removeAttribute(ATTRIBUTE_REPOSITORY);
db.close(); 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);
}
}
} }

21
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java

@ -44,8 +44,9 @@
package org.eclipse.jgit.http.server; package org.eclipse.jgit.http.server;
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; 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 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.ATTRIBUTE_HANDLER;
import static org.eclipse.jgit.http.server.ServletUtils.getRepository; 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, public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException { FilterChain chain) throws IOException, ServletException {
final HttpServletRequest req = (HttpServletRequest) request; final HttpServletRequest req = (HttpServletRequest) request;
final HttpServletResponse rsp = (HttpServletResponse) response; final HttpServletResponse res = (HttpServletResponse) response;
if (svc.equals(req.getParameter("service"))) { if (svc.equals(req.getParameter("service"))) {
final Repository db = getRepository(req); final Repository db = getRepository(req);
try { try {
begin(req, db); begin(req, db);
} catch (ServiceNotAuthorizedException e) { } catch (ServiceNotAuthorizedException e) {
rsp.sendError(SC_UNAUTHORIZED); res.sendError(SC_UNAUTHORIZED);
return; return;
} catch (ServiceNotEnabledException e) { } catch (ServiceNotEnabledException e) {
rsp.sendError(SC_FORBIDDEN); sendError(req, res, SC_FORBIDDEN);
return; return;
} }
@ -120,10 +121,10 @@ abstract class SmartServiceInfoRefs implements Filter {
private void service(ServletRequest request, ServletResponse response) private void service(ServletRequest request, ServletResponse response)
throws IOException { throws IOException {
final HttpServletRequest req = (HttpServletRequest) request; final HttpServletRequest req = (HttpServletRequest) request;
final HttpServletResponse rsp = (HttpServletResponse) response; final HttpServletResponse res = (HttpServletResponse) response;
final SmartOutputStream buf = new SmartOutputStream(req, rsp); final SmartOutputStream buf = new SmartOutputStream(req, res);
try { try {
rsp.setContentType("application/x-" + svc + "-advertisement"); res.setContentType(infoRefsResultType(svc));
final PacketLineOut out = new PacketLineOut(buf); final PacketLineOut out = new PacketLineOut(buf);
out.writeString("# service=" + svc + "\n"); out.writeString("# service=" + svc + "\n");
@ -131,16 +132,16 @@ abstract class SmartServiceInfoRefs implements Filter {
advertise(req, new PacketLineOutRefAdvertiser(out)); advertise(req, new PacketLineOutRefAdvertiser(out));
buf.close(); buf.close();
} catch (ServiceNotAuthorizedException e) { } catch (ServiceNotAuthorizedException e) {
rsp.sendError(SC_UNAUTHORIZED); res.sendError(SC_UNAUTHORIZED);
} catch (ServiceNotEnabledException e) { } catch (ServiceNotEnabledException e) {
rsp.sendError(SC_FORBIDDEN); sendError(req, res, SC_FORBIDDEN);
} catch (UploadPackMayNotContinueException e) { } catch (UploadPackMayNotContinueException e) {
if (e.isOutput()) if (e.isOutput())
buf.close(); buf.close();
else else
rsp.sendError(SC_SERVICE_UNAVAILABLE); sendError(req, res, SC_FORBIDDEN, e.getMessage());
} }
} }

22
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_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; 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_UNAUTHORIZED;
import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE; 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.ATTRIBUTE_HANDLER;
import static org.eclipse.jgit.http.server.ServletUtils.getInputStream; import static org.eclipse.jgit.http.server.ServletUtils.getInputStream;
import static org.eclipse.jgit.http.server.ServletUtils.getRepository; 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. */ /** Server side implementation of smart fetch over HTTP. */
class UploadPackServlet extends HttpServlet { 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; private static final long serialVersionUID = 1L;
static class InfoRefs extends SmartServiceInfoRefs { static class InfoRefs extends SmartServiceInfoRefs {
@ -87,7 +86,7 @@ class UploadPackServlet extends HttpServlet {
InfoRefs(UploadPackFactory<HttpServletRequest> uploadPackFactory, InfoRefs(UploadPackFactory<HttpServletRequest> uploadPackFactory,
List<Filter> filters) { List<Filter> filters) {
super("git-upload-pack", filters); super(UPLOAD_PACK, filters);
this.uploadPackFactory = uploadPackFactory; this.uploadPackFactory = uploadPackFactory;
} }
@ -132,7 +131,7 @@ class UploadPackServlet extends HttpServlet {
return; return;
} catch (ServiceNotEnabledException e) { } catch (ServiceNotEnabledException e) {
RepositoryFilter.sendError(SC_FORBIDDEN, req, rsp); sendError(req, rsp, SC_FORBIDDEN);
return; return;
} }
@ -156,7 +155,7 @@ class UploadPackServlet extends HttpServlet {
@Override @Override
public void doPost(final HttpServletRequest req, public void doPost(final HttpServletRequest req,
final HttpServletResponse rsp) throws IOException { 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); rsp.sendError(SC_UNSUPPORTED_MEDIA_TYPE);
return; return;
} }
@ -164,7 +163,7 @@ class UploadPackServlet extends HttpServlet {
UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER); UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
try { try {
up.setBiDirectionalPipe(false); up.setBiDirectionalPipe(false);
rsp.setContentType(RSP_TYPE); rsp.setContentType(UPLOAD_PACK_RESULT_TYPE);
final SmartOutputStream out = new SmartOutputStream(req, rsp) { final SmartOutputStream out = new SmartOutputStream(req, rsp) {
@Override @Override
@ -178,11 +177,12 @@ class UploadPackServlet extends HttpServlet {
} catch (UploadPackMayNotContinueException e) { } catch (UploadPackMayNotContinueException e) {
if (!e.isOutput() && !rsp.isCommitted()) { if (!e.isOutput() && !rsp.isCommitted()) {
rsp.reset(); rsp.reset();
rsp.sendError(SC_SERVICE_UNAVAILABLE); sendError(req, rsp, SC_FORBIDDEN, e.getMessage());
} }
return; return;
} catch (UploadPackInternalServerErrorException e) { } catch (UploadPackInternalServerErrorException e) {
// Special case exception, error message was sent to client.
getServletContext().log( getServletContext().log(
HttpServerText.get().internalErrorDuringUploadPack, HttpServerText.get().internalErrorDuringUploadPack,
e.getCause()); e.getCause());
@ -191,7 +191,7 @@ class UploadPackServlet extends HttpServlet {
getServletContext().log(HttpServerText.get().internalErrorDuringUploadPack, e); getServletContext().log(HttpServerText.get().internalErrorDuringUploadPack, e);
if (!rsp.isCommitted()) { if (!rsp.isCommitted()) {
rsp.reset(); rsp.reset();
rsp.sendError(SC_INTERNAL_SERVER_ERROR); sendError(req, rsp, SC_INTERNAL_SERVER_ERROR);
} }
return; return;
} }

Loading…
Cancel
Save