/** * Send a file (with zero-copy) to the client. This method doesn't provide any security guarantee. * The caller is responsible for the argument they pass in. * * @param status The status of the request (e.g. 200 OK or 404 Not Found). * @param path The path to the file to send to the client. * @param max_age The expiration time of this entity, in seconds. This is not a timestamp, it's * how old the resource is allowed to be in the client cache. See RFC 2616 section 14.9 for * more information. Use 0 to disable caching. */ public void sendFile(final HttpResponseStatus status, final String path, final int max_age) throws IOException { if (max_age < 0) { throw new IllegalArgumentException("Negative max_age=" + max_age + " for path=" + path); } if (!chan.isConnected()) { done(); return; } RandomAccessFile file; try { file = new RandomAccessFile(path, "r"); } catch (FileNotFoundException e) { logWarn("File not found: " + e.getMessage()); if (querystring != null) { querystring.remove("png"); // Avoid potential recursion. } notFound(); return; } final long length = file.length(); { final DefaultHttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status); final String mimetype = guessMimeTypeFromUri(path); response.setHeader( HttpHeaders.Names.CONTENT_TYPE, mimetype == null ? "text/plain" : mimetype); final long mtime = new File(path).lastModified(); if (mtime > 0) { response.setHeader(HttpHeaders.Names.AGE, (System.currentTimeMillis() - mtime) / 1000); } else { logWarn("Found a file with mtime=" + mtime + ": " + path); } response.setHeader( HttpHeaders.Names.CACHE_CONTROL, max_age == 0 ? "no-cache" : "max-age=" + max_age); HttpHeaders.setContentLength(response, length); chan.write(response); } final DefaultFileRegion region = new DefaultFileRegion(file.getChannel(), 0, length); final ChannelFuture future = chan.write(region); future.addListener( new ChannelFutureListener() { public void operationComplete(final ChannelFuture future) { region.releaseExternalResources(); done(); } }); if (!HttpHeaders.isKeepAlive(request)) { future.addListener(ChannelFutureListener.CLOSE); } }
/** * Sends an HTTP reply to the client. * * @param status The status of the request (e.g. 200 OK or 404 Not Found). * @param buf The content of the reply to send. */ private void sendBuffer(final HttpResponseStatus status, final ChannelBuffer buf) { if (!chan.isConnected()) { done(); return; } final DefaultHttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status); response.setHeader(HttpHeaders.Names.CONTENT_TYPE, guessMimeType(buf)); // TODO(tsuna): Server, X-Backend, etc. headers. response.setContent(buf); final boolean keepalive = HttpHeaders.isKeepAlive(request); if (keepalive) { HttpHeaders.setContentLength(response, buf.readableBytes()); } final ChannelFuture future = chan.write(response); if (!keepalive) { future.addListener(ChannelFutureListener.CLOSE); } done(); }