xref: /JGit/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ObjectFileServlet.java (revision 25a6bd4d614589c968090fb506fc9b26d5c82fe2)
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