1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package jdk.incubator.http; 24 25 import java.io.IOException; 26 import java.io.UnsupportedEncodingException; 27 import java.nio.ByteBuffer; 28 import java.nio.channels.SocketChannel; 29 import java.util.Optional; 30 import java.util.concurrent.CompletableFuture; 31 import org.testng.annotations.Test; 32 import jdk.incubator.http.internal.common.ByteBufferReference; 33 34 @Test 35 public class ResponseHeadersTest { 36 37 static final String BODY = 38 "This is the body dude,\r\n" 39 + "not a header!\r\n"; 40 41 static final String MESSAGE_OK = 42 "HTTP/1.1 200 OK\r\n" 43 + "Content-Length: " + BODY.length() + "\r\n" 44 + "MY-Folding-Header: YES\r\n" 45 + " OR\r\n" 46 + " NO\r\n" 47 + "\r\n" 48 + BODY; 49 50 static final String MESSAGE_NOK = 51 "HTTP/1.1 101 Switching Protocols\r\n" 52 + "\r\n"; 53 54 //public static void main(String[] args) throws IOException { 55 // new ResponseHeadersTest().test(); 56 //} 57 58 @Test 59 public void test() throws IOException { 60 testResponseHeaders(MESSAGE_OK); 61 testResponseHeaders(MESSAGE_NOK); 62 } 63 64 /** 65 * Verifies that ResponseHeaders behave as we expect. 66 * @param msg The response string. 67 * @throws IOException should not happen. 68 */ 69 static void testResponseHeaders(String msg) throws IOException { 70 byte[] bytes = msg.getBytes("US-ASCII"); 71 ByteBuffer buffer = ByteBuffer.wrap(bytes); 72 73 // Read status line 74 String statusLine = readStatusLine(buffer); 75 System.out.println("StatusLine: " + statusLine); 76 if (!statusLine.startsWith("HTTP/1.1")) { 77 throw new AssertionError("bad status line: " + statusLine); 78 } 79 80 // We have two cases: 81 // - MESSAGE_OK: there will be some headers to read, 82 // - MESSAGE_NOK: there will be no headers to read. 83 HttpHeaders headers = createResponseHeaders(buffer); 84 85 // Now get the expected length of the body 86 Optional<String> contentLengthValue = headers.firstValue("Content-length"); 87 int contentLength = contentLengthValue.map(Integer::parseInt).orElse(0); 88 89 // We again have two cases: 90 // - MESSAGE_OK: there should be a Content-length: header 91 // - MESSAGE_NOK: there should be no Content-length: header 92 if (contentLengthValue.isPresent()) { 93 // MESSAGE_NOK has no headers and no body and therefore 94 // no Content-length: header. 95 if (MESSAGE_NOK.equals(msg)) { 96 throw new AssertionError("Content-length: header found in" 97 + " error 101 message"); 98 } 99 } else { 100 if (!MESSAGE_NOK.equals(msg)) { 101 throw new AssertionError("Content-length: header not found"); 102 } 103 } 104 105 // Now read the remaining bytes. It should either be 106 // the empty string (MESSAGE_NOK) or BODY (MESSAGE_OK), 107 // and it should not contains any leading CR or LF" 108 String remaining = readRemainingBytes(buffer); 109 System.out.println("Body: <<<" + remaining + ">>>"); 110 if (remaining.length() != contentLength) { 111 throw new AssertionError("Unexpected body length: " + remaining.length() 112 + " expected " + contentLengthValue); 113 } 114 if (contentLengthValue.isPresent()) { 115 if (!BODY.equals(remaining)) { 116 throw new AssertionError("Body does not match!"); 117 } 118 } 119 } 120 121 static String readRemainingBytes(ByteBuffer buffer) throws UnsupportedEncodingException { 122 byte[] res = new byte[buffer.limit() - buffer.position()]; 123 System.arraycopy(buffer.array(), buffer.position(), res, 0, res.length); 124 buffer.position(buffer.limit()); 125 return new String(res, "US-ASCII"); 126 } 127 128 static String readStatusLine(ByteBuffer buffer) throws IOException { 129 buffer.mark(); 130 int p = buffer.position(); 131 while(buffer.hasRemaining()) { 132 char c = (char)buffer.get(); 133 if (c == '\r') { 134 c = (char)buffer.get(); 135 if (c == '\n') { 136 byte[] res = new byte[buffer.position() - p -2]; 137 System.arraycopy(buffer.array(), p, res, 0, res.length); 138 return new String(res, "US-ASCII"); 139 } 140 } 141 } 142 throw new IOException("Status line not found"); 143 } 144 145 private static final class HttpConnectionStub extends HttpConnection { 146 public HttpConnectionStub() { 147 super(null, null); 148 } 149 @Override 150 public void connect() throws IOException, InterruptedException { 151 throw new AssertionError("Bad test assumption: should not have reached here!"); 152 } 153 @Override 154 public CompletableFuture<Void> connectAsync() { 155 throw new AssertionError("Bad test assumption: should not have reached here!"); 156 } 157 @Override 158 boolean connected() { 159 throw new AssertionError("Bad test assumption: should not have reached here!"); 160 } 161 @Override 162 boolean isSecure() { 163 throw new AssertionError("Bad test assumption: should not have reached here!"); 164 } 165 @Override 166 boolean isProxied() { 167 throw new AssertionError("Bad test assumption: should not have reached here!"); 168 } 169 @Override 170 CompletableFuture<Void> whenReceivingResponse() { 171 throw new AssertionError("Bad test assumption: should not have reached here!"); 172 } 173 @Override 174 SocketChannel channel() { 175 throw new AssertionError("Bad test assumption: should not have reached here!"); 176 } 177 @Override 178 ConnectionPool.CacheKey cacheKey() { 179 throw new AssertionError("Bad test assumption: should not have reached here!"); 180 } 181 @Override 182 long write(ByteBuffer[] buffers, int start, int number) throws IOException { 183 throw new AssertionError("Bad test assumption: should not have reached here!"); 184 } 185 @Override 186 long write(ByteBuffer buffer) throws IOException { 187 throw new AssertionError("Bad test assumption: should not have reached here!"); 188 } 189 @Override 190 void writeAsync(ByteBufferReference[] buffers) throws IOException { 191 throw new AssertionError("Bad test assumption: should not have reached here!"); 192 } 193 @Override 194 void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException { 195 throw new AssertionError("Bad test assumption: should not have reached here!"); 196 } 197 @Override 198 void flushAsync() throws IOException { 199 throw new AssertionError("Bad test assumption: should not have reached here!"); 200 } 201 @Override 202 public void close() { 203 throw new AssertionError("Bad test assumption: should not have reached here!"); 204 } 205 @Override 206 void shutdownInput() throws IOException { 207 throw new AssertionError("Bad test assumption: should not have reached here!"); 208 } 209 @Override 210 void shutdownOutput() throws IOException { 211 throw new AssertionError("Bad test assumption: should not have reached here!"); 212 } 213 @Override 214 protected ByteBuffer readImpl() throws IOException { 215 throw new AssertionError("Bad test assumption: should not have reached here!"); 216 } 217 } 218 219 public static HttpHeaders createResponseHeaders(ByteBuffer buffer) 220 throws IOException{ 221 return new ResponseHeaders(new HttpConnectionStub(), buffer); 222 } 223 }