View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.client5.http.impl;
29  
30  import java.nio.ByteBuffer;
31  
32  import org.apache.hc.core5.annotation.Internal;
33  import org.apache.hc.core5.util.Args;
34  import org.slf4j.Logger;
35  
36  @Internal
37  public class Wire {
38  
39      private static final int MAX_STRING_BUILDER_SIZE = 2048;
40  
41      private static final ThreadLocal<StringBuilder> THREAD_LOCAL = new ThreadLocal<>();
42  
43      /**
44       * Returns a {@code StringBuilder} that this Layout implementation can use to write the formatted log event to.
45       *
46       * @return a {@code StringBuilder}
47       */
48      private static StringBuilder getStringBuilder() {
49          StringBuilder result = THREAD_LOCAL.get();
50          if (result == null) {
51              result = new StringBuilder(MAX_STRING_BUILDER_SIZE);
52              THREAD_LOCAL.set(result);
53          }
54          trimToMaxSize(result, MAX_STRING_BUILDER_SIZE);
55          result.setLength(0);
56          return result;
57      }
58  
59      /**
60       * Ensures that the char[] array of the specified StringBuilder does not exceed the specified number of characters.
61       * This method is useful to ensure that excessively long char[] arrays are not kept in memory forever.
62       *
63       * @param stringBuilder the StringBuilder to check
64       * @param maxSize the maximum number of characters the StringBuilder is allowed to have
65       */
66      private static void trimToMaxSize(final StringBuilder stringBuilder, final int maxSize) {
67          if (stringBuilder != null && stringBuilder.capacity() > maxSize) {
68              stringBuilder.setLength(maxSize);
69              stringBuilder.trimToSize();
70          }
71      }
72  
73      private final Logger log;
74      private final String id;
75  
76      public Wire(final Logger log, final String id) {
77          super();
78          this.log = log;
79          this.id = id;
80      }
81  
82      private void wire(final String header, final byte[] b, final int pos, final int off) {
83          final StringBuilder buffer = getStringBuilder();
84          for (int i = 0; i < off; i++) {
85              final int ch = b[pos + i];
86              if (ch == 13) {
87                  buffer.append("[\\r]");
88              } else if (ch == 10) {
89                  buffer.append("[\\n]\"");
90                  buffer.insert(0, "\"");
91                  buffer.insert(0, header);
92                  log.debug("{} {}", this.id, buffer);
93                  buffer.setLength(0);
94              } else if ((ch < 32) || (ch >= 127)) {
95                  buffer.append("[0x");
96                  buffer.append(Integer.toHexString(ch));
97                  buffer.append("]");
98              } else {
99                  buffer.append((char) ch);
100             }
101         }
102         if (buffer.length() > 0) {
103             buffer.append('\"');
104             buffer.insert(0, '\"');
105             buffer.insert(0, header);
106             log.debug("{} {}", this.id, buffer);
107         }
108     }
109 
110 
111     public boolean isEnabled() {
112         return log.isDebugEnabled();
113     }
114 
115     public void output(final byte[] b, final int pos, final int off) {
116         Args.notNull(b, "Output");
117         wire(">> ", b, pos, off);
118     }
119 
120     public void input(final byte[] b, final int pos, final int off) {
121         Args.notNull(b, "Input");
122         wire("<< ", b, pos, off);
123     }
124 
125     public void output(final byte[] b) {
126         Args.notNull(b, "Output");
127         output(b, 0, b.length);
128     }
129 
130     public void input(final byte[] b) {
131         Args.notNull(b, "Input");
132         input(b, 0, b.length);
133     }
134 
135     public void output(final int b) {
136         output(new byte[] {(byte) b});
137     }
138 
139     public void input(final int b) {
140         input(new byte[] {(byte) b});
141     }
142 
143     public void output(final String s) {
144         Args.notNull(s, "Output");
145         output(s.getBytes());
146     }
147 
148     public void input(final String s) {
149         Args.notNull(s, "Input");
150         input(s.getBytes());
151     }
152 
153     public void output(final ByteBuffer b) {
154         Args.notNull(b, "Output");
155         if (b.hasArray()) {
156             output(b.array(), b.arrayOffset() + b.position(), b.remaining());
157         } else {
158             final byte[] tmp = new byte[b.remaining()];
159             b.get(tmp);
160             output(tmp);
161         }
162     }
163 
164     public void input(final ByteBuffer b) {
165         Args.notNull(b, "Input");
166         if (b.hasArray()) {
167             input(b.array(), b.arrayOffset() + b.position(), b.remaining());
168         } else {
169             final byte[] tmp = new byte[b.remaining()];
170             b.get(tmp);
171             input(tmp);
172         }
173     }
174 
175 }