| // Copyright 2018 The Bazel Authors. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package com.google.devtools.build.remote.worker; |
| |
| import io.netty.buffer.Unpooled; |
| import io.netty.channel.ChannelFuture; |
| import io.netty.channel.ChannelFutureListener; |
| import io.netty.channel.ChannelHandlerContext; |
| import io.netty.channel.SimpleChannelInboundHandler; |
| import io.netty.handler.codec.http.DefaultFullHttpResponse; |
| import io.netty.handler.codec.http.FullHttpRequest; |
| import io.netty.handler.codec.http.FullHttpResponse; |
| import io.netty.handler.codec.http.HttpHeaderNames; |
| import io.netty.handler.codec.http.HttpMethod; |
| import io.netty.handler.codec.http.HttpResponseStatus; |
| import io.netty.handler.codec.http.HttpUtil; |
| import io.netty.handler.codec.http.HttpVersion; |
| import io.netty.util.CharsetUtil; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.ConcurrentMap; |
| |
| /** A simple HTTP REST in-memory cache used during testing the LRE. */ |
| public class HttpCacheServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> { |
| |
| final ConcurrentMap<String, byte[]> cache = new ConcurrentHashMap<>(); |
| |
| @Override |
| protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) { |
| if (!request.decoderResult().isSuccess()) { |
| sendError(ctx, request, HttpResponseStatus.BAD_REQUEST); |
| return; |
| } |
| |
| if (request.method() == HttpMethod.GET) { |
| handleGet(ctx, request); |
| } else if (request.method() == HttpMethod.PUT) { |
| handlePut(ctx, request); |
| } else { |
| sendError(ctx, request, HttpResponseStatus.METHOD_NOT_ALLOWED); |
| } |
| } |
| |
| private void handleGet(ChannelHandlerContext ctx, FullHttpRequest request) { |
| byte[] contents = cache.get(request.uri()); |
| |
| if (contents == null) { |
| sendError(ctx, request, HttpResponseStatus.NOT_FOUND); |
| return; |
| } |
| |
| FullHttpResponse response = |
| new DefaultFullHttpResponse( |
| HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(contents)); |
| HttpUtil.setContentLength(response, contents.length); |
| response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/octet-stream"); |
| ChannelFuture lastContentFuture = ctx.writeAndFlush(response); |
| |
| if (!HttpUtil.isKeepAlive(request)) { |
| lastContentFuture.addListener(ChannelFutureListener.CLOSE); |
| } |
| } |
| |
| private void handlePut(ChannelHandlerContext ctx, FullHttpRequest request) { |
| if (!request.decoderResult().isSuccess()) { |
| sendError(ctx, request, HttpResponseStatus.INTERNAL_SERVER_ERROR); |
| return; |
| } |
| |
| byte[] contentBytes = new byte[request.content().readableBytes()]; |
| request.content().readBytes(contentBytes); |
| cache.putIfAbsent(request.uri(), contentBytes); |
| |
| FullHttpResponse response = |
| new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NO_CONTENT); |
| ChannelFuture lastContentFuture = ctx.writeAndFlush(response); |
| |
| if (!HttpUtil.isKeepAlive(request)) { |
| lastContentFuture.addListener(ChannelFutureListener.CLOSE); |
| } |
| } |
| |
| private static void sendError( |
| ChannelHandlerContext ctx, FullHttpRequest request, HttpResponseStatus status) { |
| FullHttpResponse response = |
| new DefaultFullHttpResponse( |
| HttpVersion.HTTP_1_1, |
| status, |
| Unpooled.copiedBuffer("Failure: " + status + "\r\n", CharsetUtil.UTF_8)); |
| response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8"); |
| ChannelFuture future = ctx.writeAndFlush(response); |
| |
| if (!HttpUtil.isKeepAlive(request)) { |
| future.addListener(ChannelFutureListener.CLOSE); |
| } |
| } |
| } |