Browse Source

Merge changes I828ac2de,I80e5b7cf

* changes:
  Add utilities for smart HTTP error handling
  Strip leading slashes in RepositoryFilter
stable-1.2
Shawn O. Pearce 13 years ago committed by Code Review
parent
commit
6ec174bfba
  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. 79
      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;
} }

79
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,98 +108,46 @@ 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) == '/')
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;
} }
if (name.startsWith("/"))
name = name.substring(1);
final Repository db; final Repository db;
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