From 1bd4dd1a5579009e06a2485bc8f1b86cf3d5e7db Mon Sep 17 00:00:00 2001 From: jwilson Date: Fri, 5 Jul 2013 14:06:58 -0700 Subject: [PATCH] Add factory methods to create request bodies --- .../java/com/squareup/okhttp/Dispatcher.java | 7 ++ .../java/com/squareup/okhttp/Request.java | 83 ++++++++++++++++- .../java/com/squareup/okhttp/RequestTest.java | 90 +++++++++++++++++++ .../okhttp/internal/AsyncApiTest.java | 21 +++++ 4 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 okhttp/src/test/java/com/squareup/okhttp/RequestTest.java diff --git a/okhttp/src/main/java/com/squareup/okhttp/Dispatcher.java b/okhttp/src/main/java/com/squareup/okhttp/Dispatcher.java index 924ace5c9..e9499a56c 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/Dispatcher.java +++ b/okhttp/src/main/java/com/squareup/okhttp/Dispatcher.java @@ -92,6 +92,13 @@ class Dispatcher { Request.Body body = request.body(); if (body != null) { connection.setDoOutput(true); + long contentLength = body.contentLength(); + if (contentLength == -1 || contentLength > Integer.MAX_VALUE) { + connection.setChunkedStreamingMode(0); + } else { + // Don't call setFixedLengthStreamingMode(long); that's only available on Java 1.7+. + connection.setFixedLengthStreamingMode((int) contentLength); + } body.writeTo(connection.getOutputStream()); } return connection; diff --git a/okhttp/src/main/java/com/squareup/okhttp/Request.java b/okhttp/src/main/java/com/squareup/okhttp/Request.java index 73617716f..6f3569ba0 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/Request.java +++ b/okhttp/src/main/java/com/squareup/okhttp/Request.java @@ -15,9 +15,14 @@ */ package com.squareup.okhttp; +import com.squareup.okhttp.internal.Util; import com.squareup.okhttp.internal.http.RawHeaders; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; +import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.util.List; @@ -89,9 +94,12 @@ public final class Request { return tag; } - public abstract class Body { - /** Returns the Content-Type header for this body, or null if the content type is unknown. */ - public String contentType() { + public abstract static class Body { + /** + * Returns the Content-Type header for this body, or null if the content + * type is unknown. + */ + public MediaType contentType() { return null; } @@ -100,7 +108,76 @@ public final class Request { return -1; } + /** Writes the content of this request to {@code out}. */ public abstract void writeTo(OutputStream out) throws IOException; + + /** + * Returns a new request body that transmits {@code content}. If {@code + * contentType} lacks a charset, this will use UTF-8. + */ + public static Body create(MediaType contentType, String content) { + contentType = contentType.charset() != null + ? contentType + : MediaType.parse(contentType + "; charset=utf-8"); + try { + byte[] bytes = content.getBytes(contentType.charset().name()); + return create(contentType, bytes); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(); + } + } + + /** Returns a new request body that transmits {@code content}. */ + public static Body create(final MediaType contentType, final byte[] content) { + if (contentType == null) throw new NullPointerException("contentType == null"); + if (content == null) throw new NullPointerException("content == null"); + + return new Body() { + @Override public MediaType contentType() { + return contentType; + } + + @Override public long contentLength() { + return content.length; + } + + @Override public void writeTo(OutputStream out) throws IOException { + out.write(content); + } + }; + } + + /** Returns a new request body that transmits the content of {@code file}. */ + public static Body create(final MediaType contentType, final File file) { + if (contentType == null) throw new NullPointerException("contentType == null"); + if (file == null) throw new NullPointerException("content == null"); + + return new Body() { + @Override public MediaType contentType() { + return contentType; + } + + @Override public long contentLength() { + return file.length(); + } + + @Override public void writeTo(OutputStream out) throws IOException { + long length = contentLength(); + if (length == 0) return; + + InputStream in = null; + try { + in = new FileInputStream(file); + byte[] buffer = new byte[(int) Math.min(8192, length)]; + for (int c; (c = in.read(buffer)) != -1; ) { + out.write(buffer, 0, c); + } + } finally { + Util.closeQuietly(in); + } + } + }; + } } public static class Builder { diff --git a/okhttp/src/test/java/com/squareup/okhttp/RequestTest.java b/okhttp/src/test/java/com/squareup/okhttp/RequestTest.java new file mode 100644 index 000000000..ed43e194a --- /dev/null +++ b/okhttp/src/test/java/com/squareup/okhttp/RequestTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * 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.squareup.okhttp; + +import com.squareup.okhttp.internal.Util; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public final class RequestTest { + @Test public void string() throws Exception { + MediaType contentType = MediaType.parse("text/plain; charset=utf-8"); + Request.Body body = Request.Body.create(contentType, "abc".getBytes(Util.UTF_8)); + assertEquals(contentType, body.contentType()); + assertEquals(3, body.contentLength()); + assertEquals("616263", bodyToHex(body)); + assertEquals("Retransmit body", "616263", bodyToHex(body)); + } + + @Test public void stringWithDefaultCharsetAdded() throws Exception { + MediaType contentType = MediaType.parse("text/plain"); + Request.Body body = Request.Body.create(contentType, "\u0800"); + assertEquals(MediaType.parse("text/plain; charset=utf-8"), body.contentType()); + assertEquals(3, body.contentLength()); + assertEquals("e0a080", bodyToHex(body)); + } + + @Test public void stringWithNonDefaultCharsetSpecified() throws Exception { + MediaType contentType = MediaType.parse("text/plain; charset=utf-16be"); + Request.Body body = Request.Body.create(contentType, "\u0800"); + assertEquals(contentType, body.contentType()); + assertEquals(2, body.contentLength()); + assertEquals("0800", bodyToHex(body)); + } + + @Test public void byteArray() throws Exception { + MediaType contentType = MediaType.parse("text/plain"); + Request.Body body = Request.Body.create(contentType, "abc".getBytes(Util.UTF_8)); + assertEquals(contentType, body.contentType()); + assertEquals(3, body.contentLength()); + assertEquals("616263", bodyToHex(body)); + assertEquals("Retransmit body", "616263", bodyToHex(body)); + } + + @Test public void file() throws Exception { + File file = File.createTempFile("RequestTest", "tmp"); + FileWriter writer = new FileWriter(file); + writer.write("abc"); + writer.close(); + + MediaType contentType = MediaType.parse("text/plain"); + Request.Body body = Request.Body.create(contentType, file); + assertEquals(contentType, body.contentType()); + assertEquals(3, body.contentLength()); + assertEquals("616263", bodyToHex(body)); + assertEquals("Retransmit body", "616263", bodyToHex(body)); + } + + private String bodyToHex(Request.Body body) throws IOException { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + body.writeTo(bytes); + return bytesToHex(bytes.toByteArray()); + } + + private String bytesToHex(byte[] bytes) { + StringBuilder hex = new StringBuilder(); + for (byte b : bytes) { + if ((b & 0xff) < 0x10) hex.append('0'); + hex.append(Integer.toHexString(b & 0xff)); + } + return hex.toString(); + } +} diff --git a/okhttp/src/test/java/com/squareup/okhttp/internal/AsyncApiTest.java b/okhttp/src/test/java/com/squareup/okhttp/internal/AsyncApiTest.java index 6eefcf186..163b63912 100644 --- a/okhttp/src/test/java/com/squareup/okhttp/internal/AsyncApiTest.java +++ b/okhttp/src/test/java/com/squareup/okhttp/internal/AsyncApiTest.java @@ -17,11 +17,14 @@ package com.squareup.okhttp.internal; import com.google.mockwebserver.MockResponse; import com.google.mockwebserver.MockWebServer; +import com.google.mockwebserver.RecordedRequest; +import com.squareup.okhttp.MediaType; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.Request; import org.junit.After; import org.junit.Test; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; public final class AsyncApiTest { @@ -51,4 +54,22 @@ public final class AsyncApiTest { assertTrue(server.takeRequest().getHeaders().contains("User-Agent: AsyncApiTest")); } + + @Test public void post() throws Exception { + server.enqueue(new MockResponse().setBody("abc")); + server.play(); + + Request request = new Request.Builder(server.getUrl("/")) + .post(Request.Body.create(MediaType.parse("text/plain"), "def")) + .build(); + client.enqueue(request, receiver); + + receiver.await(request) + .assertCode(200) + .assertBody("abc"); + + RecordedRequest recordedRequest = server.takeRequest(); + assertEquals("def", recordedRequest.getUtf8Body()); + assertEquals("3", recordedRequest.getHeader("Content-Length")); + } }