View Javadoc
1   /*
2    * Copyright 2024 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.handler.ssl;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.buffer.ByteBufAllocator;
20  import io.netty.buffer.CompositeByteBuf;
21  import io.netty.channel.AbstractCoalescingBufferQueue;
22  import io.netty.channel.Channel;
23  import io.netty.util.internal.PlatformDependent;
24  
25  import static io.netty.buffer.ByteBufUtil.ensureWritableSuccess;
26  
27  /**
28   * Each call to SSL_write will introduce about ~100 bytes of overhead. This coalescing queue attempts to increase
29   * goodput by aggregating the plaintext in chunks of {@link #wrapDataSize}. If many small chunks are written
30   * this can increase goodput, decrease the amount of calls to SSL_write, and decrease overall encryption operations.
31   */
32  abstract class SslHandlerCoalescingBufferQueue extends AbstractCoalescingBufferQueue {
33  
34      private final boolean wantsDirectBuffer;
35  
36      SslHandlerCoalescingBufferQueue(Channel channel, int initSize, boolean wantsDirectBuffer) {
37          super(channel, initSize);
38          this.wantsDirectBuffer = wantsDirectBuffer;
39      }
40  
41      protected abstract int wrapDataSize();
42  
43      @Override
44      protected ByteBuf compose(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf next) {
45          return attemptCopyToCumulation(cumulation, next, wrapDataSize()) ? cumulation :
46                  copyAndCompose(alloc, cumulation, next);
47      }
48  
49      @Override
50      protected ByteBuf composeFirst(ByteBufAllocator allocator, ByteBuf first, int bufferSize) {
51          final ByteBuf newFirst;
52          if (wantsDirectBuffer) {
53              newFirst = allocator.directBuffer(bufferSize);
54          } else {
55              newFirst = allocator.heapBuffer(bufferSize);
56          }
57          try {
58              newFirst.writeBytes(first);
59          } catch (Throwable cause) {
60              newFirst.release();
61              PlatformDependent.throwException(cause);
62          }
63          assert !first.isReadable();
64          first.release();
65          return newFirst;
66      }
67  
68      @Override
69      protected ByteBuf removeEmptyValue() {
70          return null;
71      }
72  
73      private static boolean attemptCopyToCumulation(ByteBuf cumulation, ByteBuf next, int wrapDataSize) {
74          final int inReadableBytes = next.readableBytes();
75          // Nothing to copy so just release the buffer.
76          if (inReadableBytes == 0) {
77              next.release();
78              return true;
79          }
80          final int cumulationCapacity = cumulation.capacity();
81          if (wrapDataSize - cumulation.readableBytes() >= inReadableBytes &&
82                  // Avoid using the same buffer if next's data would make cumulation exceed the wrapDataSize.
83                  // Only copy if there is enough space available and the capacity is large enough, and attempt to
84                  // resize if the capacity is small.
85                  (cumulation.isWritable(inReadableBytes) && cumulationCapacity >= wrapDataSize ||
86                          cumulationCapacity < wrapDataSize &&
87                                  ensureWritableSuccess(cumulation.ensureWritable(inReadableBytes, false)))) {
88              cumulation.writeBytes(next);
89              next.release();
90              return true;
91          }
92          return false;
93      }
94  }