1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.core5.testing.nio;
29
30 import java.io.IOException;
31 import java.net.SocketAddress;
32 import java.nio.ByteBuffer;
33 import java.nio.channels.ByteChannel;
34 import java.nio.channels.SelectionKey;
35 import java.util.concurrent.locks.Lock;
36
37 import org.apache.hc.core5.annotation.Internal;
38 import org.apache.hc.core5.http.Chars;
39 import org.apache.hc.core5.io.CloseMode;
40 import org.apache.hc.core5.reactor.Command;
41 import org.apache.hc.core5.reactor.IOEventHandler;
42 import org.apache.hc.core5.reactor.IOSession;
43 import org.apache.hc.core5.reactor.ProtocolIOSession;
44 import org.apache.hc.core5.util.Args;
45 import org.apache.hc.core5.util.Timeout;
46 import org.slf4j.Logger;
47
48 @Internal
49 public class LoggingIOSession implements IOSession {
50
51 private final Logger log;
52 private final Logger wireLog;
53 private final IOSession session;
54
55 public LoggingIOSession(final IOSession session, final Logger log, final Logger wireLog) {
56 super();
57 this.session = session;
58 this.log = log;
59 this.wireLog = wireLog;
60 }
61
62 public LoggingIOSession(final ProtocolIOSession session, final Logger log) {
63 this(session, log, null);
64 }
65
66 @Override
67 public String getId() {
68 return session.getId();
69 }
70
71 @Override
72 public Lock getLock() {
73 return this.session.getLock();
74 }
75
76 @Override
77 public void enqueue(final Command command, final Command.Priority priority) {
78 this.session.enqueue(command, priority);
79 if (this.log.isDebugEnabled()) {
80 this.log.debug("{} enqueued {} with priority {}", this.session, command.getClass().getSimpleName(), priority);
81 }
82 }
83
84 @Override
85 public boolean hasCommands() {
86 return this.session.hasCommands();
87 }
88
89 @Override
90 public Command poll() {
91 return this.session.poll();
92 }
93
94 @Override
95 public ByteChannel channel() {
96 return this.session.channel();
97 }
98
99 @Override
100 public SocketAddress getLocalAddress() {
101 return this.session.getLocalAddress();
102 }
103
104 @Override
105 public SocketAddress getRemoteAddress() {
106 return this.session.getRemoteAddress();
107 }
108
109 @Override
110 public int getEventMask() {
111 return this.session.getEventMask();
112 }
113
114 private static String formatOps(final int ops) {
115 final StringBuilder buffer = new StringBuilder(6);
116 buffer.append('[');
117 if ((ops & SelectionKey.OP_READ) > 0) {
118 buffer.append('r');
119 }
120 if ((ops & SelectionKey.OP_WRITE) > 0) {
121 buffer.append('w');
122 }
123 if ((ops & SelectionKey.OP_ACCEPT) > 0) {
124 buffer.append('a');
125 }
126 if ((ops & SelectionKey.OP_CONNECT) > 0) {
127 buffer.append('c');
128 }
129 buffer.append(']');
130 return buffer.toString();
131 }
132
133 @Override
134 public void setEventMask(final int ops) {
135 this.session.setEventMask(ops);
136 if (this.log.isDebugEnabled()) {
137 this.log.debug("{} event mask set {}", this.session, formatOps(ops));
138 }
139 }
140
141 @Override
142 public void setEvent(final int op) {
143 this.session.setEvent(op);
144 if (this.log.isDebugEnabled()) {
145 this.log.debug("{} event set {}", this.session, formatOps(op));
146 }
147 }
148
149 @Override
150 public void clearEvent(final int op) {
151 this.session.clearEvent(op);
152 if (this.log.isDebugEnabled()) {
153 this.log.debug("{} event cleared {}", this.session, formatOps(op));
154 }
155 }
156
157 @Override
158 public void close() {
159 if (this.log.isDebugEnabled()) {
160 this.log.debug("{} close", this.session);
161 }
162 this.session.close();
163 }
164
165 @Override
166 public Status getStatus() {
167 return this.session.getStatus();
168 }
169
170 @Override
171 public boolean isOpen() {
172 return session.isOpen();
173 }
174
175 @Override
176 public void close(final CloseMode closeMode) {
177 if (this.log.isDebugEnabled()) {
178 this.log.debug("{} shutdown {}", this.session, closeMode);
179 }
180 this.session.close(closeMode);
181 }
182
183 @Override
184 public Timeout getSocketTimeout() {
185 return this.session.getSocketTimeout();
186 }
187
188 @Override
189 public void setSocketTimeout(final Timeout timeout) {
190 if (this.log.isDebugEnabled()) {
191 this.log.debug("{} set timeout {}", this.session, timeout);
192 }
193 this.session.setSocketTimeout(timeout);
194 }
195
196 @Override
197 public int read(final ByteBuffer dst) throws IOException {
198 final int bytesRead = session.read(dst);
199 if (log.isDebugEnabled()) {
200 log.debug("{} {} bytes read", session, bytesRead);
201 }
202 if (bytesRead > 0 && wireLog.isDebugEnabled()) {
203 final ByteBuffer b = dst.duplicate();
204 final int p = b.position();
205 b.limit(p);
206 b.position(p - bytesRead);
207 logData(b, "<< ");
208 }
209 return bytesRead;
210 }
211
212 @Override
213 public int write(final ByteBuffer src) throws IOException {
214 final int byteWritten = session.write(src);
215 if (log.isDebugEnabled()) {
216 log.debug("{} {} bytes written", session, byteWritten);
217 }
218 if (byteWritten > 0 && wireLog.isDebugEnabled()) {
219 final ByteBuffer b = src.duplicate();
220 final int p = b.position();
221 b.limit(p);
222 b.position(p - byteWritten);
223 logData(b, ">> ");
224 }
225 return byteWritten;
226 }
227
228 private void logData(final ByteBuffer data, final String prefix) throws IOException {
229 final byte[] line = new byte[16];
230 final StringBuilder buf = new StringBuilder();
231 while (data.hasRemaining()) {
232 buf.setLength(0);
233 buf.append(prefix);
234 final int chunk = Math.min(data.remaining(), line.length);
235 data.get(line, 0, chunk);
236
237 for (int i = 0; i < chunk; i++) {
238 final char ch = (char) line[i];
239 if (ch > Chars.SP && ch <= Chars.DEL) {
240 buf.append(ch);
241 } else if (Character.isWhitespace(ch)) {
242 buf.append(' ');
243 } else {
244 buf.append('.');
245 }
246 }
247 for (int i = chunk; i < 17; i++) {
248 buf.append(' ');
249 }
250
251 for (int i = 0; i < chunk; i++) {
252 buf.append(' ');
253 final int b = line[i] & 0xff;
254 final String s = Integer.toHexString(b);
255 if (s.length() == 1) {
256 buf.append("0");
257 }
258 buf.append(s);
259 }
260 wireLog.debug(buf.toString());
261 }
262 }
263
264 @Override
265 public void updateReadTime() {
266 this.session.updateReadTime();
267 }
268
269 @Override
270 public void updateWriteTime() {
271 this.session.updateWriteTime();
272 }
273
274 @Override
275 public long getLastReadTime() {
276 return this.session.getLastReadTime();
277 }
278
279 @Override
280 public long getLastWriteTime() {
281 return this.session.getLastWriteTime();
282 }
283
284 @Override
285 public long getLastEventTime() {
286 return this.session.getLastEventTime();
287 }
288
289 @Override
290 public IOEventHandler getHandler() {
291 return this.session.getHandler();
292 }
293
294 @Override
295 public void upgrade(final IOEventHandler handler) {
296 Args.notNull(handler, "Protocol handler");
297 if (this.log.isDebugEnabled()) {
298 this.log.debug("{} protocol upgrade: {}", this.session, handler.getClass());
299 }
300 this.session.upgrade(new IOEventHandler() {
301
302 @Override
303 public void connected(final IOSession protocolSession) throws IOException {
304 handler.connected(protocolSession);
305 }
306
307 @Override
308 public void inputReady(final IOSession protocolSession, final ByteBuffer src) throws IOException {
309 if (src != null && wireLog.isDebugEnabled()) {
310 final ByteBuffer b = src.duplicate();
311 logData(b, "<< ");
312 }
313 handler.inputReady(protocolSession, src);
314 }
315
316 @Override
317 public void outputReady(final IOSession protocolSession) throws IOException {
318 handler.outputReady(protocolSession);
319 }
320
321 @Override
322 public void timeout(final IOSession protocolSession, final Timeout timeout) throws IOException {
323 handler.timeout(protocolSession, timeout);
324 }
325
326 @Override
327 public void exception(final IOSession protocolSession, final Exception cause) {
328 handler.exception(protocolSession, cause);
329 }
330
331 @Override
332 public void disconnected(final IOSession protocolSession) {
333 handler.disconnected(protocolSession);
334 }
335
336 });
337
338 }
339
340 @Override
341 public String toString() {
342 return this.session.toString();
343 }
344
345 }