// 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.lib.remote.blobstore.http; import static com.google.common.truth.Truth.assertThat; import com.google.common.net.HttpHeaders; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelPromise; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpChunkedInput; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import java.io.ByteArrayInputStream; import java.net.URI; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Tests for {@link HttpUploadHandler}. */ @RunWith(JUnit4.class) public class HttpUploadHandlerTest extends AbstractHttpHandlerTest { private static final URI CACHE_URI = URI.create("http://storage.googleapis.com:80/cache-bucket"); /** * Test that uploading blobs works to both the Action Cache and the CAS. Also test that the * handler is reusable. */ @Test public void uploadsShouldWork() throws Exception { EmbeddedChannel ch = new EmbeddedChannel(new HttpUploadHandler(null)); HttpResponseStatus[] statuses = new HttpResponseStatus[] {HttpResponseStatus.OK, HttpResponseStatus.CREATED, HttpResponseStatus.ACCEPTED, HttpResponseStatus.NO_CONTENT}; for (HttpResponseStatus status : statuses) { uploadsShouldWork(true, ch, status); uploadsShouldWork(false, ch, status); } } private void uploadsShouldWork(boolean casUpload, EmbeddedChannel ch, HttpResponseStatus status) throws Exception { ByteArrayInputStream data = new ByteArrayInputStream(new byte[] {1, 2, 3, 4, 5}); ChannelPromise writePromise = ch.newPromise(); ch.writeOneOutbound(new UploadCommand(CACHE_URI, casUpload, "abcdef", data, 5), writePromise); HttpRequest request = ch.readOutbound(); assertThat(request.method()).isEqualTo(HttpMethod.PUT); assertThat(request.headers().get(HttpHeaders.CONNECTION)) .isEqualTo(HttpHeaderValues.KEEP_ALIVE.toString()); HttpChunkedInput content = ch.readOutbound(); assertThat(content.readChunk(ByteBufAllocator.DEFAULT).content().readableBytes()).isEqualTo(5); FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status); response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); ch.writeInbound(response); assertThat(writePromise.isDone()).isTrue(); assertThat(ch.isOpen()).isTrue(); } /** Test that the handler correctly supports http error codes i.e. 404 (NOT FOUND). */ @Test public void httpErrorsAreSupported() throws Exception { EmbeddedChannel ch = new EmbeddedChannel(new HttpUploadHandler(null)); ByteArrayInputStream data = new ByteArrayInputStream(new byte[] {1, 2, 3, 4, 5}); ChannelPromise writePromise = ch.newPromise(); ch.writeOneOutbound(new UploadCommand(CACHE_URI, true, "abcdef", data, 5), writePromise); HttpRequest request = ch.readOutbound(); assertThat(request).isInstanceOf(HttpRequest.class); HttpChunkedInput content = ch.readOutbound(); assertThat(content).isInstanceOf(HttpChunkedInput.class); FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.FORBIDDEN); response.headers().set(HttpHeaders.CONNECTION, HttpHeaderValues.CLOSE); ch.writeInbound(response); assertThat(writePromise.isDone()).isTrue(); assertThat(writePromise.cause()).isInstanceOf(HttpException.class); assertThat(((HttpException) writePromise.cause()).status()) .isEqualTo(HttpResponseStatus.FORBIDDEN); assertThat(ch.isOpen()).isFalse(); } }