/*
* Copyright (C) 2014 SCVNGR, Inc. d/b/a LevelUp
*
* 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.scvngr.levelup.core.net;
import android.content.Context;
import android.os.Parcel;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.test.suitebuilder.annotation.SmallTest;
import com.scvngr.levelup.core.net.AbstractRequest.BadRequestException;
import com.scvngr.levelup.core.test.SupportAndroidTestCase;
import com.scvngr.levelup.core.util.NullUtils;
import com.google.mockwebserver.MockResponse;
import com.google.mockwebserver.MockWebServer;
import com.google.mockwebserver.RecordedRequest;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.Map;
/**
* Tests {@link com.scvngr.levelup.core.net.NetworkConnection}.
*/
public final class NetworkConnectionTest extends SupportAndroidTestCase {
@NonNull
private final MockWebServer mServer = new MockWebServer();
@NonNull
private static final String RESPONSE_BODY = "This is a response body";
@NonNull
private static final String BASE_URL = "http://www.example.com";
@Override
protected void setUp() throws Exception {
super.setUp();
mServer.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_OK)
.setBody(RESPONSE_BODY)
.setHeader("content-length", RESPONSE_BODY.getBytes("utf-8").length));
mServer.play();
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
mServer.shutdown();
}
/**
* Tests {@link com.scvngr.levelup.core.net.NetworkConnection#configureConnection} builds a basic request properly.
*
* @throws java.io.IOException if {@link com.scvngr.levelup.core.net.NetworkConnection#configureConnection} throws
*/
@SmallTest
public void testConfigureConnection_Basic() throws IOException, BadRequestException {
final RequestStub request = new RequestStub(HttpMethod.GET, BASE_URL, null, null, null);
final HttpURLConnection connection =
NetworkConnection.configureConnection(getContext(), request);
assertEquals(connection.getURL(), request.getUrl(getContext()));
assertEquals(connection.getRequestMethod(), request.getMethod().name());
assertEquals(connection.getURL().getQuery(), null);
assertTrue("Request headers are empty", connection.getRequestProperties().isEmpty());
}
/**
* Tests {@link com.scvngr.levelup.core.net.NetworkConnection#configureConnection} builds a request with headers properly.
*
* @throws java.io.IOException if {@link com.scvngr.levelup.core.net.NetworkConnection#configureConnection} throws
*/
@SmallTest
public void testConfigureConnection_withHeaders() throws IOException, BadRequestException {
final Map<String, String> headers = new HashMap<String, String>();
headers.put("test_value", "test");
headers.put("header2", "header");
final RequestStub request = new RequestStub(HttpMethod.GET, BASE_URL, headers, null, null);
final HttpURLConnection connection =
NetworkConnection.configureConnection(getContext(), request);
assertRequestPropertiesEquals(headers, connection);
}
/**
* Tests {@link com.scvngr.levelup.core.net.NetworkConnection#configureConnection} builds a request with null headers
* properly.
*
* @throws java.io.IOException if {@link com.scvngr.levelup.core.net.NetworkConnection#configureConnection} throws
*/
@SmallTest
public void testConfigureConnection_withNullHeaders() throws IOException, BadRequestException {
final RequestStub request = new RequestStub(HttpMethod.GET, BASE_URL, null, null, null);
final HttpURLConnection connection =
NetworkConnection.configureConnection(getContext(), request);
assertEquals(connection.getURL(), request.getUrl(getContext()));
assertEquals(connection.getRequestMethod(), request.getMethod().name());
assertEquals(connection.getURL().getQuery(), null);
assertTrue("Request headers are empty", connection.getRequestProperties().isEmpty());
}
/**
* Tests {@link com.scvngr.levelup.core.net.NetworkConnection#configureConnection} builds a request with query params
* properly.
*
* @throws java.io.IOException if {@link com.scvngr.levelup.core.net.NetworkConnection#configureConnection} throws
*/
@SmallTest
public void testConfigureConnection_withQueryParameters() throws IOException,
BadRequestException {
final Map<String, String> params = new HashMap<String, String>();
params.put("test_value", "test");
params.put("param2", "param");
final RequestStub request = new RequestStub(HttpMethod.GET, BASE_URL, null, params, null);
final HttpURLConnection connection =
NetworkConnection.configureConnection(getContext(), request);
final String expectedQueryString = "param2=param&test_value=test";
assertEquals(request.getUrl(getContext()).getQuery(), connection.getURL().getQuery());
assertEquals(expectedQueryString, connection.getURL().getQuery());
assertTrue("Request headers are empty", connection.getRequestProperties().isEmpty());
}
/**
* Tests {@link com.scvngr.levelup.core.net.NetworkConnection#configureConnection} builds a request with null query params
* properly.
*
* @throws java.io.IOException if {@link com.scvngr.levelup.core.net.NetworkConnection#configureConnection} throws
*/
@SmallTest
public void testConfigureConnection_withNullQueryParamters() throws IOException,
BadRequestException {
final RequestStub request = new RequestStub(HttpMethod.GET, BASE_URL, null, null, null);
final HttpURLConnection connection =
NetworkConnection.configureConnection(getContext(), request);
assertEquals(request.getUrl(getContext()).getQuery(), connection.getURL().getQuery());
assertEquals(null, connection.getURL().getQuery());
}
/**
* Tests {@link com.scvngr.levelup.core.net.NetworkConnection#configureConnection} builds a request with a POST body
* correctly.
*
* @throws java.io.IOException if {@link com.scvngr.levelup.core.net.NetworkConnection#configureConnection} throws
*/
@SmallTest
public void testConfigureConnection_withPostBody() throws IOException, BadRequestException {
final String body = "this is a test body";
final RequestStub request = new RequestStub(HttpMethod.POST, BASE_URL, null, null, body);
final HttpURLConnection connection =
NetworkConnection.configureConnection(getContext(), request);
assertTrue("Connection is marked as output", connection.getDoOutput());
assertEquals(HttpMethod.POST.name(), connection.getRequestMethod());
}
/**
* Tests {@link com.scvngr.levelup.core.net.NetworkConnection#configureConnection} builds a request with a POST body
* correctly.
*
* @throws java.io.IOException if {@link com.scvngr.levelup.core.net.NetworkConnection#configureConnection} throws
*/
@SmallTest
public void testConfigureConnection_withPostBodyAndPutRequest() throws IOException,
BadRequestException {
final String body = "this is a test body";
final RequestStub request =
new RequestStub(HttpMethod.PUT, BASE_URL, new HashMap<String, String>(),
new HashMap<String, String>(), body);
final HttpURLConnection connection =
NetworkConnection.configureConnection(getContext(), request);
assertTrue("Connection is marked as output", connection.getDoOutput());
assertEquals(HttpMethod.PUT.name(), connection.getRequestMethod());
}
/**
* Tests {@link com.scvngr.levelup.core.net.NetworkConnection#doOutput} with a get request with no post body does not mark
* the connection as output and keeps it as a get.
*
* @throws java.io.IOException if {@link com.scvngr.levelup.core.net.NetworkConnection#configureConnection} throws
*/
@SmallTest
public void testDoOutput_withNoPostBody() throws IOException, BadRequestException {
final RequestStub request =
new RequestStub(HttpMethod.GET, getMockServerUrl(), new HashMap<String, String>(),
new HashMap<String, String>(), null);
final HttpURLConnection connection =
NetworkConnection.configureConnection(getContext(), request);
assertFalse("Connection is not marked as output", connection.getDoOutput());
NetworkConnection.doOutput(getContext(), connection, request);
assertEquals(HttpMethod.GET.name(), connection.getRequestMethod());
}
/**
* Tests {@link com.scvngr.levelup.core.net.NetworkConnection#configureConnection} builds a request with a POST body
* correctly even if the request is marked as a GET.
*
* @throws java.io.IOException if {@link com.scvngr.levelup.core.net.NetworkConnection#configureConnection} throws
*/
@SmallTest
public void testDoOutput_withPostBodyAndGetRequest() throws IOException, BadRequestException {
final String body = "this is a test body";
final RequestStub request =
new RequestStub(HttpMethod.GET, getMockServerUrl(), new HashMap<String, String>(),
new HashMap<String, String>(), body);
final HttpURLConnection connection =
NetworkConnection.configureConnection(getContext(), request);
assertTrue("Connection is marked as output", connection.getDoOutput());
NetworkConnection.doOutput(getContext(), connection, request);
assertEquals(HttpMethod.POST.name(), connection.getRequestMethod());
}
/**
* Tests {@link com.scvngr.levelup.core.net.NetworkConnection#send} with a GET request.
*
* @throws InterruptedException if {@link MockWebServer#takeRequest()} throws it
* @throws java.io.IOException if {@link #getMockServerUrl()} throws it
*/
@SmallTest
public void testSend_get() throws InterruptedException, IOException {
final RequestStub request =
new RequestStub(HttpMethod.GET, getMockServerUrl(), new HashMap<String, String>(),
new HashMap<String, String>(), null);
final StreamingResponse response = NetworkConnection.send(getContext(), request);
final RecordedRequest recorded = mServer.takeRequest();
assertEquals("/", recorded.getPath());
assertEquals(request.getMethod().name(), recorded.getMethod());
assertEquals(HttpURLConnection.HTTP_OK, response.getHttpStatusCode());
}
/**
* Tests {@link com.scvngr.levelup.core.net.NetworkConnection#send} with a GET request and query params.
*
* @throws InterruptedException if {@link MockWebServer#takeRequest()} throws it
* @throws java.io.IOException if {@link #getMockServerUrl()} throws it
*/
@SmallTest
public void testSend_getWithQueryParams() throws InterruptedException, IOException {
final Map<String, String> query = new HashMap<String, String>();
query.put("test1", "value1");
final RequestStub request =
new RequestStub(HttpMethod.GET, getMockServerUrl(), new HashMap<String, String>(),
query, null);
final StreamingResponse response = NetworkConnection.send(getContext(), request);
final RecordedRequest recorded = mServer.takeRequest();
assertEquals("/?test1=value1", recorded.getPath());
assertEquals(request.getMethod().name(), recorded.getMethod());
assertEquals(HttpURLConnection.HTTP_OK, response.getHttpStatusCode());
}
/**
* Tests {@link com.scvngr.levelup.core.net.NetworkConnection#send} with a GET request and headers.
*
* @throws InterruptedException if {@link MockWebServer#takeRequest()} throws it
* @throws java.io.IOException if {@link #getMockServerUrl()} throws it
*/
@SmallTest
public void testSend_getWithHeaders() throws InterruptedException, IOException {
final Map<String, String> headers = new HashMap<String, String>();
headers.put("test1", "value1");
headers.put("testing_this", "more headers");
final RequestStub request =
new RequestStub(HttpMethod.GET, getMockServerUrl(), headers,
new HashMap<String, String>(), null);
final StreamingResponse response = NetworkConnection.send(getContext(), request);
final RecordedRequest recorded = mServer.takeRequest();
assertEquals("/", recorded.getPath());
assertTrue("First header was found", recorded.getHeaders().contains("test1: value1"));
assertTrue("Second header was found",
recorded.getHeaders().contains("testing_this: more headers"));
assertEquals(request.getMethod().name(), recorded.getMethod());
assertEquals(HttpURLConnection.HTTP_OK, response.getHttpStatusCode());
}
/**
* Tests {@link com.scvngr.levelup.core.net.NetworkConnection#send} with a GET request and data returned.
*
* @throws InterruptedException if {@link MockWebServer#takeRequest()} throws it
* @throws java.io.IOException if {@link #getMockServerUrl()} throws it
*/
@SmallTest
public void testSend_getWithData() throws InterruptedException, IOException {
final RequestStub request =
new RequestStub(HttpMethod.GET, getMockServerUrl(), new HashMap<String, String>(),
null, null);
final StreamingResponse response = NetworkConnection.send(getContext(), request);
final RecordedRequest recorded = mServer.takeRequest();
assertEquals("/", recorded.getPath());
assertEquals(request.getMethod().name(), recorded.getMethod());
assertEquals(HttpURLConnection.HTTP_OK, response.getHttpStatusCode());
final BufferedReader reader =
new BufferedReader(new InputStreamReader(response.getData(), "utf-8"));
final String out = reader.readLine();
assertEquals(RESPONSE_BODY, out);
}
/**
* Tests {@link com.scvngr.levelup.core.net.NetworkConnection#send} with a POST request with post body.
*
* @throws InterruptedException if {@link MockWebServer#takeRequest()} throws it
* @throws java.io.IOException if {@link #getMockServerUrl()} throws it
*/
@SmallTest
public void testSend_post() throws InterruptedException, IOException {
final String body = "this is a test body";
final RequestStub request =
new RequestStub(HttpMethod.POST, getMockServerUrl(), new HashMap<String, String>(),
new HashMap<String, String>(), body);
final StreamingResponse response = NetworkConnection.send(getContext(), request);
final RecordedRequest recorded = mServer.takeRequest();
assertEquals(request.getMethod().name(), recorded.getMethod());
assertEquals(HttpURLConnection.HTTP_OK, response.getHttpStatusCode());
assertEquals(body, new String(recorded.getBody()));
}
/**
* Tests {@link com.scvngr.levelup.core.net.NetworkConnection#send} with a PUT request.
*
* @throws InterruptedException if {@link MockWebServer#takeRequest()} throws it
* @throws java.io.IOException if {@link #getMockServerUrl()} throws it
*/
@SmallTest
public void testSend_put() throws InterruptedException, IOException {
final String body = "this is a test body";
final RequestStub request =
new RequestStub(HttpMethod.PUT, getMockServerUrl(), new HashMap<String, String>(),
new HashMap<String, String>(), body);
final StreamingResponse response = NetworkConnection.send(getContext(), request);
final RecordedRequest recorded = mServer.takeRequest();
assertEquals(request.getMethod().name(), recorded.getMethod());
assertEquals(HttpURLConnection.HTTP_OK, response.getHttpStatusCode());
assertEquals(body, new String(recorded.getBody()));
}
/**
* Tests {@link com.scvngr.levelup.core.net.NetworkConnection#send} with a DELETE request.
*
* @throws InterruptedException if {@link MockWebServer#takeRequest()} throws it
* @throws java.io.IOException if {@link #getMockServerUrl()} throws it
*/
@SmallTest
public void testSend_delete() throws InterruptedException, IOException {
final RequestStub request =
new RequestStub(HttpMethod.DELETE, getMockServerUrl(),
new HashMap<String, String>(), new HashMap<String, String>(), null);
final StreamingResponse response = NetworkConnection.send(getContext(), request);
final RecordedRequest recorded = mServer.takeRequest();
assertEquals(request.getMethod().name(), recorded.getMethod());
assertEquals(HttpURLConnection.HTTP_OK, response.getHttpStatusCode());
}
/**
* Tests {@link com.scvngr.levelup.core.net.NetworkConnection#send} with a request that throws a {@link com.scvngr.levelup.core.net.AbstractRequest.BadRequestException}
* .
*/
@SmallTest
public void testSend_withBadRequestException() throws IOException {
final BadRequestExceptionThrowingRequest request =
new BadRequestExceptionThrowingRequest(HttpMethod.GET, getMockServerUrl(),
new HashMap<String, String>(), new HashMap<String, String>(), null);
final StreamingResponse response = NetworkConnection.send(getContext(), request);
final Exception exception = response.getError();
assertNotNull("Does not retry when a BadRequestException is thrown", exception);
assertTrue(exception instanceof BadRequestException);
}
/**
* Tests {@link NetworkConnection#send} with a request that throws one {@link IOException}.
*/
@SmallTest
public void testSend_withIOException() throws IOException {
final IOExceptionThrowingRequest request =
new IOExceptionThrowingRequest(HttpMethod.POST, getMockServerUrl(),
new HashMap<String, String>(), new HashMap<String, String>(), "test body");
final StreamingResponse response = NetworkConnection.send(getContext(), request);
final Exception exception = response.getError();
assertEquals(1, request.mNumWrites);
assertNotNull("Does not retry when an IOException is thrown", exception);
assertTrue(exception instanceof IOException);
}
/**
* Tests {@link NetworkConnection#send} with a request that throws two {@link EOFException}s.
*/
@SmallTest
public void testSend_withTwoEOFExceptions() throws IOException {
final EOFExceptionThrowingRequest request = getEOFExceptionThrowingRequest(2);
final StreamingResponse response = NetworkConnection.send(getContext(), request);
assertEquals(3, request.mNumWrites);
assertNull("Retries at least 2 times to close stale sockets when an EOFException is thrown",
response.getError());
}
/**
* Tests {@link NetworkConnection#send} with a request that throws one more than
* {@link NetworkConnection#MAX_POOLED_CONNECTIONS} {@link EOFException}s.
*/
@SmallTest
public void testSend_withMaxPlusOneEOFExceptions() throws IOException {
final EOFExceptionThrowingRequest request =
getEOFExceptionThrowingRequest(NetworkConnection.MAX_POOLED_CONNECTIONS + 1);
final StreamingResponse response = NetworkConnection.send(getContext(), request);
final Exception exception = response.getError();
assertEquals(NetworkConnection.MAX_POOLED_CONNECTIONS + 2, request.mNumWrites);
assertNull(NullUtils.format(
"Retries at least MAX_POOLED_CONNECTIONS(%d) + 1 times to close stale sockets when "
+ "EOFExceptions are thrown", NetworkConnection.MAX_POOLED_CONNECTIONS), exception);
}
/**
* Tests {@link NetworkConnection#send} with a request that throws two more than
* {@link NetworkConnection#MAX_POOLED_CONNECTIONS} {@link EOFException}s.
*/
@SmallTest
public void testSend_withMaxPlusTwoEOFExceptions() throws IOException {
final EOFExceptionThrowingRequest request =
getEOFExceptionThrowingRequest(NetworkConnection.MAX_POOLED_CONNECTIONS + 2);
final StreamingResponse response = NetworkConnection.send(getContext(), request);
final Exception exception = response.getError();
assertEquals(NetworkConnection.MAX_POOLED_CONNECTIONS + 2, request.mNumWrites);
assertNotNull(NullUtils.format(
"Fails if more than MAX_POOLED_CONNECTIONS(%d) + 2 EOFExceptions are thrown",
NetworkConnection.MAX_POOLED_CONNECTIONS), exception);
assertTrue(exception instanceof EOFException);
}
/**
* @param numExceptions the number of {@link EOFException}s to throw.
* @return the {@link EOFExceptionThrowingRequest}.
*/
private EOFExceptionThrowingRequest getEOFExceptionThrowingRequest(final int numExceptions) {
try {
return new EOFExceptionThrowingRequest(HttpMethod.POST, getMockServerUrl(),
new HashMap<String, String>(), new HashMap<String, String>(), "test body",
numExceptions);
} catch (final IOException e) {
throw new AssertionError(e);
}
}
/**
* Helper method to get the mock server URL.
*
* @return the string URL to the mock server
* @throws java.io.IOException if {@link MockWebServer#getUrl(String)} throws
*/
@NonNull
private String getMockServerUrl() throws IOException {
return NullUtils.nonNullContract(mServer.getUrl("/").toString());
}
/**
* Test implementation of {@link com.scvngr.levelup.core.net.AbstractRequest} that throws a {@link com.scvngr.levelup.core.net.AbstractRequest.BadRequestException} when
* {@link com.scvngr.levelup.core.net.AbstractRequest#getUrlString} is called.
*/
private static final class BadRequestExceptionThrowingRequest extends RequestStub {
private int mNumExceptions = 1;
public BadRequestExceptionThrowingRequest(@NonNull final HttpMethod method,
@NonNull final String url, @Nullable final Map<String, String> requestHeaders,
@Nullable final Map<String, String> queryParams, @Nullable final String body) {
super(method, url, requestHeaders, queryParams, body);
}
@Override
@NonNull
public String getUrlString(@NonNull final Context context) throws BadRequestException {
if (0 < mNumExceptions) {
mNumExceptions--;
throw new BadRequestException("bad request");
}
return super.getUrlString(context);
}
}
/**
* Test implementation of {@link AbstractRequest} that throws up to {@code numExceptions}
* {@link EOFException}s when {@link AbstractRequest#writeBodyToStream} is called.
*/
private static final class EOFExceptionThrowingRequest extends RequestStub {
private int mNumExceptions;
private int mNumWrites;
public EOFExceptionThrowingRequest(@NonNull final HttpMethod method,
@NonNull final String url, @Nullable final Map<String, String> requestHeaders,
@Nullable final Map<String, String> queryParams, @NonNull final String body,
final int numExceptions) {
super(method, url, requestHeaders, queryParams, body);
mNumExceptions = numExceptions;
}
@Override
public void writeBodyToStream(@NonNull final Context context,
@NonNull final OutputStream stream) throws IOException {
super.writeBodyToStream(context, stream);
mNumWrites++;
if (0 < mNumExceptions) {
mNumExceptions--;
throw new EOFException("stale connection");
}
}
}
/**
* Test implementation of {@link AbstractRequest} that throws an {@link IOException} the first
* time {@link AbstractRequest#writeBodyToStream} is called.
*/
private static final class IOExceptionThrowingRequest extends RequestStub {
private int mNumExceptions = 1;
private int mNumWrites;
public IOExceptionThrowingRequest(@NonNull final HttpMethod method,
@NonNull final String url, @Nullable final Map<String, String> requestHeaders,
@Nullable final Map<String, String> queryParams, @Nullable final String body) {
super(method, url, requestHeaders, queryParams, body);
}
@Override
public void writeBodyToStream(@NonNull final Context context,
@NonNull final OutputStream stream) throws IOException {
super.writeBodyToStream(context, stream);
mNumWrites++;
if (0 < mNumExceptions) {
mNumExceptions--;
throw new IOException("broken pipe");
}
}
}
private static void assertRequestPropertiesEquals(
@NonNull final Map<String, String> expectedHeaders,
@NonNull final HttpURLConnection connection) {
assertEquals(expectedHeaders.size(), connection.getRequestProperties().size());
for (final String key : expectedHeaders.keySet()) {
assertEquals(expectedHeaders.get(key), connection.getRequestProperty(key));
}
}
/**
* A mock implementation of an AbstractRequest.
*/
public static class RequestStub extends AbstractRequest {
@Nullable
private final RequestBody mRequestBody;
/**
* @param method request method
* @param url mock URL
* @param requestHeaders optional request headers
* @param queryParams optional query parameters
* @param body optional body
*/
public RequestStub(@NonNull final HttpMethod method, @NonNull final String url,
@Nullable final Map<String, String> requestHeaders,
@Nullable final Map<String, String> queryParams,
@Nullable final String body) {
super(method, url, requestHeaders, queryParams);
mRequestBody = body != null ? new StringRequestBodyImpl(body) : null;
}
@Override
public void writeBodyToStream(@NonNull final Context context,
@NonNull final OutputStream stream) throws IOException {
if (mRequestBody != null) {
mRequestBody.writeToOutputStream(context, stream);
}
}
@Override
public int getBodyLength(@NonNull final Context context) {
return mRequestBody != null ? mRequestBody.getContentLength() : 0;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((mRequestBody == null) ? 0 : mRequestBody.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final RequestStub other = (RequestStub) obj;
if (mRequestBody == null) {
if (other.mRequestBody != null) {
return false;
}
} else if (!mRequestBody.equals(other.mRequestBody)) {
return false;
}
return true;
}
}
/**
* Concrete implementation of {@link com.scvngr.levelup.core.net.StringRequestBody}.
*/
protected static final class StringRequestBodyImpl extends StringRequestBody {
/**
* Parcelable creator.
*/
public static final Creator<StringRequestBodyImpl> CREATOR =
new Creator<StringRequestBodyImpl>() {
@Override
public StringRequestBodyImpl createFromParcel(final Parcel source) {
return new StringRequestBodyImpl(NullUtils.nonNullContract(source));
}
@Override
public StringRequestBodyImpl[] newArray(final int size) {
return new StringRequestBodyImpl[size];
}
};
/**
* @param in source
*/
public StringRequestBodyImpl(@NonNull final Parcel in) {
super(in);
}
/**
* @param body contents
*/
public StringRequestBodyImpl(@NonNull final String body) {
super(body);
}
@Override
@NonNull
public String getContentType() {
return "text/plain";
}
}
}