1 /*
2 * Copyright (C) 2014 Square, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package okhttp3;
17
18 import java.io.File;
19 import java.io.IOException;
20 import java.nio.charset.Charset;
21 import javax.annotation.Nullable;
22 import okhttp3.internal.Util;
23 import okio.BufferedSink;
24 import okio.ByteString;
25 import okio.Okio;
26 import okio.Source;
27
28 import static java.nio.charset.StandardCharsets.UTF_8;
29
30 public abstract class RequestBody {
31 /** Returns the Content-Type header for this body. */
32 public abstract @Nullable MediaType contentType();
33
34 /**
35 * Returns the number of bytes that will be written to {@code sink} in a call to {@link #writeTo},
36 * or -1 if that count is unknown.
37 */
38 public long contentLength() throws IOException {
39 return -1;
40 }
41
42 /** Writes the content of this request to {@code sink}. */
43 public abstract void writeTo(BufferedSink sink) throws IOException;
44
45 /**
46 * A duplex request body is special in how it is <strong>transmitted</strong> on the network and
47 * in the <strong>API contract</strong> between OkHttp and the application.
48 *
49 * <p>This method returns false unless it is overridden by a subclass.
50 *
51 * <h3>Duplex Transmission</h3>
52 *
53 * <p>With regular HTTP calls the request always completes sending before the response may begin
54 * receiving. With duplex the request and response may be interleaved! That is, request body bytes
55 * may be sent after response headers or body bytes have been received.
56 *
57 * <p>Though any call may be initiated as a duplex call, only web servers that are specially
58 * designed for this nonstandard interaction will use it. As of 2019-01, the only widely-used
59 * implementation of this pattern is <a
60 * href="https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md">gRPC</a>.
61 *
62 * <p>Because the encoding of interleaved data is not well-defined for HTTP/1, duplex request
63 * bodies may only be used with HTTP/2. Calls to HTTP/1 servers will fail before the HTTP request
64 * is transmitted. If you cannot ensure that your client and server both support HTTP/2, do not
65 * use this feature.
66 *
67 * <p>Duplex APIs</p>
68 *
69 * <p>With regular request bodies it is not legal to write bytes to the sink passed to {@link
70 * RequestBody#writeTo} after that method returns. For duplex requests bodies that condition is
71 * lifted. Such writes occur on an application-provided thread and may occur concurrently with
72 * reads of the {@link ResponseBody}. For duplex request bodies, {@link #writeTo} should return
73 * quickly, possibly by handing off the provided request body to another thread to perform
74 * writing.
75 */
76 public boolean isDuplex() {
77 return false;
78 }
79
80 /**
81 * Returns true if this body expects at most one call to {@link #writeTo} and can be transmitted
82 * at most once. This is typically used when writing the request body is destructive and it is not
83 * possible to recreate the request body after it has been sent.
84 *
85 * <p>This method returns false unless it is overridden by a subclass.
86 *
87 * <p>By default OkHttp will attempt to retransmit request bodies when the original request fails
88 * due to a stale connection, a client timeout (HTTP 408), a satisfied authorization challenge
89 * (HTTP 401 and 407), or a retryable server failure (HTTP 503 with a {@code Retry-After: 0}
90 * header).
91 */
92 public boolean isOneShot() {
93 return false;
94 }
95
96 /**
97 * Returns a new request body that transmits {@code content}. If {@code contentType} is non-null
98 * and lacks a charset, this will use UTF-8.
99 */
100 public static RequestBody create(@Nullable MediaType contentType, String content) {
101 Charset charset = UTF_8;
102 if (contentType != null) {
103 charset = contentType.charset();
104 if (charset == null) {
105 charset = UTF_8;
106 contentType = MediaType.parse(contentType + "; charset=utf-8");
107 }
108 }
109 byte[] bytes = content.getBytes(charset);
110 return create(contentType, bytes);
111 }
112
113 /** Returns a new request body that transmits {@code content}. */
114 public static RequestBody create(
115 final @Nullable MediaType contentType, final ByteString content) {
116 return new RequestBody() {
117 @Override public @Nullable MediaType contentType() {
118 return contentType;
119 }
120
121 @Override public long contentLength() throws IOException {
122 return content.size();
123 }
124
125 @Override public void writeTo(BufferedSink sink) throws IOException {
126 sink.write(content);
127 }
128 };
129 }
130
131 /** Returns a new request body that transmits {@code content}. */
132 public static RequestBody create(final @Nullable MediaType contentType, final byte[] content) {
133 return create(contentType, content, 0, content.length);
134 }
135
136 /** Returns a new request body that transmits {@code content}. */
137 public static RequestBody create(final @Nullable MediaType contentType, final byte[] content,
138 final int offset, final int byteCount) {
139 if (content == null) throw new NullPointerException("content == null");
140 Util.checkOffsetAndCount(content.length, offset, byteCount);
141 return new RequestBody() {
142 @Override public @Nullable MediaType contentType() {
143 return contentType;
144 }
145
146 @Override public long contentLength() {
147 return byteCount;
148 }
149
150 @Override public void writeTo(BufferedSink sink) throws IOException {
151 sink.write(content, offset, byteCount);
152 }
153 };
154 }
155
156 /** Returns a new request body that transmits the content of {@code file}. */
157 public static RequestBody create(final @Nullable MediaType contentType, final File file) {
158 if (file == null) throw new NullPointerException("file == null");
159
160 return new RequestBody() {
161 @Override public @Nullable MediaType contentType() {
162 return contentType;
163 }
164
165 @Override public long contentLength() {
166 return file.length();
167 }
168
169 @Override public void writeTo(BufferedSink sink) throws IOException {
170 try (Source source = Okio.source(file)) {
171 sink.writeAll(source);
172 }
173 }
174 };
175 }
176 }
177