private void serveFile()

in core/src/main/java/hudson/model/DirectoryBrowserSupport.java [187:373]


    private void serveFile(StaplerRequest req, StaplerResponse rsp, VirtualFile root, String icon, boolean serveDirIndex) throws IOException, ServletException, InterruptedException {
        // handle form submission
        String pattern = req.getParameter("pattern");
        if(pattern==null)
            pattern = req.getParameter("path"); // compatibility with Hudson<1.129
        if(pattern!=null && Util.isSafeToRedirectTo(pattern)) {// avoid open redirect
            rsp.sendRedirect2(pattern);
            return;
        }

        String path = getPath(req);
        if(path.replace('\\', '/').contains("/../")) {
            // don't serve anything other than files in the artifacts dir
            rsp.sendError(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }

        // split the path to the base directory portion "abc/def/ghi" which doesn't include any wildcard,
        // and the GLOB portion "**/*.xml" (the rest)
        StringBuilder _base = new StringBuilder();
        StringBuilder _rest = new StringBuilder();
        int restSize=-1; // number of ".." needed to go back to the 'base' level.
        boolean zip=false;  // if we are asked to serve a zip file bundle
        boolean plain = false; // if asked to serve a plain text directory listing
        {
            boolean inBase = true;
            StringTokenizer pathTokens = new StringTokenizer(path,"/");
            while(pathTokens.hasMoreTokens()) {
                String pathElement = pathTokens.nextToken();
                // Treat * and ? as wildcard unless they match a literal filename
                if((pathElement.contains("?") || pathElement.contains("*"))
                        && inBase && !root.child((_base.length() > 0 ? _base + "/" : "") + pathElement).exists())
                    inBase = false;
                if(pathElement.equals("*zip*")) {
                    // the expected syntax is foo/bar/*zip*/bar.zip
                    // the last 'bar.zip' portion is to causes browses to set a good default file name.
                    // so the 'rest' portion ends here.
                    zip=true;
                    break;
                }
                if(pathElement.equals("*plain*")) {
                    plain = true;
                    break;
                }

                StringBuilder sb = inBase?_base:_rest;
                if(sb.length()>0)   sb.append('/');
                sb.append(pathElement);
                if(!inBase)
                    restSize++;
            }
        }
        restSize = Math.max(restSize,0);
        String base = _base.toString();
        String rest = _rest.toString();

        if(!ALLOW_SYMLINK_ESCAPE && (root.supportIsDescendant() && !root.isDescendant(base))){
            LOGGER.log(Level.WARNING, "Trying to access a file outside of the directory, target: "+ base);
            rsp.sendError(HttpServletResponse.SC_FORBIDDEN, "Trying to access a file outside of the directory, target: " + base);
            return;
        }

        // this is the base file/directory
        VirtualFile baseFile = base.isEmpty() ? root : root.child(base);

        if(baseFile.isDirectory()) {
            if(zip) {
                rsp.setContentType("application/zip");
                zip(rsp, root, baseFile, rest);
                return;
            }
            if (plain) {
                rsp.setContentType("text/plain;charset=UTF-8");
                try (OutputStream os = rsp.getOutputStream()) {
                    for (VirtualFile kid : baseFile.list()) {
                        os.write(kid.getName().getBytes(StandardCharsets.UTF_8));
                        if (kid.isDirectory()) {
                            os.write('/');
                        }
                        os.write('\n');
                    }
                    os.flush();
                }
                return;
            }

            if(rest.length()==0) {
                // if the target page to be displayed is a directory and the path doesn't end with '/', redirect
                StringBuffer reqUrl = req.getRequestURL();
                if(reqUrl.charAt(reqUrl.length()-1)!='/') {
                    rsp.sendRedirect2(reqUrl.append('/').toString());
                    return;
                }
            }

            List<List<Path>> glob = null;
            boolean patternUsed = rest.length() > 0;
            if(patternUsed) {
                // the rest is Ant glob pattern
                glob = patternScan(baseFile, rest, createBackRef(restSize));
            } else
            if(serveDirIndex) {
                // serve directory index
                glob = baseFile.run(new BuildChildPaths(baseFile, req.getLocale()));
            }

            if(glob!=null) {
                List<List<Path>> filteredGlob = keepReadabilityOnlyOnDescendants(baseFile, patternUsed, glob);
                
                // serve glob
                req.setAttribute("it", this);
                List<Path> parentPaths = buildParentPath(base,restSize);
                req.setAttribute("parentPath",parentPaths);
                req.setAttribute("backPath", createBackRef(restSize));
                req.setAttribute("topPath", createBackRef(parentPaths.size()+restSize));
                req.setAttribute("files", filteredGlob);
                req.setAttribute("icon", icon);
                req.setAttribute("path", path);
                req.setAttribute("pattern",rest);
                req.setAttribute("dir", baseFile);
                if (ResourceDomainConfiguration.isResourceRequest(req)) {
                    req.getView(this, "plaindir.jelly").forward(req, rsp);
                } else {
                    req.getView(this, "dir.jelly").forward(req, rsp);
                }
                return;
            }

            // convert a directory service request to a single file service request by serving
            // 'index.html'
            baseFile = baseFile.child(indexFileName);
        }

        //serve a single file
        if(!baseFile.exists()) {
            rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        boolean view = rest.equals("*view*");

        if(rest.equals("*fingerprint*")) {
            try (InputStream fingerprintInput = baseFile.open()) {
                rsp.forward(Jenkins.get().getFingerprint(Util.getDigestOf(fingerprintInput)), "/", req);
            }
            return;
        }

        URL external = baseFile.toExternalURL();
        if (external != null) {
            // or this URL could be emitted directly from dir.jelly
            // though we would prefer to delay toExternalURL calls unless and until needed
            rsp.sendRedirect2(external.toExternalForm());
            return;
        }

        long lastModified = baseFile.lastModified();
        long length = baseFile.length();

        if(LOGGER.isLoggable(Level.FINE))
            LOGGER.fine("Serving "+baseFile+" with lastModified=" + lastModified + ", length=" + length);

        if (view) {
            // for binary files, provide the file name for download
            rsp.setHeader("Content-Disposition", "inline; filename=" + baseFile.getName());

            // pseudo file name to let the Stapler set text/plain
            rsp.serveFile(req, baseFile.open(), lastModified, -1, length, "plain.txt");
        } else {
            if (resourceToken != null) {
                // redirect to second domain
                rsp.sendRedirect(302, ResourceDomainRootAction.get().getRedirectUrl(resourceToken, req.getRestOfPath()));
            } else {
                if (!ResourceDomainConfiguration.isResourceRequest(req)) {
                    // if we're serving this from the main domain, set CSP headers
                    String csp = SystemProperties.getString(CSP_PROPERTY_NAME, DEFAULT_CSP_VALUE);
                    if (!csp.trim().equals("")) {
                        // allow users to prevent sending this header by setting empty system property
                        for (String header : new String[]{"Content-Security-Policy", "X-WebKit-CSP", "X-Content-Security-Policy"}) {
                            rsp.setHeader(header, csp);
                        }
                    }
                }
                rsp.serveFile(req, baseFile.open(), lastModified, -1, length, baseFile.getName());
            }
        }
    }