1 /* 2 * Copyright (c) 2016, 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 24 /** 25 * @test 26 * @bug 8164704 27 * @modules jdk.incubator.httpclient 28 * jdk.httpserver 29 * java.base/sun.net.www 30 * @run main MessageHeadersTest 31 * @summary Tests expected behavior of MessageHeader. This test 32 * cannot be used to verify 8164704 - it simply verifies 33 * the assumptions on which the fix is based. 34 */ 35 36 import java.io.IOException; 37 import java.io.InputStream; 38 import java.io.UnsupportedEncodingException; 39 import java.nio.ByteBuffer; 40 41 42 public class MessageHeadersTest { 43 44 static final String BODY = 45 "This is the body dude,\r\n" 46 + "not a header!\r\n"; 47 48 static final String MESSAGE_OK = 49 "HTTP/1.1 200 OK\r\n" 50 + "Content-Length: " + BODY.length() + "\r\n" 51 + "MY-Folding-Header: YES\r\n" 52 + " OR\r\n" 53 + " NO\r\n" 54 + "\r\n" 55 + BODY; 56 57 static final String MESSAGE_NOK = 58 "HTTP/1.1 101 Switching Protocols\r\n" 59 + "\r\n"; 60 61 static final class ByteBufferInputStream extends InputStream { 62 final ByteBuffer buffer; 63 int lastRead = -1; 64 ByteBufferInputStream(ByteBuffer buffer) { 65 this.buffer = buffer; 66 } 67 @Override 68 public int read() throws IOException { 69 if (buffer.hasRemaining()) { 70 return lastRead = buffer.get(); 71 } 72 return -1; 73 } 74 } 75 76 public static void main(String[] args) throws IOException { 77 testMessageHeaders(MESSAGE_OK); 78 testMessageHeaders(MESSAGE_NOK); 79 } 80 81 /** 82 * Verifies that MessageHeader behave as we expect. 83 * @param msg The response string. 84 * @throws IOException should not happen. 85 */ 86 static void testMessageHeaders(String msg) throws IOException { 87 byte[] bytes = msg.getBytes("US-ASCII"); 88 ByteBuffer buffer = ByteBuffer.wrap(bytes); 89 90 // Read status line 91 String statusLine = readStatusLine(buffer); 92 System.out.println("StatusLine: " + statusLine); 93 if (!statusLine.startsWith("HTTP/1.1")) { 94 throw new AssertionError("bad status line: " + statusLine); 95 } 96 97 // Wrap the buffer into an input stream and pass 98 // that to MessageHeader to read the header. 99 // We have two cases: 100 // - MESSAGE_OK: there will be some headers to read, 101 // - MESSAGE_NOK: there will be no headers to read. 102 ByteBufferInputStream bbis = new ByteBufferInputStream(buffer); 103 sun.net.www.MessageHeader mh = new sun.net.www.MessageHeader(bbis); 104 105 // Now get the expected length of the body 106 String contentLengthValue = mh.findValue("Content-length"); 107 int contentLength = contentLengthValue == null ? 0 108 : Integer.parseInt(contentLengthValue); 109 110 // We again have two cases: 111 // - MESSAGE_OK: there should be a Content-length: header 112 // - MESSAGE_NOK: there should be no Content-length: header 113 if (contentLengthValue == null) { 114 // MESSAGE_NOK has no headers and no body and therefore 115 // no Content-length: header. 116 if (!MESSAGE_NOK.equals(msg)) { 117 throw new AssertionError("Content-length: header not found"); 118 } 119 // In that case we expect MessageHeader to read the CR but 120 // leave the LF in the buffer. We therefore need to consume 121 // the the LF in order to get an empty (all consumed) buffer. 122 // This is what ResponseHeaders does. 123 byte c = buffer.get(); 124 if (c != '\n' || bbis.lastRead != '\r') { 125 throw new AssertionError("Unexpected byte sequence for empty body" 126 + ": " + bbis.lastRead + " " + c + " expected " 127 + (byte)'\r' + " " + (byte)'\n'); 128 } 129 } else { 130 if (MESSAGE_NOK.equals(msg)) { 131 throw new AssertionError("Content-length: header found in" 132 + " error 101 message"); 133 } 134 } 135 136 // Now read the remaining bytes. It should either be 137 // the empty string (MESSAGE_NOK) or BODY (MESSAGE_OK), 138 // and it should not contains any leading CR or LF" 139 String remaining = readRemainingBytes(buffer); 140 System.out.println("Body: <<<" + remaining + ">>>"); 141 if (remaining.length() != contentLength) { 142 throw new AssertionError("Unexpected body length: " + remaining.length() 143 + " expected " + contentLengthValue); 144 } 145 if (contentLengthValue != null) { 146 if (!BODY.equals(remaining)) { 147 throw new AssertionError("Body does not match!"); 148 } 149 } 150 } 151 152 static String readRemainingBytes(ByteBuffer buffer) throws UnsupportedEncodingException { 153 byte[] res = new byte[buffer.limit() - buffer.position()]; 154 System.arraycopy(buffer.array(), buffer.position(), res, 0, res.length); 155 buffer.position(buffer.limit()); 156 return new String(res, "US-ASCII"); 157 } 158 159 static String readStatusLine(ByteBuffer buffer) throws IOException { 160 buffer.mark(); 161 int p = buffer.position(); 162 while(buffer.hasRemaining()) { 163 char c = (char)buffer.get(); 164 if (c == '\r') { 165 c = (char)buffer.get(); 166 if (c == '\n') { 167 byte[] res = new byte[buffer.position() - p -2]; 168 System.arraycopy(buffer.array(), p, res, 0, res.length); 169 return new String(res, "US-ASCII"); 170 } 171 } 172 } 173 throw new IOException("Status line not found"); 174 } 175 }