| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * 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 java.net; |
| |
| import java.net.*; |
| import java.io.BufferedReader; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.OutputStream; |
| import java.util.Arrays; |
| import java.util.concurrent.atomic.AtomicInteger; |
| import tests.support.Support_PortManager; |
| import tests.support.Support_TestWebServer; |
| |
| public class URLConnectionTest extends junit.framework.TestCase { |
| private int mPort; |
| private Support_TestWebServer mServer; |
| |
| @Override |
| public void setUp() throws Exception { |
| super.setUp(); |
| mPort = Support_PortManager.getNextPort(); |
| mServer = new Support_TestWebServer(); |
| mServer.initServer(mPort, true); |
| } |
| |
| @Override |
| public void tearDown() throws Exception { |
| super.tearDown(); |
| mServer.close(); |
| } |
| |
| private String readFirstLine() throws Exception { |
| URLConnection connection = new URL("http://localhost:" + mPort + "/test1").openConnection(); |
| BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); |
| String result = in.readLine(); |
| in.close(); |
| return result; |
| } |
| |
| // Check that if we don't read to the end of a response, the next request on the |
| // recycled connection doesn't get the unread tail of the first request's response. |
| // http://code.google.com/p/android/issues/detail?id=2939 |
| public void test_2939() throws Exception { |
| mServer.setChunked(true); |
| mServer.setMaxChunkSize(8); |
| assertTrue(readFirstLine().equals("<html>")); |
| assertTrue(readFirstLine().equals("<html>")); |
| } |
| |
| enum UploadKind { CHUNKED, FIXED_LENGTH }; |
| enum WriteKind { BYTE_BY_BYTE, SMALL_BUFFERS, LARGE_BUFFERS }; |
| |
| public void test_chunkedUpload_byteByByte() throws Exception { |
| doUpload(UploadKind.CHUNKED, WriteKind.BYTE_BY_BYTE); |
| } |
| |
| public void test_chunkedUpload_smallBuffers() throws Exception { |
| doUpload(UploadKind.CHUNKED, WriteKind.SMALL_BUFFERS); |
| } |
| |
| public void test_chunkedUpload_largeBuffers() throws Exception { |
| doUpload(UploadKind.CHUNKED, WriteKind.LARGE_BUFFERS); |
| } |
| |
| public void test_fixedLengthUpload_byteByByte() throws Exception { |
| doUpload(UploadKind.FIXED_LENGTH, WriteKind.BYTE_BY_BYTE); |
| } |
| |
| public void test_fixedLengthUpload_smallBuffers() throws Exception { |
| doUpload(UploadKind.FIXED_LENGTH, WriteKind.SMALL_BUFFERS); |
| } |
| |
| public void test_fixedLengthUpload_largeBuffers() throws Exception { |
| doUpload(UploadKind.FIXED_LENGTH, WriteKind.LARGE_BUFFERS); |
| } |
| |
| private void doUpload(UploadKind uploadKind, WriteKind writeKind) throws Exception { |
| int n = 512*1024; |
| AtomicInteger total = new AtomicInteger(0); |
| ServerSocket ss = startSinkServer(total); |
| URL url = new URL("http://localhost:" + ss.getLocalPort() + "/test1"); |
| HttpURLConnection conn = (HttpURLConnection) url.openConnection(); |
| conn.setDoOutput(true); |
| conn.setRequestMethod("POST"); |
| if (uploadKind == UploadKind.CHUNKED) { |
| conn.setChunkedStreamingMode(-1); |
| } else { |
| conn.setFixedLengthStreamingMode(n); |
| } |
| OutputStream out = conn.getOutputStream(); |
| if (writeKind == WriteKind.BYTE_BY_BYTE) { |
| for (int i = 0; i < n; ++i) { |
| out.write('x'); |
| } |
| } else { |
| byte[] buf = new byte[writeKind == WriteKind.SMALL_BUFFERS ? 256 : 64*1024]; |
| Arrays.fill(buf, (byte) 'x'); |
| for (int i = 0; i < n; i += buf.length) { |
| out.write(buf, 0, Math.min(buf.length, n - i)); |
| } |
| } |
| out.close(); |
| assertTrue(conn.getResponseCode() > 0); |
| assertEquals(uploadKind == UploadKind.CHUNKED ? -1 : n, total.get()); |
| } |
| |
| private ServerSocket startSinkServer(final AtomicInteger totalByteCount) throws Exception { |
| final ServerSocket ss = new ServerSocket(0); |
| ss.setReuseAddress(true); |
| Thread t = new Thread(new Runnable() { |
| public void run() { |
| try { |
| Socket s = ss.accept(); |
| BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream())); |
| int contentLength = -1; |
| String line; |
| int emptyLineCount = 0; |
| // read the headers |
| while ((line = in.readLine()) != null) { |
| if (contentLength == -1 && line.toLowerCase().startsWith("content-length: ")) { |
| contentLength = Integer.parseInt(line.substring(16)); |
| } |
| if (line.length() == 0) { |
| ++emptyLineCount; |
| // If we had a content length, the first empty line we see marks the |
| // start of the payload. The loop below then skips over that. |
| // If we didn't get a content length, we're using chunked encoding. |
| // The first empty line again marks the start of the payload, and the |
| // second empty line is a consequence of both the last chunk ending |
| // CRLF and the chunked-body itself ending with a CRLF. (The fact that |
| // a chunk of size 0 is used to mark the end isn't sufficient because |
| // there may also be a "trailer": header fields deferred until after |
| // the payload.) |
| if (contentLength != -1 || emptyLineCount == 2) { |
| break; |
| } |
| } |
| } |
| // Skip the payload in the setFixedLengthStreamingMode case. |
| // In the chunked case, we read all the chunked data in the loop above. |
| long left = contentLength; |
| while (left > 0) { |
| left -= in.skip(left); |
| } |
| // Send a response to unblock the client. |
| totalByteCount.set(contentLength); |
| OutputStream out = s.getOutputStream(); |
| out.write("HTTP/1.1 200 OK\r\n\r\n".getBytes()); |
| out.flush(); |
| out.close(); |
| // Check there wasn't junk at the end. |
| try { |
| assertEquals(-1, in.read()); |
| } catch (SocketException expected) { |
| // The client already closed the connection. |
| } |
| } catch (Exception ex) { |
| throw new RuntimeException("server died unexpectedly", ex); |
| } |
| } |
| }); |
| t.start(); |
| return ss; |
| } |
| } |