public static DefaultFullHttpResponse directView()

in core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/DirectoryBrowser.java [150:254]


    public static DefaultFullHttpResponse directView(File dir, String path, FullHttpRequest request, ChannelHandlerContext ctx) throws IOException {
        if (path.startsWith("/")) {
            path = path.substring(1);
        }

        // path maybe: arthas-output/20201225-203454.svg 
        // 需要取 dir的parent来去掉前缀
        File file = new File(dir.getParent(), path);
        HttpVersion version = request.protocolVersion();
        if (isSubFile(dir, file)) {
            DefaultFullHttpResponse fullResp = new DefaultFullHttpResponse(version, HttpResponseStatus.OK);

            if (file.isDirectory()) {
                if (!path.endsWith("/")) {
                    fullResp.setStatus(HttpResponseStatus.FOUND).headers().set(HttpHeaderNames.LOCATION, "/" + path + "/");
                }
                String renderResult = renderDir(file, !isSameFile(dir, file));
                fullResp.content().writeBytes(renderResult.getBytes("utf-8"));
                fullResp.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=utf-8");
                ctx.write(fullResp);
                ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
                future.addListener(ChannelFutureListener.CLOSE);
                return fullResp;
            } else {
                logger.info("get file now. file:" + file.getPath());
                if (file.isHidden() || !file.exists() || file.isDirectory() || !file.isFile()) {
                    return null;
                }

                long fileLength = file.length();
                if (fileLength < MIN_NETTY_DIRECT_SEND_SIZE){
                    FileInputStream fileInputStream = new FileInputStream(file);
                    try {
                        byte[] content = IOUtils.getBytes(fileInputStream);
                        fullResp.content().writeBytes(content);
                        HttpUtil.setContentLength(fullResp, fullResp.content().readableBytes());
                    } finally {
                        IOUtils.close(fileInputStream);
                    }
                    ChannelFuture channelFuture = ctx.writeAndFlush(fullResp);
                    channelFuture.addListener((ChannelFutureListener) future -> {
                        if (future.isSuccess()) {
                            ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
                            lastContentFuture.addListener(ChannelFutureListener.CLOSE);
                        } else {
                            future.channel().close();
                        }
                    });
                    return fullResp;
                }
                logger.info("file {} size bigger than {}, send by future.",file.getName(), MIN_NETTY_DIRECT_SEND_SIZE);
                HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
                HttpUtil.setContentLength(response, fileLength);
                setContentTypeHeader(response, file);
                setDateAndCacheHeaders(response, file);
                if (HttpUtil.isKeepAlive(request)) {
                    response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
                }

                // Write the initial line and the header.
                ctx.write(response);
                // Write the content.
                ChannelFuture sendFileFuture;
                ChannelFuture lastContentFuture;
                RandomAccessFile raf = new RandomAccessFile(file, "r"); // will closed by netty
                if (ctx.pipeline().get(SslHandler.class) == null) {
                    sendFileFuture =
                            ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise());
                    // Write the end marker.
                    lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
                } else {
                    sendFileFuture =
                            ctx.writeAndFlush(new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192)),
                                    ctx.newProgressivePromise());
                    // HttpChunkedInput will write the end marker (LastHttpContent) for us.
                    lastContentFuture = sendFileFuture;
                }

                sendFileFuture.addListener(new ChannelProgressiveFutureListener() {
                    @Override
                    public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) {
                        if (total < 0) { // total unknown
                            logger.info(future.channel() + " Transfer progress: " + progress);
                        } else {
                            logger.info(future.channel() + " Transfer progress: " + progress + " / " + total);
                        }
                    }

                    @Override
                    public void operationComplete(ChannelProgressiveFuture future) {
                        logger.info(future.channel() + " Transfer complete.");
                    }
                });

                // Decide whether to close the connection or not.
                if (!HttpUtil.isKeepAlive(request)) {
                    // Close the connection when the whole content is written out.
                    lastContentFuture.addListener(ChannelFutureListener.CLOSE);
                }
                return fullResp;
            }
        }

        return null;
    }