@Test public void testRespondWithFile() throws Exception { File webDir = tmp.newFolder("webDir"); Router router = new Router() .addGet("/:*", new HistoryServerStaticFileServerHandler(webDir)); WebFrontendBootstrap webUI = new WebFrontendBootstrap( router, LoggerFactory.getLogger(HistoryServerStaticFileServerHandlerTest.class), tmp.newFolder("uploadDir"), null, "localhost", 0, new Configuration()); int port = webUI.getServerPort(); try { String notFound404 = HistoryServerTest.getFromHTTP("http: Assert.assertThat(notFound404, containsString("not found")); String index = HistoryServerTest.getFromHTTP("http: Assert.assertTrue(index.contains("Apache Flink Web Dashboard")); String index2 = HistoryServerTest.getFromHTTP("http: Assert.assertEquals(index, index2); File dir = new File(webDir, "dir.json"); dir.mkdirs(); String dirNotFound404 = HistoryServerTest.getFromHTTP("http: Assert.assertTrue(dirNotFound404.contains("not found")); tmp.newFile("secret"); String x = HistoryServerTest.getFromHTTP("http: Assert.assertTrue(x.contains("not found")); } finally { webUI.shutdown(); } }
private void respondWithFile(ChannelHandlerContext ctx, HttpRequest request, String requestPath) throws IOException, ParseException { if (requestPath.endsWith("/")) { requestPath = requestPath + "index.html"; } if (!requestPath.contains(".")) { requestPath = requestPath + ".json"; } final File file = new File(rootPath, requestPath); if (!file.exists()) { ClassLoader cl = HistoryServerStaticFileServerHandler.class.getClassLoader(); try (InputStream resourceStream = cl.getResourceAsStream("web" + requestPath)) { boolean success = false; try { if (resourceStream != null) { URL root = cl.getResource("web"); URL requested = cl.getResource("web" + requestPath); if (root != null && requested != null) { URI rootURI = new URI(root.getPath()).normalize(); URI requestedURI = new URI(requested.getPath()).normalize(); if (!rootURI.relativize(requestedURI).equals(requestedURI)) { LOG.debug("Loading missing file from classloader: {}", requestPath); file.getParentFile().mkdirs(); Files.copy(resourceStream, file.toPath()); success = true; } } } } catch (Throwable t) { LOG.error("error while responding", t); } finally { if (!success) { LOG.debug("Unable to load requested file {} from classloader", requestPath); HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } } } } if (!file.exists() || file.isHidden() || file.isDirectory() || !file.isFile()) { HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } if (!file.getCanonicalFile().toPath().startsWith(rootPath.toPath())) { HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } final String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE); if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) { SimpleDateFormat dateFormatter = new SimpleDateFormat(StaticFileServerHandler.HTTP_DATE_FORMAT, Locale.US); Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince); long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000; long fileLastModifiedSeconds = file.lastModified() / 1000; if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) { if (LOG.isDebugEnabled()) { LOG.debug("Responding 'NOT MODIFIED' for file '" + file.getAbsolutePath() + '\''); } StaticFileServerHandler.sendNotModified(ctx); return; } } if (LOG.isDebugEnabled()) { LOG.debug("Responding with file '" + file.getAbsolutePath() + '\''); } final RandomAccessFile raf; try { raf = new RandomAccessFile(file, "r"); } catch (FileNotFoundException e) { HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } try { long fileLength = raf.length(); HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); StaticFileServerHandler.setContentTypeHeader(response, file); if (!requestPath.equals("/joboverview.json")) { StaticFileServerHandler.setDateAndCacheHeaders(response, file); } if (HttpHeaders.isKeepAlive(request)) { response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); } HttpHeaders.setContentLength(response, fileLength); ctx.write(response); ChannelFuture lastContentFuture; if (ctx.pipeline().get(SslHandler.class) == null) { ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise()); lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); } else { lastContentFuture = ctx.writeAndFlush(new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192)), ctx.newProgressivePromise()); } if (!HttpHeaders.isKeepAlive(request)) { lastContentFuture.addListener(ChannelFutureListener.CLOSE); } } catch (Exception e) { raf.close(); LOG.error("Failed to serve file.", e); HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("Internal server error."), INTERNAL_SERVER_ERROR, Collections.emptyMap()); } }
HistoryServerStaticFileServerHandler extends SimpleChannelInboundHandler<RoutedRequest> { private void respondWithFile(ChannelHandlerContext ctx, HttpRequest request, String requestPath) throws IOException, ParseException { if (requestPath.endsWith("/")) { requestPath = requestPath + "index.html"; } if (!requestPath.contains(".")) { requestPath = requestPath + ".json"; } final File file = new File(rootPath, requestPath); if (!file.exists()) { ClassLoader cl = HistoryServerStaticFileServerHandler.class.getClassLoader(); try (InputStream resourceStream = cl.getResourceAsStream("web" + requestPath)) { boolean success = false; try { if (resourceStream != null) { URL root = cl.getResource("web"); URL requested = cl.getResource("web" + requestPath); if (root != null && requested != null) { URI rootURI = new URI(root.getPath()).normalize(); URI requestedURI = new URI(requested.getPath()).normalize(); if (!rootURI.relativize(requestedURI).equals(requestedURI)) { LOG.debug("Loading missing file from classloader: {}", requestPath); file.getParentFile().mkdirs(); Files.copy(resourceStream, file.toPath()); success = true; } } } } catch (Throwable t) { LOG.error("error while responding", t); } finally { if (!success) { LOG.debug("Unable to load requested file {} from classloader", requestPath); HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } } } } if (!file.exists() || file.isHidden() || file.isDirectory() || !file.isFile()) { HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } if (!file.getCanonicalFile().toPath().startsWith(rootPath.toPath())) { HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } final String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE); if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) { SimpleDateFormat dateFormatter = new SimpleDateFormat(StaticFileServerHandler.HTTP_DATE_FORMAT, Locale.US); Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince); long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000; long fileLastModifiedSeconds = file.lastModified() / 1000; if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) { if (LOG.isDebugEnabled()) { LOG.debug("Responding 'NOT MODIFIED' for file '" + file.getAbsolutePath() + '\''); } StaticFileServerHandler.sendNotModified(ctx); return; } } if (LOG.isDebugEnabled()) { LOG.debug("Responding with file '" + file.getAbsolutePath() + '\''); } final RandomAccessFile raf; try { raf = new RandomAccessFile(file, "r"); } catch (FileNotFoundException e) { HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } try { long fileLength = raf.length(); HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); StaticFileServerHandler.setContentTypeHeader(response, file); if (!requestPath.equals("/joboverview.json")) { StaticFileServerHandler.setDateAndCacheHeaders(response, file); } if (HttpHeaders.isKeepAlive(request)) { response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); } HttpHeaders.setContentLength(response, fileLength); ctx.write(response); ChannelFuture lastContentFuture; if (ctx.pipeline().get(SslHandler.class) == null) { ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise()); lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); } else { lastContentFuture = ctx.writeAndFlush(new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192)), ctx.newProgressivePromise()); } if (!HttpHeaders.isKeepAlive(request)) { lastContentFuture.addListener(ChannelFutureListener.CLOSE); } } catch (Exception e) { raf.close(); LOG.error("Failed to serve file.", e); HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("Internal server error."), INTERNAL_SERVER_ERROR, Collections.emptyMap()); } } }
HistoryServerStaticFileServerHandler extends SimpleChannelInboundHandler<RoutedRequest> { private void respondWithFile(ChannelHandlerContext ctx, HttpRequest request, String requestPath) throws IOException, ParseException { if (requestPath.endsWith("/")) { requestPath = requestPath + "index.html"; } if (!requestPath.contains(".")) { requestPath = requestPath + ".json"; } final File file = new File(rootPath, requestPath); if (!file.exists()) { ClassLoader cl = HistoryServerStaticFileServerHandler.class.getClassLoader(); try (InputStream resourceStream = cl.getResourceAsStream("web" + requestPath)) { boolean success = false; try { if (resourceStream != null) { URL root = cl.getResource("web"); URL requested = cl.getResource("web" + requestPath); if (root != null && requested != null) { URI rootURI = new URI(root.getPath()).normalize(); URI requestedURI = new URI(requested.getPath()).normalize(); if (!rootURI.relativize(requestedURI).equals(requestedURI)) { LOG.debug("Loading missing file from classloader: {}", requestPath); file.getParentFile().mkdirs(); Files.copy(resourceStream, file.toPath()); success = true; } } } } catch (Throwable t) { LOG.error("error while responding", t); } finally { if (!success) { LOG.debug("Unable to load requested file {} from classloader", requestPath); HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } } } } if (!file.exists() || file.isHidden() || file.isDirectory() || !file.isFile()) { HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } if (!file.getCanonicalFile().toPath().startsWith(rootPath.toPath())) { HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } final String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE); if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) { SimpleDateFormat dateFormatter = new SimpleDateFormat(StaticFileServerHandler.HTTP_DATE_FORMAT, Locale.US); Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince); long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000; long fileLastModifiedSeconds = file.lastModified() / 1000; if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) { if (LOG.isDebugEnabled()) { LOG.debug("Responding 'NOT MODIFIED' for file '" + file.getAbsolutePath() + '\''); } StaticFileServerHandler.sendNotModified(ctx); return; } } if (LOG.isDebugEnabled()) { LOG.debug("Responding with file '" + file.getAbsolutePath() + '\''); } final RandomAccessFile raf; try { raf = new RandomAccessFile(file, "r"); } catch (FileNotFoundException e) { HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } try { long fileLength = raf.length(); HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); StaticFileServerHandler.setContentTypeHeader(response, file); if (!requestPath.equals("/joboverview.json")) { StaticFileServerHandler.setDateAndCacheHeaders(response, file); } if (HttpHeaders.isKeepAlive(request)) { response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); } HttpHeaders.setContentLength(response, fileLength); ctx.write(response); ChannelFuture lastContentFuture; if (ctx.pipeline().get(SslHandler.class) == null) { ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise()); lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); } else { lastContentFuture = ctx.writeAndFlush(new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192)), ctx.newProgressivePromise()); } if (!HttpHeaders.isKeepAlive(request)) { lastContentFuture.addListener(ChannelFutureListener.CLOSE); } } catch (Exception e) { raf.close(); LOG.error("Failed to serve file.", e); HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("Internal server error."), INTERNAL_SERVER_ERROR, Collections.emptyMap()); } } HistoryServerStaticFileServerHandler(File rootPath); }
HistoryServerStaticFileServerHandler extends SimpleChannelInboundHandler<RoutedRequest> { private void respondWithFile(ChannelHandlerContext ctx, HttpRequest request, String requestPath) throws IOException, ParseException { if (requestPath.endsWith("/")) { requestPath = requestPath + "index.html"; } if (!requestPath.contains(".")) { requestPath = requestPath + ".json"; } final File file = new File(rootPath, requestPath); if (!file.exists()) { ClassLoader cl = HistoryServerStaticFileServerHandler.class.getClassLoader(); try (InputStream resourceStream = cl.getResourceAsStream("web" + requestPath)) { boolean success = false; try { if (resourceStream != null) { URL root = cl.getResource("web"); URL requested = cl.getResource("web" + requestPath); if (root != null && requested != null) { URI rootURI = new URI(root.getPath()).normalize(); URI requestedURI = new URI(requested.getPath()).normalize(); if (!rootURI.relativize(requestedURI).equals(requestedURI)) { LOG.debug("Loading missing file from classloader: {}", requestPath); file.getParentFile().mkdirs(); Files.copy(resourceStream, file.toPath()); success = true; } } } } catch (Throwable t) { LOG.error("error while responding", t); } finally { if (!success) { LOG.debug("Unable to load requested file {} from classloader", requestPath); HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } } } } if (!file.exists() || file.isHidden() || file.isDirectory() || !file.isFile()) { HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } if (!file.getCanonicalFile().toPath().startsWith(rootPath.toPath())) { HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } final String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE); if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) { SimpleDateFormat dateFormatter = new SimpleDateFormat(StaticFileServerHandler.HTTP_DATE_FORMAT, Locale.US); Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince); long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000; long fileLastModifiedSeconds = file.lastModified() / 1000; if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) { if (LOG.isDebugEnabled()) { LOG.debug("Responding 'NOT MODIFIED' for file '" + file.getAbsolutePath() + '\''); } StaticFileServerHandler.sendNotModified(ctx); return; } } if (LOG.isDebugEnabled()) { LOG.debug("Responding with file '" + file.getAbsolutePath() + '\''); } final RandomAccessFile raf; try { raf = new RandomAccessFile(file, "r"); } catch (FileNotFoundException e) { HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } try { long fileLength = raf.length(); HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); StaticFileServerHandler.setContentTypeHeader(response, file); if (!requestPath.equals("/joboverview.json")) { StaticFileServerHandler.setDateAndCacheHeaders(response, file); } if (HttpHeaders.isKeepAlive(request)) { response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); } HttpHeaders.setContentLength(response, fileLength); ctx.write(response); ChannelFuture lastContentFuture; if (ctx.pipeline().get(SslHandler.class) == null) { ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise()); lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); } else { lastContentFuture = ctx.writeAndFlush(new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192)), ctx.newProgressivePromise()); } if (!HttpHeaders.isKeepAlive(request)) { lastContentFuture.addListener(ChannelFutureListener.CLOSE); } } catch (Exception e) { raf.close(); LOG.error("Failed to serve file.", e); HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("Internal server error."), INTERNAL_SERVER_ERROR, Collections.emptyMap()); } } HistoryServerStaticFileServerHandler(File rootPath); @Override void channelRead0(ChannelHandlerContext ctx, RoutedRequest routedRequest); @Override void exceptionCaught(ChannelHandlerContext ctx, Throwable cause); }
HistoryServerStaticFileServerHandler extends SimpleChannelInboundHandler<RoutedRequest> { private void respondWithFile(ChannelHandlerContext ctx, HttpRequest request, String requestPath) throws IOException, ParseException { if (requestPath.endsWith("/")) { requestPath = requestPath + "index.html"; } if (!requestPath.contains(".")) { requestPath = requestPath + ".json"; } final File file = new File(rootPath, requestPath); if (!file.exists()) { ClassLoader cl = HistoryServerStaticFileServerHandler.class.getClassLoader(); try (InputStream resourceStream = cl.getResourceAsStream("web" + requestPath)) { boolean success = false; try { if (resourceStream != null) { URL root = cl.getResource("web"); URL requested = cl.getResource("web" + requestPath); if (root != null && requested != null) { URI rootURI = new URI(root.getPath()).normalize(); URI requestedURI = new URI(requested.getPath()).normalize(); if (!rootURI.relativize(requestedURI).equals(requestedURI)) { LOG.debug("Loading missing file from classloader: {}", requestPath); file.getParentFile().mkdirs(); Files.copy(resourceStream, file.toPath()); success = true; } } } } catch (Throwable t) { LOG.error("error while responding", t); } finally { if (!success) { LOG.debug("Unable to load requested file {} from classloader", requestPath); HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } } } } if (!file.exists() || file.isHidden() || file.isDirectory() || !file.isFile()) { HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } if (!file.getCanonicalFile().toPath().startsWith(rootPath.toPath())) { HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } final String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE); if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) { SimpleDateFormat dateFormatter = new SimpleDateFormat(StaticFileServerHandler.HTTP_DATE_FORMAT, Locale.US); Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince); long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000; long fileLastModifiedSeconds = file.lastModified() / 1000; if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) { if (LOG.isDebugEnabled()) { LOG.debug("Responding 'NOT MODIFIED' for file '" + file.getAbsolutePath() + '\''); } StaticFileServerHandler.sendNotModified(ctx); return; } } if (LOG.isDebugEnabled()) { LOG.debug("Responding with file '" + file.getAbsolutePath() + '\''); } final RandomAccessFile raf; try { raf = new RandomAccessFile(file, "r"); } catch (FileNotFoundException e) { HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("File not found."), NOT_FOUND, Collections.emptyMap()); return; } try { long fileLength = raf.length(); HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); StaticFileServerHandler.setContentTypeHeader(response, file); if (!requestPath.equals("/joboverview.json")) { StaticFileServerHandler.setDateAndCacheHeaders(response, file); } if (HttpHeaders.isKeepAlive(request)) { response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); } HttpHeaders.setContentLength(response, fileLength); ctx.write(response); ChannelFuture lastContentFuture; if (ctx.pipeline().get(SslHandler.class) == null) { ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise()); lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); } else { lastContentFuture = ctx.writeAndFlush(new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192)), ctx.newProgressivePromise()); } if (!HttpHeaders.isKeepAlive(request)) { lastContentFuture.addListener(ChannelFutureListener.CLOSE); } } catch (Exception e) { raf.close(); LOG.error("Failed to serve file.", e); HandlerUtils.sendErrorResponse( ctx, request, new ErrorResponseBody("Internal server error."), INTERNAL_SERVER_ERROR, Collections.emptyMap()); } } HistoryServerStaticFileServerHandler(File rootPath); @Override void channelRead0(ChannelHandlerContext ctx, RoutedRequest routedRequest); @Override void exceptionCaught(ChannelHandlerContext ctx, Throwable cause);  }