1
16 package okio;
17
18 import java.io.File;
19 import java.io.FileInputStream;
20 import java.io.FileNotFoundException;
21 import java.io.FileOutputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.InterruptedIOException;
25 import java.io.OutputStream;
26 import java.net.Socket;
27 import java.net.SocketTimeoutException;
28 import java.nio.file.Files;
29 import java.nio.file.OpenOption;
30 import java.nio.file.Path;
31 import java.util.logging.Level;
32 import java.util.logging.Logger;
33 import javax.annotation.Nullable;
34 import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
35
36 import static okio.Util.checkOffsetAndCount;
37
38
39 public final class Okio {
40 static final Logger logger = Logger.getLogger(Okio.class.getName());
41
42 private Okio() {
43 }
44
45
50 public static BufferedSource buffer(Source source) {
51 return new RealBufferedSource(source);
52 }
53
54
59 public static BufferedSink buffer(Sink sink) {
60 return new RealBufferedSink(sink);
61 }
62
63
64 public static Sink sink(OutputStream out) {
65 return sink(out, new Timeout());
66 }
67
68 private static Sink sink(final OutputStream out, final Timeout timeout) {
69 if (out == null) throw new IllegalArgumentException("out == null");
70 if (timeout == null) throw new IllegalArgumentException("timeout == null");
71
72 return new Sink() {
73 @Override public void write(Buffer source, long byteCount) throws IOException {
74 checkOffsetAndCount(source.size, 0, byteCount);
75 while (byteCount > 0) {
76 timeout.throwIfReached();
77 Segment head = source.head;
78 int toCopy = (int) Math.min(byteCount, head.limit - head.pos);
79 out.write(head.data, head.pos, toCopy);
80
81 head.pos += toCopy;
82 byteCount -= toCopy;
83 source.size -= toCopy;
84
85 if (head.pos == head.limit) {
86 source.head = head.pop();
87 SegmentPool.recycle(head);
88 }
89 }
90 }
91
92 @Override public void flush() throws IOException {
93 out.flush();
94 }
95
96 @Override public void close() throws IOException {
97 out.close();
98 }
99
100 @Override public Timeout timeout() {
101 return timeout;
102 }
103
104 @Override public String toString() {
105 return "sink(" + out + ")";
106 }
107 };
108 }
109
110
115 public static Sink sink(Socket socket) throws IOException {
116 if (socket == null) throw new IllegalArgumentException("socket == null");
117 if (socket.getOutputStream() == null) throw new IOException("socket's output stream == null");
118 AsyncTimeout timeout = timeout(socket);
119 Sink sink = sink(socket.getOutputStream(), timeout);
120 return timeout.sink(sink);
121 }
122
123
124 public static Source source(InputStream in) {
125 return source(in, new Timeout());
126 }
127
128 private static Source source(final InputStream in, final Timeout timeout) {
129 if (in == null) throw new IllegalArgumentException("in == null");
130 if (timeout == null) throw new IllegalArgumentException("timeout == null");
131
132 return new Source() {
133 @Override public long read(Buffer sink, long byteCount) throws IOException {
134 if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
135 if (byteCount == 0) return 0;
136 try {
137 timeout.throwIfReached();
138 Segment tail = sink.writableSegment(1);
139 int maxToCopy = (int) Math.min(byteCount, Segment.SIZE - tail.limit);
140 int bytesRead = in.read(tail.data, tail.limit, maxToCopy);
141 if (bytesRead == -1) return -1;
142 tail.limit += bytesRead;
143 sink.size += bytesRead;
144 return bytesRead;
145 } catch (AssertionError e) {
146 if (isAndroidGetsocknameError(e)) throw new IOException(e);
147 throw e;
148 }
149 }
150
151 @Override public void close() throws IOException {
152 in.close();
153 }
154
155 @Override public Timeout timeout() {
156 return timeout;
157 }
158
159 @Override public String toString() {
160 return "source(" + in + ")";
161 }
162 };
163 }
164
165
166 public static Source source(File file) throws FileNotFoundException {
167 if (file == null) throw new IllegalArgumentException("file == null");
168 return source(new FileInputStream(file));
169 }
170
171
172 @IgnoreJRERequirement
173 public static Source source(Path path, OpenOption... options) throws IOException {
174 if (path == null) throw new IllegalArgumentException("path == null");
175 return source(Files.newInputStream(path, options));
176 }
177
178
179 public static Sink sink(File file) throws FileNotFoundException {
180 if (file == null) throw new IllegalArgumentException("file == null");
181 return sink(new FileOutputStream(file));
182 }
183
184
185 public static Sink appendingSink(File file) throws FileNotFoundException {
186 if (file == null) throw new IllegalArgumentException("file == null");
187 return sink(new FileOutputStream(file, true));
188 }
189
190
191 @IgnoreJRERequirement
192 public static Sink sink(Path path, OpenOption... options) throws IOException {
193 if (path == null) throw new IllegalArgumentException("path == null");
194 return sink(Files.newOutputStream(path, options));
195 }
196
197
198 public static Sink blackhole() {
199 return new Sink() {
200 @Override public void write(Buffer source, long byteCount) throws IOException {
201 source.skip(byteCount);
202 }
203
204 @Override public void flush() throws IOException {
205 }
206
207 @Override public Timeout timeout() {
208 return Timeout.NONE;
209 }
210
211 @Override public void close() throws IOException {
212 }
213 };
214 }
215
216
221 public static Source source(Socket socket) throws IOException {
222 if (socket == null) throw new IllegalArgumentException("socket == null");
223 if (socket.getInputStream() == null) throw new IOException("socket's input stream == null");
224 AsyncTimeout timeout = timeout(socket);
225 Source source = source(socket.getInputStream(), timeout);
226 return timeout.source(source);
227 }
228
229 private static AsyncTimeout timeout(final Socket socket) {
230 return new AsyncTimeout() {
231 @Override protected IOException newTimeoutException(@Nullable IOException cause) {
232 InterruptedIOException ioe = new SocketTimeoutException("timeout");
233 if (cause != null) {
234 ioe.initCause(cause);
235 }
236 return ioe;
237 }
238
239 @Override protected void timedOut() {
240 try {
241 socket.close();
242 } catch (Exception e) {
243 logger.log(Level.WARNING, "Failed to close timed out socket " + socket, e);
244 } catch (AssertionError e) {
245 if (isAndroidGetsocknameError(e)) {
246
247
248 logger.log(Level.WARNING, "Failed to close timed out socket " + socket, e);
249 } else {
250 throw e;
251 }
252 }
253 }
254 };
255 }
256
257
261 static boolean isAndroidGetsocknameError(AssertionError e) {
262 return e.getCause() != null && e.getMessage() != null
263 && e.getMessage().contains("getsockname failed");
264 }
265 }
266