private int handleGet(Request baseRequest, HttpServletResponse response) throws IOException { if (!artifactCache.isPresent()) { response.getWriter().write("Serving local cache is disabled for this instance."); return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; } String path = baseRequest.getUri().getPath(); String[] pathElements = path.split("/"); if (pathElements.length != 4 || !pathElements[2].equals("key")) { response.getWriter().write("Incorrect url format."); return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; } RuleKey ruleKey = RuleKey.TO_RULE_KEY.apply(pathElements[3]); Path temp = null; try { temp = projectFilesystem.createTempFile(BuckConstant.SCRATCH_PATH, "outgoing_rulekey", ".tmp"); projectFilesystem.createParentDirs(temp); CacheResult fetchResult; try { fetchResult = artifactCache.get().fetch(ruleKey, temp); } catch (InterruptedException e) { LOG.error(e, "Interrupted when fetching from local cache."); e.printStackTrace(response.getWriter()); return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; } if (!fetchResult.getType().isSuccess()) { return HttpServletResponse.SC_NOT_FOUND; } final Path tempFinal = temp; HttpArtifactCacheBinaryProtocol.FetchResponse fetchResponse = new HttpArtifactCacheBinaryProtocol.FetchResponse( ImmutableSet.of(ruleKey), fetchResult.getMetadata(), new ByteSource() { @Override public InputStream openStream() throws IOException { return projectFilesystem.newFileInputStream(tempFinal); } }); fetchResponse.write(response.getOutputStream()); response.setContentLengthLong(fetchResponse.getContentLength()); return HttpServletResponse.SC_OK; } finally { if (temp != null) { projectFilesystem.deleteFileAtPathIfExists(temp); } } }
/** * Serve the specified resource, optionally including the data content. * * @param request The servlet request we are processing * @param response The servlet response we are creating * @param content Should the content be included? * @param encoding The encoding to use if it is necessary to access the source as characters * rather than as bytes * @exception IOException if an input/output error occurs * @exception ServletException if a servlet-specified error occurs */ protected void serveResource( HttpServletRequest request, HttpServletResponse response, boolean content, String encoding) throws IOException, ServletException { boolean serveContent = content; boolean debug = log.isDebug(); // Identify the requested resource path String path = getRelativePath(request); if (debug) { if (serveContent) log.debug( "DefaultServlet.serveResource: Serving resource '" + path + "' headers and data"); else log.debug("DefaultServlet.serveResource: Serving resource '" + path + "' headers only"); } WebResourceRoot resources = getResources(request); WebResource resource = resources.getResource(path); if (!resource.exists()) { // Check if we're included so we can return the appropriate // missing resource name in the error String requestUri = (String) request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI); if (requestUri == null) { requestUri = request.getRequestURI(); } else { // We're included // SRV.9.3 says we must throw a FNFE throw new FileNotFoundException("defaultServlet.missingResource"); } response.sendError(HttpServletResponse.SC_NOT_FOUND, requestUri); return; } // If the resource is not a collection, and the resource path // ends with "/" or "\", return NOT FOUND if (resource.isFile() && path.endsWith("/") || path.endsWith("\\")) { // Check if we're included so we can return the appropriate // missing resource name in the error String requestUri = (String) request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI); if (requestUri == null) { requestUri = request.getRequestURI(); } response.sendError(HttpServletResponse.SC_NOT_FOUND, requestUri); return; } boolean isError = response.getStatus() >= HttpServletResponse.SC_BAD_REQUEST; boolean included = false; // Check if the conditions specified in the optional If headers are // satisfied. if (resource.isFile()) { // Checking If headers included = (request.getAttribute(RequestDispatcher.INCLUDE_CONTEXT_PATH) != null); if (!included && !isError && !checkIfHeaders(request, response, resource)) { return; } } // Find content type. String contentType = resource.getMimeType(); if (contentType == null) { contentType = request.getServletContext().getMimeType(resource.getName()); resource.setMimeType(contentType); } // These need to reflect the original resource, not the potentially // gzip'd version of the resource so get them now if they are going to // be needed later String eTag = null; String lastModifiedHttp = null; if (resource.isFile() && !isError) { eTag = resource.getETag(); lastModifiedHttp = resource.getLastModifiedHttp(); } // Serve a gzipped version of the file if present boolean usingGzippedVersion = false; if (gzip && !included && resource.isFile() && !path.endsWith(".gz")) { WebResource gzipResource = resources.getResource(path + ".gz"); if (gzipResource.exists() && gzipResource.isFile()) { Collection<String> varyHeaders = response.getHeaders("Vary"); boolean addRequired = true; for (String varyHeader : varyHeaders) { if ("*".equals(varyHeader) || "accept-encoding".equalsIgnoreCase(varyHeader)) { addRequired = false; break; } } if (addRequired) { response.addHeader("Vary", "accept-encoding"); } if (checkIfGzip(request)) { response.addHeader("Content-Encoding", "gzip"); resource = gzipResource; usingGzippedVersion = true; } } } ArrayList<Range> ranges = null; long contentLength = -1L; if (resource.isDirectory()) { contentType = "text/html;charset=UTF-8"; } else { if (!isError) { if (useAcceptRanges) { // Accept ranges header response.setHeader("Accept-Ranges", "bytes"); } // Parse range specifier ranges = parseRange(request, response, resource); // ETag header response.setHeader("ETag", eTag); // Last-Modified header response.setHeader("Last-Modified", lastModifiedHttp); } // Get content length contentLength = resource.getContentLength(); // Special case for zero length files, which would cause a // (silent) ISE when setting the output buffer size if (contentLength == 0L) { serveContent = false; } } ServletOutputStream ostream = null; PrintWriter writer = null; if (serveContent) { // Trying to retrieve the servlet output stream try { ostream = response.getOutputStream(); } catch (IllegalStateException e) { // If it fails, we try to get a Writer instead if we're // trying to serve a text file if (!usingGzippedVersion && ((contentType == null) || (contentType.startsWith("text")) || (contentType.endsWith("xml")) || (contentType.contains("/javascript")))) { writer = response.getWriter(); // Cannot reliably serve partial content with a Writer ranges = FULL; } else { throw e; } } } // Check to see if a Filter, Valve of wrapper has written some content. // If it has, disable range requests and setting of a content length // since neither can be done reliably. ServletResponse r = response; long contentWritten = 0; while (r instanceof ServletResponseWrapper) { r = ((ServletResponseWrapper) r).getResponse(); } if (contentWritten > 0) { ranges = FULL; } if (resource.isDirectory() || isError || ((ranges == null || ranges.isEmpty()) && request.getHeader("Range") == null) || ranges == FULL) { // Set the appropriate output headers if (contentType != null) { if (debug) log.debug("DefaultServlet.serveFile: contentType='" + contentType + "'"); response.setContentType(contentType); } if (resource.isFile() && contentLength >= 0 && (!serveContent || ostream != null)) { if (debug) log.debug("DefaultServlet.serveFile: contentLength=" + contentLength); // Don't set a content length if something else has already // written to the response. if (contentWritten == 0) { response.setContentLengthLong(contentLength); } } InputStream renderResult = null; if (resource.isDirectory()) { if (serveContent) { // Serve the directory browser renderResult = null; // TODO tomcat render(getPathPrefix(request), resource); } } // Copy the input stream to our output stream (if requested) if (serveContent) { resource.increaseDownloadCount(); try { response.setBufferSize(output); } catch (IllegalStateException e) { // Silent catch } if (ostream == null) { // Output via a writer so can't use sendfile or write // content directly. if (resource.isDirectory()) { renderResult = null; // render(getPathPrefix(request), resource); } else { renderResult = resource.getInputStream(); } copy(resource, renderResult, writer, encoding); } else { // Output is via an InputStream if (resource.isDirectory()) { renderResult = null; // render(getPathPrefix(request), resource); } else { renderResult = resource.getInputStream(); } // If a stream was configured, it needs to be copied to // the output (this method closes the stream) if (renderResult != null) { copy(renderResult, ostream); } } } } else { // download counter resource.increaseDownloadCount(); if ((ranges == null) || (ranges.isEmpty())) return; // Partial content response. response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); if (ranges.size() == 1) { Range range = ranges.get(0); response.addHeader( "Content-Range", "bytes " + range.start + "-" + range.end + "/" + range.length); long length = range.end - range.start + 1; response.setContentLengthLong(length); if (contentType != null) { if (debug) log.debug("DefaultServlet.serveFile: contentType='" + contentType + "'"); response.setContentType(contentType); } if (serveContent) { try { response.setBufferSize(output); } catch (IllegalStateException e) { // Silent catch } if (ostream != null) { copy(resource, ostream, range); } else { // we should not get here throw new IllegalStateException(); } } } else { response.setContentType("multipart/byteranges; boundary=" + mimeSeparation); if (serveContent) { try { response.setBufferSize(output); } catch (IllegalStateException e) { // Silent catch } if (ostream != null) { copy(resource, ostream, ranges.iterator(), contentType); } else { // we should not get here throw new IllegalStateException(); } } } } }