// ======================================================================== // Copyright (c) Webtide LLC // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.apache.org/licenses/LICENSE-2.0.txt // // You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.test.support.rawhttp; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import junit.framework.Assert; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeaders; import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.View; import org.eclipse.jetty.test.support.StringUtil; import org.eclipse.jetty.toolchain.test.StringAssert; import org.eclipse.jetty.util.ByteArrayOutputStream2; /** * Assists in testing of HTTP Responses. */ public class HttpResponseTester { private class PH extends HttpParser.EventHandler { @Override public void content(Buffer ref) throws IOException { if (content == null) content = new ByteArrayOutputStream2(); content.write(ref.asArray()); } @Override public void headerComplete() throws IOException { contentType = fields.get(HttpHeaders.CONTENT_TYPE_BUFFER); if (contentType != null) { String calcCharset = MimeTypes.getCharsetFromContentType(contentType); if (calcCharset != null) { charset = calcCharset; } } } @Override public void messageComplete(long contextLength) throws IOException { } @Override public void parsedHeader(Buffer name, Buffer value) throws IOException { fields.add(name,value); } @Override public void startRequest(Buffer method, Buffer url, Buffer version) throws IOException { reset(); HttpResponseTester.this.method = getString(method); HttpResponseTester.this.uri = getString(url); HttpResponseTester.this.version = getString(version); } @Override public void startResponse(Buffer version, int status, Buffer reason) throws IOException { reset(); HttpResponseTester.this.version = getString(version); HttpResponseTester.this.status = status; HttpResponseTester.this.reason = getString(reason); } } public static List<HttpResponseTester> parseMulti(CharSequence rawHTTP) throws IOException { List<HttpResponseTester> responses = new ArrayList<HttpResponseTester>(); String parse = rawHTTP.toString(); while (StringUtil.isNotBlank(parse)) { HttpResponseTester response = new HttpResponseTester(); parse = response.parse(parse); responses.add(response); } return responses; } private HttpFields fields = new HttpFields(); private CharSequence rawResponse; private String method; private String uri; private String version; private int status; private String reason; private Buffer contentType; private ByteArrayOutputStream2 content; private String charset; private String defaultCharset; public HttpResponseTester() { this("UTF-8"); } public HttpResponseTester(String defCharset) { this.defaultCharset = defCharset; } public String getMethod() { return method; } public String getURI() { return uri; } public String getVersion() { return version; } public int getStatus() { return status; } public CharSequence getRawResponse() { return rawResponse; } public String getReason() { return reason; } public String getContentType() { if (contentType == null) { return null; } return contentType.toString(); } public ByteArrayOutputStream2 getContentBytes() { return content; } public String getContent() { return content.toString(); } public String getBody() { return content.toString(); } private byte[] getByteArray(CharSequence str) { if (charset == null) { return str.toString().getBytes(); } try { return str.toString().getBytes(charset); } catch (Exception e) { return str.toString().getBytes(); } } private String getString(Buffer buffer) { return getString(buffer.asArray()); } private String getString(byte[] b) { if (charset == null) { return new String(b); } try { return new String(b,charset); } catch (Exception e) { return new String(b); } } /** * @param name * @return the header value as a date * @see org.eclipse.jetty.http.HttpFields#getDateField(java.lang.String) */ public long getDateHeader(String name) { return fields.getDateField(name); } /** * @param name * @return the header value as a long * @throws NumberFormatException * @see org.eclipse.jetty.http.HttpFields#getLongField(java.lang.String) */ public long getLongHeader(String name) throws NumberFormatException { return fields.getLongField(name); } /** * @param name * @return the header value * @see org.eclipse.jetty.http.HttpFields#getStringField(java.lang.String) */ public String getHeader(String name) { return fields.getStringField(name); } public boolean hasHeader(String headerKey) { return fields.containsKey(headerKey); } /** * Parse on HTTP Response * * @param rawHTTP * Raw HTTP to parse * @return Any unparsed data in the rawHTTP (eg pipelined requests) * @throws IOException */ public String parse(CharSequence rawHTTP) throws IOException { this.charset = defaultCharset; this.rawResponse = rawHTTP; ByteArrayBuffer buf = new ByteArrayBuffer(getByteArray(rawHTTP)); View view = new View(buf); HttpParser parser = new HttpParser(view,new PH()); parser.parse(); return getString(view.asArray()); } public void reset() { fields.clear(); method = null; uri = null; version = null; status = 0; reason = null; content = null; } /** * Make sure that status code is "OK" */ public void assertStatusOK() { assertStatus(HttpStatus.OK_200,"OK"); } public void assertStatusOK(String msg) { assertStatus(msg,HttpStatus.OK_200,"OK"); } public void assertStatus(int expectedStatus, String expectedReason) { Assert.assertEquals("Response.status",expectedStatus,this.status); Assert.assertEquals("Response.reason",expectedReason,this.reason); } public void assertStatus(String msg, int expectedStatus, String expectedReason) { Assert.assertEquals(msg + ": Response.status",expectedStatus,this.status); Assert.assertEquals(msg + ": Response.reason",expectedReason,this.reason); } public void assertStatus(String msg, int expectedStatus) { assertStatus(msg,expectedStatus,HttpStatus.getMessage(expectedStatus)); } public void assertContentType(String expectedType) { assertHeader("Content-Type",expectedType); } private void assertHeader(String headerKey, String expectedValue) { String actual = fields.getStringField(headerKey); Assert.assertNotNull("Response[" + headerKey + "] should not be null",actual); Assert.assertEquals("Response[" + headerKey + "]",expectedValue,actual); } public void assertHeader(String msg, String headerKey, String expectedValue) { String actual = fields.getStringField(headerKey); Assert.assertNotNull(msg + ": Response[" + headerKey + "] should not be null, expecting <" + expectedValue + ">",actual); Assert.assertEquals(msg + ": Response[" + headerKey + "]",expectedValue,actual); } public void assertBody(String expected) { Assert.assertNotNull("Response.content should not be null",this.content); String actual = this.content.toString(); Assert.assertEquals("Response.content",expected,actual); } public void assertBody(String msg, String expected) { Assert.assertNotNull(msg + ": Response.content should not be null",this.content); String actual = this.content.toString(); Assert.assertEquals(msg + ": Response.content",expected,actual); } public void assertNoBody(String msg) { Assert.assertNull(msg + ": Response.content should be null",this.content); } public void assertBodyContains(String msg, String expectedNeedle) { StringAssert.assertContains(msg + ": Response Content",this.content.toString(),expectedNeedle); } public void assertHeaderExists(String msg, String expectedHeaderKey) { Assert.assertTrue(msg + ": header <" + expectedHeaderKey + "> should exist",fields.containsKey(expectedHeaderKey)); } public void assertHeaderNotPresent(String msg, String headerKey) { Assert.assertFalse(msg + ": header <" + headerKey + "> should NOT exist",fields.containsKey(headerKey)); } public List<HttpResponseTester> findBodyMultiparts(String boundary) throws IOException { List<HttpResponseTester> multiparts = new ArrayList<HttpResponseTester>(); BufferedReader buf = new BufferedReader(new StringReader(getBody())); String line; String startBoundary = "--" + boundary; String endBoundary = "--" + boundary + "--"; HttpResponseTester resp = null; boolean parsingHeader = true; boolean previousBodyLine = false; while ((line = buf.readLine()) != null) { if (line.equals(startBoundary)) { // end of multipart, start a new one. if (resp != null) { multiparts.add(resp); } resp = new HttpResponseTester(); parsingHeader = true; previousBodyLine = false; continue; } if (line.equals(endBoundary)) { if (resp != null) { multiparts.add(resp); } break; } if (parsingHeader) { if (line.equals("")) { parsingHeader = false; continue; } resp.parseHeader(line); } else { if (previousBodyLine) { resp.appendBody("\n"); } resp.appendBody(line); previousBodyLine = true; } } return multiparts; } public void parseHeader(String line) { int idx = line.indexOf(":"); String key = line.substring(0,idx).trim(); String val = line.substring(idx + 1).trim(); fields.add(key,val); } public void appendBody(String s) throws IOException { appendBody(s.getBytes()); } public void appendBody(byte buf[]) throws IOException { if (content == null) { content = new ByteArrayOutputStream2(); } content.write(buf); } }