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);
      }
    }
  }
Example #2
0
  /**
   * 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();
          }
        }
      }
    }
  }