/******************************************************************************* * Copyright (c) 2010, 2014 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.orion.internal.server.servlets.file; import java.io.IOException; import java.security.NoSuchAlgorithmException; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.runtime.CoreException; import org.eclipse.orion.internal.server.servlets.ServletResourceHandler; import org.eclipse.orion.server.core.HashUtilities; import org.eclipse.orion.server.core.IOUtilities; import org.eclipse.orion.server.core.ProtocolConstants; import org.eclipse.osgi.util.NLS; /** * Serializes IFileStore responses in a format suitable for a generic client, * such as a web browser. */ class GenericFileHandler extends ServletResourceHandler<IFileStore> { private final ServletContext context; public GenericFileHandler(ServletContext context) { this.context = context; } protected boolean handleFileContents(HttpServletRequest request, HttpServletResponse response, IFileStore file) throws CoreException, IOException, NoSuchAlgorithmException { switch (getMethod(request)) { case GET : setCacheHeaders(response, file); response.setHeader(ProtocolConstants.HEADER_CONTENT_TYPE, context.getMimeType(file.getName())); response.setHeader(ProtocolConstants.HEADER_ACCEPT_PATCH, ProtocolConstants.CONTENT_TYPE_JSON_PATCH); IOUtilities.pipe(file.openInputStream(EFS.NONE, null), response.getOutputStream(), true, false); break; case PUT : setCacheHeaders(response, file); IOUtilities.pipe(request.getInputStream(), file.openOutputStream(EFS.NONE, null), false, true); break; default : return false; } return true; } private void setCacheHeaders(HttpServletResponse response, IFileStore file) throws NoSuchAlgorithmException, IOException, CoreException { response.setHeader("Cache-Control", "no-cache"); //$NON-NLS-1$ //$NON-NLS-2$ response.setHeader(ProtocolConstants.KEY_ETAG, generateFileETag(file)); } @Override public boolean handleRequest(HttpServletRequest request, HttpServletResponse response, IFileStore file) throws ServletException { // generic variant doesn't handle queries // acceptable to just ignore query parameters we do not understand // if (request.getQueryString() != null) // return false; try { String fileETag = generateFileETag(file); if (handleIfMatchHeader(request, response, fileETag)) { return true; } if (handleIfNoneMatchHeader(request, response, fileETag)) { return true; } return handleFileContents(request, response, file); } catch (Exception e) { if (!handleAuthFailure(request, response, e)) throw new ServletException(NLS.bind("Error retrieving file: {0}", file), e); } return true; } /** * Returns an ETag calculated using SHA-1 hash function. */ public static String generateFileETag(IFileStore file) throws NoSuchAlgorithmException, IOException, CoreException { //give empty ETag if file does not exist if (!file.fetchInfo().exists()) return ""; //$NON-NLS-1$ return HashUtilities.getHash(file.openInputStream(EFS.NONE, null), true, HashUtilities.SHA_1); } /** * Handles If-Match header precondition * * @param request The HTTP request object * @param response The servlet response object * @param etag The file's ETag * @return {@code true} if the If-Match header precondition failed (doesn't match the file's ETag), {@code false} otherwise */ protected boolean handleIfMatchHeader(HttpServletRequest request, HttpServletResponse response, String etag) { String ifMatchHeader = request.getHeader(ProtocolConstants.HEADER_IF_MATCH); if (ifMatchHeader != null && !ifMatchHeader.equals(etag)) { response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED); return true; } return false; } /** * Handles If-None-Match header precondition * * @param request The HTTP request object * @param response The servlet response object * @param etag The file's ETag * @return {@code true} if the If-None-Match header precondition failed (matches the file's ETag), {@code false} otherwise */ protected boolean handleIfNoneMatchHeader(HttpServletRequest request, HttpServletResponse response, String etag) { String ifNoneMatchHeader = request.getHeader(ProtocolConstants.HEADER_IF_NONE_MATCH); if (ifNoneMatchHeader != null && ifNoneMatchHeader.equals(etag)) { switch (getMethod(request)) { case HEAD : // fall through case GET : response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); break; case DELETE : //see Bug 450014 return false; default : response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED); break; } response.setHeader("Cache-Control", "no-cache"); //$NON-NLS-1$ //$NON-NLS-2$ response.setHeader(ProtocolConstants.KEY_ETAG, etag); return true; } return false; } }