15e33a1deSShawn O. Pearce /* 25c5f7c6bSMatthias Sohn * Copyright (C) 2009-2010, Google Inc. and others 35e33a1deSShawn O. Pearce * 45c5f7c6bSMatthias Sohn * This program and the accompanying materials are made available under the 55c5f7c6bSMatthias Sohn * terms of the Eclipse Distribution License v. 1.0 which is available at 65c5f7c6bSMatthias Sohn * https://www.eclipse.org/org/documents/edl-v10.php. 75e33a1deSShawn O. Pearce * 85c5f7c6bSMatthias Sohn * SPDX-License-Identifier: BSD-3-Clause 95e33a1deSShawn O. Pearce */ 105e33a1deSShawn O. Pearce 115e33a1deSShawn O. Pearce package org.eclipse.jgit.http.server; 125e33a1deSShawn O. Pearce 135e33a1deSShawn O. Pearce import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; 145e33a1deSShawn O. Pearce import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED; 155e33a1deSShawn O. Pearce import static org.eclipse.jgit.http.server.ServletUtils.getRepository; 165e33a1deSShawn O. Pearce import static org.eclipse.jgit.util.HttpSupport.HDR_ETAG; 175e33a1deSShawn O. Pearce import static org.eclipse.jgit.util.HttpSupport.HDR_IF_MODIFIED_SINCE; 185e33a1deSShawn O. Pearce import static org.eclipse.jgit.util.HttpSupport.HDR_IF_NONE_MATCH; 195e33a1deSShawn O. Pearce import static org.eclipse.jgit.util.HttpSupport.HDR_LAST_MODIFIED; 205e33a1deSShawn O. Pearce 215e33a1deSShawn O. Pearce import java.io.File; 225e33a1deSShawn O. Pearce import java.io.FileNotFoundException; 235e33a1deSShawn O. Pearce import java.io.IOException; 2495e8264cSMatthias Sohn import java.time.Instant; 255e33a1deSShawn O. Pearce 265e33a1deSShawn O. Pearce import javax.servlet.ServletException; 275e33a1deSShawn O. Pearce import javax.servlet.http.HttpServlet; 285e33a1deSShawn O. Pearce import javax.servlet.http.HttpServletRequest; 295e33a1deSShawn O. Pearce import javax.servlet.http.HttpServletResponse; 305e33a1deSShawn O. Pearce 31f32b8612SShawn Pearce import org.eclipse.jgit.internal.storage.file.ObjectDirectory; 325e33a1deSShawn O. Pearce import org.eclipse.jgit.lib.Repository; 335e33a1deSShawn O. Pearce 345e33a1deSShawn O. Pearce /** Sends any object from {@code GIT_DIR/objects/??/0 38}, or any pack file. */ 355e33a1deSShawn O. Pearce abstract class ObjectFileServlet extends HttpServlet { 365e33a1deSShawn O. Pearce private static final long serialVersionUID = 1L; 375e33a1deSShawn O. Pearce 385e33a1deSShawn O. Pearce static class Loose extends ObjectFileServlet { 395e33a1deSShawn O. Pearce private static final long serialVersionUID = 1L; 405e33a1deSShawn O. Pearce Loose()415e33a1deSShawn O. Pearce Loose() { 425e33a1deSShawn O. Pearce super("application/x-git-loose-object"); 435e33a1deSShawn O. Pearce } 445e33a1deSShawn O. Pearce 455e33a1deSShawn O. Pearce @Override etag(FileSender sender)46f3ec7cf3SHan-Wen Nienhuys String etag(FileSender sender) throws IOException { 4795e8264cSMatthias Sohn Instant lastModified = sender.getLastModified(); 4895e8264cSMatthias Sohn return Long.toHexString(lastModified.getEpochSecond()) 4995e8264cSMatthias Sohn + Long.toHexString(lastModified.getNano()); 505e33a1deSShawn O. Pearce } 515e33a1deSShawn O. Pearce } 525e33a1deSShawn O. Pearce 53*064834d3SDavid Pursehouse private abstract static class PackData extends ObjectFileServlet { 545e33a1deSShawn O. Pearce private static final long serialVersionUID = 1L; 555e33a1deSShawn O. Pearce PackData(String contentType)565e33a1deSShawn O. Pearce PackData(String contentType) { 575e33a1deSShawn O. Pearce super(contentType); 585e33a1deSShawn O. Pearce } 595e33a1deSShawn O. Pearce 605e33a1deSShawn O. Pearce @Override etag(FileSender sender)61f3ec7cf3SHan-Wen Nienhuys String etag(FileSender sender) throws IOException { 625e33a1deSShawn O. Pearce return sender.getTailChecksum(); 635e33a1deSShawn O. Pearce } 645e33a1deSShawn O. Pearce } 655e33a1deSShawn O. Pearce 665e33a1deSShawn O. Pearce static class Pack extends PackData { 675e33a1deSShawn O. Pearce private static final long serialVersionUID = 1L; 685e33a1deSShawn O. Pearce Pack()695e33a1deSShawn O. Pearce Pack() { 705e33a1deSShawn O. Pearce super("application/x-git-packed-objects"); 715e33a1deSShawn O. Pearce } 725e33a1deSShawn O. Pearce } 735e33a1deSShawn O. Pearce 745e33a1deSShawn O. Pearce static class PackIdx extends PackData { 755e33a1deSShawn O. Pearce private static final long serialVersionUID = 1L; 765e33a1deSShawn O. Pearce PackIdx()775e33a1deSShawn O. Pearce PackIdx() { 785e33a1deSShawn O. Pearce super("application/x-git-packed-objects-toc"); 795e33a1deSShawn O. Pearce } 805e33a1deSShawn O. Pearce } 815e33a1deSShawn O. Pearce 825e33a1deSShawn O. Pearce private final String contentType; 835e33a1deSShawn O. Pearce ObjectFileServlet(String contentType)84f3ec7cf3SHan-Wen Nienhuys ObjectFileServlet(String contentType) { 855e33a1deSShawn O. Pearce this.contentType = contentType; 865e33a1deSShawn O. Pearce } 875e33a1deSShawn O. Pearce etag(FileSender sender)885e33a1deSShawn O. Pearce abstract String etag(FileSender sender) throws IOException; 895e33a1deSShawn O. Pearce 903b00041cSMatthias Sohn /** {@inheritDoc} */ 915e33a1deSShawn O. Pearce @Override doGet(final HttpServletRequest req, final HttpServletResponse rsp)925e33a1deSShawn O. Pearce public void doGet(final HttpServletRequest req, 935e33a1deSShawn O. Pearce final HttpServletResponse rsp) throws IOException { 945e33a1deSShawn O. Pearce serve(req, rsp, true); 955e33a1deSShawn O. Pearce } 965e33a1deSShawn O. Pearce 973b00041cSMatthias Sohn /** {@inheritDoc} */ 985e33a1deSShawn O. Pearce @Override doHead(final HttpServletRequest req, final HttpServletResponse rsp)995e33a1deSShawn O. Pearce protected void doHead(final HttpServletRequest req, 1005e33a1deSShawn O. Pearce final HttpServletResponse rsp) throws ServletException, IOException { 1015e33a1deSShawn O. Pearce serve(req, rsp, false); 1025e33a1deSShawn O. Pearce } 1035e33a1deSShawn O. Pearce serve(final HttpServletRequest req, final HttpServletResponse rsp, final boolean sendBody)1045e33a1deSShawn O. Pearce private void serve(final HttpServletRequest req, 1055e33a1deSShawn O. Pearce final HttpServletResponse rsp, final boolean sendBody) 1065e33a1deSShawn O. Pearce throws IOException { 1075e33a1deSShawn O. Pearce final File obj = new File(objects(req), req.getPathInfo()); 1085e33a1deSShawn O. Pearce final FileSender sender; 1095e33a1deSShawn O. Pearce try { 1105e33a1deSShawn O. Pearce sender = new FileSender(obj); 1115e33a1deSShawn O. Pearce } catch (FileNotFoundException e) { 1125e33a1deSShawn O. Pearce rsp.sendError(SC_NOT_FOUND); 1135e33a1deSShawn O. Pearce return; 1145e33a1deSShawn O. Pearce } 1155e33a1deSShawn O. Pearce 1165e33a1deSShawn O. Pearce try { 1175e33a1deSShawn O. Pearce final String etag = etag(sender); 11895e8264cSMatthias Sohn // HTTP header Last-Modified header has a resolution of 1 sec, see 11995e8264cSMatthias Sohn // https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.29 12095e8264cSMatthias Sohn final long lastModified = sender.getLastModified().getEpochSecond(); 1215e33a1deSShawn O. Pearce 1225e33a1deSShawn O. Pearce String ifNoneMatch = req.getHeader(HDR_IF_NONE_MATCH); 1235e33a1deSShawn O. Pearce if (etag != null && etag.equals(ifNoneMatch)) { 1245e33a1deSShawn O. Pearce rsp.sendError(SC_NOT_MODIFIED); 1255e33a1deSShawn O. Pearce return; 1265e33a1deSShawn O. Pearce } 1275e33a1deSShawn O. Pearce 1285e33a1deSShawn O. Pearce long ifModifiedSince = req.getDateHeader(HDR_IF_MODIFIED_SINCE); 1295e33a1deSShawn O. Pearce if (0 < lastModified && lastModified < ifModifiedSince) { 1305e33a1deSShawn O. Pearce rsp.sendError(SC_NOT_MODIFIED); 1315e33a1deSShawn O. Pearce return; 1325e33a1deSShawn O. Pearce } 1335e33a1deSShawn O. Pearce 1345e33a1deSShawn O. Pearce if (etag != null) 1355e33a1deSShawn O. Pearce rsp.setHeader(HDR_ETAG, etag); 1365e33a1deSShawn O. Pearce if (0 < lastModified) 1375e33a1deSShawn O. Pearce rsp.setDateHeader(HDR_LAST_MODIFIED, lastModified); 1385e33a1deSShawn O. Pearce rsp.setContentType(contentType); 1395e33a1deSShawn O. Pearce sender.serve(req, rsp, sendBody); 1405e33a1deSShawn O. Pearce } finally { 1415e33a1deSShawn O. Pearce sender.close(); 1425e33a1deSShawn O. Pearce } 1435e33a1deSShawn O. Pearce } 1445e33a1deSShawn O. Pearce objects(HttpServletRequest req)1456d370d83SHan-Wen Nienhuys private static File objects(HttpServletRequest req) { 1465e33a1deSShawn O. Pearce final Repository db = getRepository(req); 1475e33a1deSShawn O. Pearce return ((ObjectDirectory) db.getObjectDatabase()).getDirectory(); 1485e33a1deSShawn O. Pearce } 1495e33a1deSShawn O. Pearce } 150