mirror of
https://github.com/google/nomulus.git
synced 2025-05-05 22:47:51 +02:00
According to RFC 2046, the body of the multipart contains: multipart-body := [preamble CRLF] dash-boundary transport-padding CRLF body-part *encapsulation close-delimiter transport-padding [CRLF epilogue] The preemble and epilogue are optional, and ignored. However, it's not 100% explicit whether the CRLFs after the preamble and before the epilogue are required. The one after the preemble is often not given if there's no preemble, so it's conceivable that you don't *have* to give the CRLF before the epilogue if there's no epilogue (it's also enclosed in the [], making it part of the "optional") However, it seems that when the TMDB "migrated to the cloud" (as they describe it) on Aug. 13 2018, they started requiring that CRLF. TESTED=connected to a TMDB-whitelisted server, used CURL to manually create the message as we currently send it (without the final CRLF) with junk data and got the error from the bug. Then sent the exact same message with the additional CRLF, and got a different error that directly relates to the content of the junk data. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=212637246
107 lines
4.3 KiB
Java
107 lines
4.3 KiB
Java
// Copyright 2017 The Nomulus 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 google.registry.util;
|
|
|
|
import static com.google.common.net.HttpHeaders.CONTENT_LENGTH;
|
|
import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
|
|
import static com.google.common.net.MediaType.CSV_UTF_8;
|
|
import static com.google.common.truth.Truth.assertThat;
|
|
import static google.registry.testing.JUnitBackports.assertThrows;
|
|
import static google.registry.util.UrlFetchUtils.setPayloadMultipart;
|
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
|
import static org.mockito.Matchers.any;
|
|
import static org.mockito.Mockito.doAnswer;
|
|
import static org.mockito.Mockito.mock;
|
|
import static org.mockito.Mockito.times;
|
|
import static org.mockito.Mockito.verify;
|
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
|
|
import com.google.appengine.api.urlfetch.HTTPHeader;
|
|
import com.google.appengine.api.urlfetch.HTTPRequest;
|
|
import google.registry.testing.AppEngineRule;
|
|
import google.registry.testing.InjectRule;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.Random;
|
|
import org.junit.Before;
|
|
import org.junit.Rule;
|
|
import org.junit.Test;
|
|
import org.junit.runner.RunWith;
|
|
import org.junit.runners.JUnit4;
|
|
import org.mockito.ArgumentCaptor;
|
|
|
|
/** Unit tests for {@link UrlFetchUtils}. */
|
|
@RunWith(JUnit4.class)
|
|
public class UrlFetchUtilsTest {
|
|
|
|
@Rule
|
|
public final AppEngineRule appEngine = AppEngineRule.builder()
|
|
.build();
|
|
@Rule
|
|
public final InjectRule inject = new InjectRule();
|
|
|
|
@Before
|
|
public void setupRandomZeroes() {
|
|
Random random = mock(Random.class);
|
|
inject.setStaticField(UrlFetchUtils.class, "random", random);
|
|
doAnswer(
|
|
info -> {
|
|
Arrays.fill((byte[]) info.getArguments()[0], (byte) 0);
|
|
return null;
|
|
})
|
|
.when(random)
|
|
.nextBytes(any(byte[].class));
|
|
}
|
|
|
|
@Test
|
|
public void testSetPayloadMultipart() {
|
|
HTTPRequest request = mock(HTTPRequest.class);
|
|
setPayloadMultipart(
|
|
request, "lol", "cat", CSV_UTF_8, "The nice people at the store say hello. ヘ(◕。◕ヘ)");
|
|
ArgumentCaptor<HTTPHeader> headerCaptor = ArgumentCaptor.forClass(HTTPHeader.class);
|
|
verify(request, times(2)).addHeader(headerCaptor.capture());
|
|
List<HTTPHeader> addedHeaders = headerCaptor.getAllValues();
|
|
assertThat(addedHeaders.get(0).getName()).isEqualTo(CONTENT_TYPE);
|
|
assertThat(addedHeaders.get(0).getValue())
|
|
.isEqualTo(
|
|
"multipart/form-data; "
|
|
+ "boundary=\"------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"");
|
|
assertThat(addedHeaders.get(1).getName()).isEqualTo(CONTENT_LENGTH);
|
|
assertThat(addedHeaders.get(1).getValue()).isEqualTo("294");
|
|
String payload = "--------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n"
|
|
+ "Content-Disposition: form-data; name=\"lol\"; filename=\"cat\"\r\n"
|
|
+ "Content-Type: text/csv; charset=utf-8\r\n"
|
|
+ "\r\n"
|
|
+ "The nice people at the store say hello. ヘ(◕。◕ヘ)\r\n"
|
|
+ "--------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA--\r\n";
|
|
verify(request).setPayload(payload.getBytes(UTF_8));
|
|
verifyNoMoreInteractions(request);
|
|
}
|
|
|
|
@Test
|
|
public void testSetPayloadMultipart_boundaryInPayload() {
|
|
HTTPRequest request = mock(HTTPRequest.class);
|
|
String payload = "I screamed------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHHH";
|
|
IllegalStateException thrown =
|
|
assertThrows(
|
|
IllegalStateException.class,
|
|
() -> setPayloadMultipart(request, "lol", "cat", CSV_UTF_8, payload));
|
|
assertThat(thrown)
|
|
.hasMessageThat()
|
|
.contains(
|
|
"Multipart data contains autogenerated boundary: "
|
|
+ "------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
|
|
}
|
|
}
|