1/* 2 * Copyright (C) 2011 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.google.mockwebserver; 18 19import java.io.UnsupportedEncodingException; 20import java.net.Socket; 21import java.security.Principal; 22import java.security.cert.Certificate; 23import java.util.ArrayList; 24import java.util.List; 25 26import javax.net.ssl.SSLPeerUnverifiedException; 27import javax.net.ssl.SSLSession; 28import javax.net.ssl.SSLSocket; 29 30/** 31 * An HTTP request that came into the mock web server. 32 */ 33public final class RecordedRequest { 34 private final String requestLine; 35 private final String method; 36 private final String path; 37 private final List<String> headers; 38 private final List<Integer> chunkSizes; 39 private final int bodySize; 40 private final byte[] body; 41 private final int sequenceNumber; 42 private final String sslProtocol; 43 private final String sslCipherSuite; 44 private final Principal sslLocalPrincipal; 45 private final Principal sslPeerPrincipal; 46 private final Certificate[] sslLocalCertificates; 47 private final Certificate[] sslPeerCertificates; 48 49 public RecordedRequest(String requestLine, List<String> headers, List<Integer> chunkSizes, 50 int bodySize, byte[] body, int sequenceNumber, Socket socket) { 51 this.requestLine = requestLine; 52 this.headers = headers; 53 this.chunkSizes = chunkSizes; 54 this.bodySize = bodySize; 55 this.body = body; 56 this.sequenceNumber = sequenceNumber; 57 58 if (socket instanceof SSLSocket) { 59 SSLSocket sslSocket = (SSLSocket) socket; 60 SSLSession session = sslSocket.getSession(); 61 sslProtocol = session.getProtocol(); 62 sslCipherSuite = session.getCipherSuite(); 63 sslLocalPrincipal = session.getLocalPrincipal(); 64 sslLocalCertificates = session.getLocalCertificates(); 65 Principal peerPrincipal = null; 66 Certificate[] peerCertificates = null; 67 try { 68 peerPrincipal = session.getPeerPrincipal(); 69 peerCertificates = session.getPeerCertificates(); 70 } catch (SSLPeerUnverifiedException e) { 71 // No-op: use nulls instead 72 } 73 sslPeerPrincipal = peerPrincipal; 74 sslPeerCertificates = peerCertificates; 75 } else { 76 sslProtocol = null; 77 sslCipherSuite = null; 78 sslLocalPrincipal = null; 79 sslLocalCertificates = null; 80 sslPeerPrincipal = null; 81 sslPeerCertificates = null; 82 } 83 84 if (requestLine != null) { 85 int methodEnd = requestLine.indexOf(' '); 86 int pathEnd = requestLine.indexOf(' ', methodEnd + 1); 87 this.method = requestLine.substring(0, methodEnd); 88 this.path = requestLine.substring(methodEnd + 1, pathEnd); 89 } else { 90 this.method = null; 91 this.path = null; 92 } 93 } 94 95 public String getRequestLine() { 96 return requestLine; 97 } 98 99 public String getMethod() { 100 return method; 101 } 102 103 public String getPath() { 104 return path; 105 } 106 107 /** 108 * Returns all headers. 109 */ 110 public List<String> getHeaders() { 111 return headers; 112 } 113 114 /** 115 * Returns the first header named {@code name}, or null if no such header 116 * exists. 117 */ 118 public String getHeader(String name) { 119 name += ":"; 120 for (String header : headers) { 121 if (name.regionMatches(true, 0, header, 0, name.length())) { 122 return header.substring(name.length()).trim(); 123 } 124 } 125 return null; 126 } 127 128 /** 129 * Returns the headers named {@code name}. 130 */ 131 public List<String> getHeaders(String name) { 132 List<String> result = new ArrayList<String>(); 133 name += ":"; 134 for (String header : headers) { 135 if (name.regionMatches(true, 0, header, 0, name.length())) { 136 result.add(header.substring(name.length()).trim()); 137 } 138 } 139 return result; 140 } 141 142 /** 143 * Returns the sizes of the chunks of this request's body, or an empty list 144 * if the request's body was empty or unchunked. 145 */ 146 public List<Integer> getChunkSizes() { 147 return chunkSizes; 148 } 149 150 /** 151 * Returns the total size of the body of this POST request (before 152 * truncation). 153 */ 154 public int getBodySize() { 155 return bodySize; 156 } 157 158 /** 159 * Returns the body of this POST request. This may be truncated. 160 */ 161 public byte[] getBody() { 162 return body; 163 } 164 165 /** 166 * Returns the body of this POST request decoded as a UTF-8 string. 167 */ 168 public String getUtf8Body() { 169 try { 170 return new String(body, "UTF-8"); 171 } catch (UnsupportedEncodingException e) { 172 throw new AssertionError(); 173 } 174 } 175 176 /** 177 * Returns the index of this request on its HTTP connection. Since a single 178 * HTTP connection may serve multiple requests, each request is assigned its 179 * own sequence number. 180 */ 181 public int getSequenceNumber() { 182 return sequenceNumber; 183 } 184 185 /** 186 * Returns the SSL connection's protocol like {@code TLSv1}, {@code SSLv3}, 187 * {@code NONE} or {@code null} if the connection doesn't use SSL. 188 */ 189 public String getSslProtocol() { 190 return sslProtocol; 191 } 192 193 /** 194 * Returns the SSL connection's cipher protocol retrieved using 195 * {@code sslSocket.getSession().getCipherSuite()} or {@code null} if the connection doesn't 196 * use SSL. 197 */ 198 public String getSslCipherSuite() { 199 return sslCipherSuite; 200 } 201 202 /** 203 * Returns the SSL connection's local principal retrieved using 204 * {@code sslSocket.getSession().getLocalPrincipal()} or {@code null} if the connection doesn't 205 * use SSL. 206 */ 207 public Principal getSslLocalPrincipal() { 208 return sslLocalPrincipal; 209 } 210 211 /** 212 * Returns the SSL connection's local certificates retrieved using 213 * {@code sslSocket.getSession().getLocalCertificates()} or {@code null} if the connection 214 * doesn't use SSL. 215 */ 216 public Certificate[] getSslLocalCertificates() { 217 return sslLocalCertificates; 218 } 219 220 /** 221 * Returns the SSL connection's peer principal retrieved using 222 * {@code sslSocket.getSession().getPeerPrincipal()}, or {@code null} if the connection doesn't 223 * use SSL or the peer has not been verified. 224 */ 225 public Principal getSslPeerPrincipal() { 226 return sslPeerPrincipal; 227 } 228 229 /** 230 * Returns the SSL connection's peer certificates retrieved using 231 * {@code sslSocket.getSession().getPeerCertificates()}, or {@code null} if the connection 232 * doesn't use SSL or the peer has not been verified. 233 */ 234 public Certificate[] getSslPeerCertificates() { 235 return sslPeerCertificates; 236 } 237 238 @Override public String toString() { 239 return "RecordedRequest {" + requestLine + "}"; 240 } 241} 242