| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package org.chromium.sdk.internal.websocket; | |
| 6 | |
| 7 import java.io.BufferedInputStream; | |
| 8 import java.io.IOException; | |
| 9 import java.io.InputStream; | |
| 10 import java.io.OutputStream; | |
| 11 import java.net.SocketAddress; | |
| 12 import java.nio.ByteBuffer; | |
| 13 import java.nio.charset.Charset; | |
| 14 | |
| 15 import org.chromium.sdk.ConnectionLogger; | |
| 16 import org.chromium.sdk.ConnectionLogger.StreamListener; | |
| 17 import org.chromium.sdk.internal.transport.AbstractSocketWrapper; | |
| 18 import org.chromium.sdk.internal.websocket.ManualLoggingSocketWrapper.LoggableIn
put; | |
| 19 import org.chromium.sdk.internal.websocket.ManualLoggingSocketWrapper.LoggableOu
tput; | |
| 20 | |
| 21 /** | |
| 22 * A wrapper around platform socket that handles logging and closing. It allows
user to manually | |
| 23 * control what goes to socket and what is logged. This makes sense when protoco
l | |
| 24 * is not clear-text. | |
| 25 */ | |
| 26 public class ManualLoggingSocketWrapper extends | |
| 27 AbstractSocketWrapper<LoggableInput, LoggableOutput> { | |
| 28 | |
| 29 public static final Charset UTF_8_CHARSET = Charset.forName("UTF-8"); | |
| 30 | |
| 31 public ManualLoggingSocketWrapper(SocketAddress endpoint, int connectionTimeou
tMs, | |
| 32 ConnectionLogger connectionLogger, | |
| 33 WrapperFactory<LoggableInput, LoggableOutput> wrapperFactory) throws IOExc
eption { | |
| 34 super(endpoint, connectionTimeoutMs, connectionLogger, wrapperFactory); | |
| 35 } | |
| 36 | |
| 37 /** | |
| 38 * Provides access to incoming bytes and possibly logs traffic. | |
| 39 */ | |
| 40 public static abstract class LoggableInput { | |
| 41 public abstract int readByteOrEos() throws IOException; | |
| 42 public abstract byte[] readBytes(int length) throws IOException; | |
| 43 public abstract ByteBuffer readUpTo0x0D0A() throws IOException; | |
| 44 | |
| 45 public abstract void markSeparatorForLog(); | |
| 46 } | |
| 47 | |
| 48 /** | |
| 49 * Receives outgoing bytes and possibly logs traffic. Its methods allow to man
ually | |
| 50 * control what goes into socket and what goes into log. | |
| 51 */ | |
| 52 public static abstract class LoggableOutput { | |
| 53 public abstract void writeAsciiString(String string) throws IOException; | |
| 54 | |
| 55 public abstract void writeByte(byte b) throws IOException; | |
| 56 public abstract void writeByteNoLogging(byte b) throws IOException; | |
| 57 public abstract void writeByteToLog(byte b) throws IOException; | |
| 58 | |
| 59 public abstract void writeBytes(byte[] bytes) throws IOException; | |
| 60 public abstract void writeBytesToLog(byte[] bytes); | |
| 61 public abstract void writeBytesNoLogging(byte[] bytes) throws IOException; | |
| 62 | |
| 63 /** | |
| 64 * Write a string to log with a small string that may somehow annotate that
this | |
| 65 * string is not a clear-text out-take. | |
| 66 */ | |
| 67 public abstract void writeToLog(String string, String annotation) throws IOE
xception; | |
| 68 | |
| 69 public abstract void markSeparatorForLog(); | |
| 70 } | |
| 71 | |
| 72 public static abstract class FactoryBase | |
| 73 implements WrapperFactory<LoggableInput, LoggableOutput> { | |
| 74 protected static final Charset CHARSET = AbstractWsConnection.LOGGER_CHARSET
; | |
| 75 | |
| 76 @Override | |
| 77 public LoggableInput wrapInputStream(InputStream inputStream) { | |
| 78 final BufferedInputStream bufferedInputStream = new BufferedInputStream(in
putStream); | |
| 79 | |
| 80 return new LoggableInput() { | |
| 81 @Override | |
| 82 public ByteBuffer readUpTo0x0D0A() throws IOException { | |
| 83 ByteBuffer buffer = ByteBuffer.allocate(20); | |
| 84 while (true) { | |
| 85 byte b = expectByte(); | |
| 86 if (b == (byte) 0x0D) { | |
| 87 break; | |
| 88 } | |
| 89 if (!buffer.hasRemaining()) { | |
| 90 buffer.flip(); | |
| 91 ByteBuffer biggerBuffer = ByteBuffer.allocate(buffer.remaining() *
2); | |
| 92 biggerBuffer.put(buffer); | |
| 93 buffer = biggerBuffer; | |
| 94 } | |
| 95 buffer.put(b); | |
| 96 } | |
| 97 byte b2 = expectByte(); | |
| 98 if (b2 != (byte) 0x0A) { | |
| 99 throw new IOException("0x0A byte expected"); | |
| 100 } | |
| 101 buffer.flip(); | |
| 102 return buffer; | |
| 103 } | |
| 104 | |
| 105 @Override | |
| 106 public int readByteOrEos() throws IOException { | |
| 107 return bufferedInputStream.read(); | |
| 108 } | |
| 109 | |
| 110 private byte expectByte() throws IOException { | |
| 111 int next = bufferedInputStream.read(); | |
| 112 if (next == -1) { | |
| 113 throw new IOException("Unexpected EOS"); | |
| 114 } | |
| 115 return (byte) next; | |
| 116 } | |
| 117 | |
| 118 @Override | |
| 119 public byte[] readBytes(int length) throws IOException { | |
| 120 byte[] result = new byte[length]; | |
| 121 int offset = 0; | |
| 122 while (length > 0) { | |
| 123 int r = bufferedInputStream.read(result, offset, length); | |
| 124 if (r == -1) { | |
| 125 throw new IOException("Unexpected EOS"); | |
| 126 } | |
| 127 length -= r; | |
| 128 offset += r; | |
| 129 } | |
| 130 return result; | |
| 131 } | |
| 132 | |
| 133 @Override | |
| 134 public void markSeparatorForLog() { | |
| 135 } | |
| 136 }; | |
| 137 } | |
| 138 | |
| 139 @Override | |
| 140 public LoggableOutput wrapOutputStream(final OutputStream outputStream) { | |
| 141 return new LoggableOutput() { | |
| 142 @Override public void writeAsciiString(String string) throws IOException
{ | |
| 143 outputStream.write(string.getBytes(UTF_8_CHARSET)); | |
| 144 } | |
| 145 @Override public void writeByte(byte b) throws IOException { | |
| 146 outputStream.write(b); | |
| 147 } | |
| 148 @Override public void writeBytes(byte[] bytes) throws IOException { | |
| 149 outputStream.write(bytes); | |
| 150 } | |
| 151 @Override public void writeBytesToLog(byte[] bytes) { | |
| 152 } | |
| 153 @Override public void writeBytesNoLogging(byte[] bytes) throws IOExcepti
on { | |
| 154 outputStream.write(bytes); | |
| 155 } | |
| 156 @Override public void writeToLog(String string, String annotation) throw
s IOException { | |
| 157 } | |
| 158 @Override public void writeByteNoLogging(byte b) throws IOException { | |
| 159 outputStream.write(b); | |
| 160 } | |
| 161 @Override public void writeByteToLog(byte b) throws IOException { | |
| 162 } | |
| 163 @Override public void markSeparatorForLog() { | |
| 164 } | |
| 165 }; | |
| 166 } | |
| 167 | |
| 168 @Override | |
| 169 public LoggableInput wrapInputStream(final LoggableInput originalInputWrappe
r, | |
| 170 final StreamListener streamListener) { | |
| 171 return new LoggableInput() { | |
| 172 @Override | |
| 173 public ByteBuffer readUpTo0x0D0A() throws IOException { | |
| 174 ByteBuffer bytes = originalInputWrapper.readUpTo0x0D0A(); | |
| 175 String logString = | |
| 176 new String(bytes.array(), bytes.arrayOffset(), bytes.limit(), CHAR
SET) + "\r\n"; | |
| 177 streamListener.addContent(logString); | |
| 178 return bytes; | |
| 179 } | |
| 180 | |
| 181 @Override | |
| 182 public byte[] readBytes(int length) throws IOException { | |
| 183 byte[] bytes = originalInputWrapper.readBytes(length); | |
| 184 String logString = new String(bytes, CHARSET); | |
| 185 streamListener.addContent(logString); | |
| 186 return bytes; | |
| 187 } | |
| 188 | |
| 189 @Override | |
| 190 public int readByteOrEos() throws IOException { | |
| 191 int res = originalInputWrapper.readByteOrEos(); | |
| 192 if (res != -1) { | |
| 193 StringBuilder builder = new StringBuilder(4); | |
| 194 dumpByte((byte) res, builder); | |
| 195 streamListener.addContent(builder); | |
| 196 } | |
| 197 return res; | |
| 198 } | |
| 199 | |
| 200 @Override | |
| 201 public void markSeparatorForLog() { | |
| 202 streamListener.addSeparator(); | |
| 203 } | |
| 204 }; | |
| 205 } | |
| 206 | |
| 207 protected static abstract class OutputWrapperBase extends LoggableOutput { | |
| 208 private final LoggableOutput originalOutputWrapper; | |
| 209 private final StreamListener streamListener; | |
| 210 | |
| 211 public OutputWrapperBase(LoggableOutput originalOutputWrapper, | |
| 212 StreamListener streamListener) { | |
| 213 this.originalOutputWrapper = originalOutputWrapper; | |
| 214 this.streamListener = streamListener; | |
| 215 } | |
| 216 | |
| 217 @Override | |
| 218 public void writeAsciiString(String string) throws IOException { | |
| 219 originalOutputWrapper.writeAsciiString(string); | |
| 220 streamListener.addContent(string); | |
| 221 } | |
| 222 | |
| 223 @Override | |
| 224 public void writeByte(byte b) throws IOException { | |
| 225 originalOutputWrapper.writeByte(b); | |
| 226 dumpByte(b, getStreamListener()); | |
| 227 } | |
| 228 | |
| 229 @Override | |
| 230 public void writeBytes(byte[] bytes) throws IOException { | |
| 231 originalOutputWrapper.writeBytes(bytes); | |
| 232 StringBuilder builder = new StringBuilder(bytes.length * 4); | |
| 233 for (byte b : bytes) { | |
| 234 dumpByte(b, builder); | |
| 235 } | |
| 236 streamListener.addContent(builder); | |
| 237 } | |
| 238 | |
| 239 @Override | |
| 240 public void markSeparatorForLog() { | |
| 241 streamListener.addSeparator(); | |
| 242 } | |
| 243 | |
| 244 protected LoggableOutput getOriginalOutputWrapper() { | |
| 245 return originalOutputWrapper; | |
| 246 } | |
| 247 | |
| 248 protected StreamListener getStreamListener() { | |
| 249 return streamListener; | |
| 250 } | |
| 251 } | |
| 252 } | |
| 253 | |
| 254 /** | |
| 255 * Creates loggable input/output that logs all traffic as a non-masked ASCII t
ext or bytes. | |
| 256 * Does not employ annotations. | |
| 257 */ | |
| 258 public static final FactoryBase PLAIN_ASCII = new FactoryBase() { | |
| 259 @Override | |
| 260 public LoggableOutput wrapOutputStream( | |
| 261 LoggableOutput originalOutputWrapper, | |
| 262 StreamListener streamListener) { | |
| 263 return new OutputWrapperBase(originalOutputWrapper, streamListener) { | |
| 264 @Override | |
| 265 public void writeByteToLog(byte b) throws IOException { | |
| 266 } | |
| 267 | |
| 268 @Override | |
| 269 public void writeToLog(String string, String annotation) | |
| 270 throws IOException { | |
| 271 } | |
| 272 | |
| 273 @Override | |
| 274 public void writeByteNoLogging(byte b) throws IOException { | |
| 275 getOriginalOutputWrapper().writeByteNoLogging(b); | |
| 276 dumpByte(b, getStreamListener()); | |
| 277 } | |
| 278 | |
| 279 @Override | |
| 280 public void writeBytesToLog(byte[] bytes) { | |
| 281 } | |
| 282 | |
| 283 @Override | |
| 284 public void writeBytesNoLogging(byte[] bytes) throws IOException { | |
| 285 getOriginalOutputWrapper().writeBytesNoLogging(bytes); | |
| 286 String str = new String(bytes, CHARSET); | |
| 287 getStreamListener().addContent(str); | |
| 288 } | |
| 289 }; | |
| 290 } | |
| 291 }; | |
| 292 | |
| 293 /** | |
| 294 * Creates loggable input/output that logs all traffic as an ASCII text or byt
es, or | |
| 295 * demasked annotated text. | |
| 296 */ | |
| 297 public static final FactoryBase ANNOTATED = new FactoryBase() { | |
| 298 @Override | |
| 299 public LoggableOutput wrapOutputStream( | |
| 300 LoggableOutput originalOutputWrapper, | |
| 301 StreamListener streamListener) { | |
| 302 return new OutputWrapperBase(originalOutputWrapper, streamListener) { | |
| 303 @Override | |
| 304 public void writeByteToLog(byte b) throws IOException { | |
| 305 dumpByte(b, getStreamListener()); | |
| 306 } | |
| 307 | |
| 308 @Override | |
| 309 public void writeToLog(String string, String annotation) | |
| 310 throws IOException { | |
| 311 getStreamListener().addContent(annotation + "<" + string + ">"); | |
| 312 } | |
| 313 | |
| 314 @Override | |
| 315 public void writeBytesToLog(byte[] bytes) { | |
| 316 StringBuilder builder = new StringBuilder(bytes.length * 4); | |
| 317 for (byte b : bytes) { | |
| 318 dumpByte(b, builder); | |
| 319 } | |
| 320 getStreamListener().addContent(builder); | |
| 321 } | |
| 322 | |
| 323 @Override | |
| 324 public void writeByteNoLogging(byte b) throws IOException { | |
| 325 getOriginalOutputWrapper().writeByteNoLogging(b); | |
| 326 } | |
| 327 | |
| 328 @Override | |
| 329 public void writeBytesNoLogging(byte[] bytes) throws IOException { | |
| 330 getOriginalOutputWrapper().writeBytesNoLogging(bytes); | |
| 331 } | |
| 332 }; | |
| 333 } | |
| 334 }; | |
| 335 | |
| 336 private static void dumpByte(byte b, StringBuilder output) { | |
| 337 AbstractWsConnection.dumpByte(b, output); | |
| 338 } | |
| 339 | |
| 340 private static void dumpByte(byte b, StreamListener streamListener) { | |
| 341 StringBuilder builder = new StringBuilder(4); | |
| 342 dumpByte(b, builder); | |
| 343 streamListener.addContent(builder); | |
| 344 } | |
| 345 } | |
| OLD | NEW |