Provide content-length header to Docker API calls

Docker daemon authorization plugins reject POST or PUT requests that have a
content type `application/json` header but no content length header. This
commit ensures that a content length header is provided in these cases.

This is a cherry-pick of the changes in d5b2836ec9
which were lost in a forward-merge.

Fixes gh-23957
This commit is contained in:
Scott Frederick
2020-10-28 12:04:33 -05:00
parent 7f02be4286
commit 04a40a4c68
3 changed files with 80 additions and 18 deletions

View File

@@ -337,10 +337,10 @@ class DockerApiTests {
.willReturn(responseOf("create-container-response.json"));
ContainerReference containerReference = this.api.create(config);
assertThat(containerReference.toString()).isEqualTo("e90e34656806");
ByteArrayOutputStream out = new ByteArrayOutputStream();
verify(http()).post(any(), any(), this.writer.capture());
ByteArrayOutputStream out = new ByteArrayOutputStream();
this.writer.getValue().accept(out);
assertThat(out.toByteArray()).hasSizeGreaterThan(130);
assertThat(out.toByteArray().length).isEqualTo(config.toString().length());
}
@Test
@@ -359,10 +359,10 @@ class DockerApiTests {
given(http().put(eq(uploadUri), eq("application/x-tar"), any())).willReturn(emptyResponse());
ContainerReference containerReference = this.api.create(config, content);
assertThat(containerReference.toString()).isEqualTo("e90e34656806");
ByteArrayOutputStream out = new ByteArrayOutputStream();
verify(http()).post(any(), any(), this.writer.capture());
ByteArrayOutputStream out = new ByteArrayOutputStream();
this.writer.getValue().accept(out);
assertThat(out.toByteArray()).hasSizeGreaterThan(130);
assertThat(out.toByteArray().length).isEqualTo(config.toString().length());
verify(http()).put(any(), any(), this.writer.capture());
this.writer.getValue().accept(out);
assertThat(out.toByteArray()).hasSizeGreaterThan(2000);

View File

@@ -64,6 +64,8 @@ class HttpClientTransportTests {
private static final String APPLICATION_JSON = "application/json";
private static final String APPLICATION_X_TAR = "application/x-tar";
@Mock
private CloseableHttpClient client;
@@ -155,44 +157,90 @@ class HttpClientTransportTests {
}
@Test
void postWithContentShouldExecuteHttpPost() throws Exception {
void postWithJsonContentShouldExecuteHttpPost() throws Exception {
String content = "test";
givenClientWillReturnResponse();
given(this.entity.getContent()).willReturn(this.content);
given(this.statusLine.getStatusCode()).willReturn(200);
Response response = this.http.post(this.uri, APPLICATION_JSON,
(out) -> StreamUtils.copy("test", StandardCharsets.UTF_8, out));
(out) -> StreamUtils.copy(content, StandardCharsets.UTF_8, out));
verify(this.client).execute(this.hostCaptor.capture(), this.requestCaptor.capture());
HttpUriRequest request = this.requestCaptor.getValue();
HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
assertThat(request).isInstanceOf(HttpPost.class);
assertThat(request.getURI()).isEqualTo(this.uri);
assertThat(request.getFirstHeader(HttpHeaders.CONTENT_TYPE).getValue()).isEqualTo(APPLICATION_JSON);
assertThat(entity.isRepeatable()).isFalse();
assertThat(entity.getContentLength()).isEqualTo(-1);
assertThat(entity.getContentLength()).isEqualTo(content.length());
assertThat(entity.getContentType().getValue()).isEqualTo(APPLICATION_JSON);
assertThat(entity.isStreaming()).isTrue();
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(entity::getContent);
assertThat(writeToString(entity)).isEqualTo("test");
assertThat(writeToString(entity)).isEqualTo(content);
assertThat(response.getContent()).isSameAs(this.content);
}
@Test
void putWithContentShouldExecuteHttpPut() throws Exception {
void postWithArchiveContentShouldExecuteHttpPost() throws Exception {
String content = "test";
givenClientWillReturnResponse();
given(this.entity.getContent()).willReturn(this.content);
given(this.statusLine.getStatusCode()).willReturn(200);
Response response = this.http.post(this.uri, APPLICATION_X_TAR,
(out) -> StreamUtils.copy(content, StandardCharsets.UTF_8, out));
verify(this.client).execute(this.hostCaptor.capture(), this.requestCaptor.capture());
HttpUriRequest request = this.requestCaptor.getValue();
HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
assertThat(request).isInstanceOf(HttpPost.class);
assertThat(request.getURI()).isEqualTo(this.uri);
assertThat(entity.isRepeatable()).isFalse();
assertThat(entity.getContentLength()).isEqualTo(-1);
assertThat(entity.getContentType().getValue()).isEqualTo(APPLICATION_X_TAR);
assertThat(entity.isStreaming()).isTrue();
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(entity::getContent);
assertThat(writeToString(entity)).isEqualTo(content);
assertThat(response.getContent()).isSameAs(this.content);
}
@Test
void putWithJsonContentShouldExecuteHttpPut() throws Exception {
String content = "test";
givenClientWillReturnResponse();
given(this.entity.getContent()).willReturn(this.content);
given(this.statusLine.getStatusCode()).willReturn(200);
Response response = this.http.put(this.uri, APPLICATION_JSON,
(out) -> StreamUtils.copy("test", StandardCharsets.UTF_8, out));
(out) -> StreamUtils.copy(content, StandardCharsets.UTF_8, out));
verify(this.client).execute(this.hostCaptor.capture(), this.requestCaptor.capture());
HttpUriRequest request = this.requestCaptor.getValue();
HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
assertThat(request).isInstanceOf(HttpPut.class);
assertThat(request.getURI()).isEqualTo(this.uri);
assertThat(request.getFirstHeader(HttpHeaders.CONTENT_TYPE).getValue()).isEqualTo(APPLICATION_JSON);
assertThat(entity.isRepeatable()).isFalse();
assertThat(entity.getContentLength()).isEqualTo(-1);
assertThat(entity.getContentLength()).isEqualTo(content.length());
assertThat(entity.getContentType().getValue()).isEqualTo(APPLICATION_JSON);
assertThat(entity.isStreaming()).isTrue();
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(entity::getContent);
assertThat(writeToString(entity)).isEqualTo("test");
assertThat(writeToString(entity)).isEqualTo(content);
assertThat(response.getContent()).isSameAs(this.content);
}
@Test
void putWithArchiveContentShouldExecuteHttpPut() throws Exception {
String content = "test";
givenClientWillReturnResponse();
given(this.entity.getContent()).willReturn(this.content);
given(this.statusLine.getStatusCode()).willReturn(200);
Response response = this.http.put(this.uri, APPLICATION_X_TAR,
(out) -> StreamUtils.copy(content, StandardCharsets.UTF_8, out));
verify(this.client).execute(this.hostCaptor.capture(), this.requestCaptor.capture());
HttpUriRequest request = this.requestCaptor.getValue();
HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
assertThat(request).isInstanceOf(HttpPut.class);
assertThat(request.getURI()).isEqualTo(this.uri);
assertThat(entity.isRepeatable()).isFalse();
assertThat(entity.getContentLength()).isEqualTo(-1);
assertThat(entity.getContentType().getValue()).isEqualTo(APPLICATION_X_TAR);
assertThat(entity.isStreaming()).isTrue();
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(entity::getContent);
assertThat(writeToString(entity)).isEqualTo(content);
assertThat(response.getContent()).isSameAs(this.content);
}