View Javadoc
1   /*
2    * Copyright 2020 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.codec.http.multipart;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.buffer.Unpooled;
20  import io.netty.handler.codec.http.DefaultHttpContent;
21  import io.netty.handler.codec.http.DefaultHttpRequest;
22  import io.netty.handler.codec.http.DefaultLastHttpContent;
23  import io.netty.handler.codec.http.HttpHeaderNames;
24  import io.netty.handler.codec.http.HttpMethod;
25  import io.netty.handler.codec.http.HttpVersion;
26  import io.netty.microbench.util.AbstractMicrobenchmark;
27  import io.netty.util.CharsetUtil;
28  import io.netty.util.ResourceLeakDetector;
29  import io.netty.util.ResourceLeakDetector.Level;
30  import org.openjdk.jmh.annotations.Benchmark;
31  import org.openjdk.jmh.annotations.Measurement;
32  import org.openjdk.jmh.annotations.OutputTimeUnit;
33  import org.openjdk.jmh.annotations.Threads;
34  import org.openjdk.jmh.annotations.Warmup;
35  
36  import java.util.concurrent.TimeUnit;
37  
38  
39  @Threads(1)
40  @Warmup(iterations = 2)
41  @Measurement(iterations = 3)
42  @OutputTimeUnit(TimeUnit.MILLISECONDS)
43  public class HttpPostMultipartRequestDecoderBenchmark
44          extends AbstractMicrobenchmark {
45  
46      public double testHighNumberChunks(boolean big, boolean noDisk) {
47          String BOUNDARY = "01f136d9282f";
48          int size = 8 * 1024;
49          int chunkNumber = 64;
50          StringBuilder stringBuilder = new StringBuilder(size);
51          stringBuilder.setLength(size);
52          String data = stringBuilder.toString();
53  
54          byte[] bodyStartBytes = ("--" + BOUNDARY + "\n" +
55                                   "Content-Disposition: form-data; name=\"msg_id\"\n\n15200\n--" +
56                                   BOUNDARY +
57                                   "\nContent-Disposition: form-data; name=\"msg1\"; filename=\"file1.txt\"\n\n" +
58                                   data).getBytes(CharsetUtil.UTF_8);
59          byte[] bodyPartBigBytes = data.getBytes(CharsetUtil.UTF_8);
60          byte[] intermediaryBytes = ("\n--" + BOUNDARY +
61                                      "\nContent-Disposition: form-data; name=\"msg2\"; filename=\"file2.txt\"\n\n" +
62                                      data).getBytes(CharsetUtil.UTF_8);
63          byte[] finalBigBytes = ("\n" + "--" + BOUNDARY + "--\n").getBytes(CharsetUtil.UTF_8);
64          ByteBuf firstBuf = Unpooled.wrappedBuffer(bodyStartBytes);
65          ByteBuf finalBuf = Unpooled.wrappedBuffer(finalBigBytes);
66          ByteBuf nextBuf;
67          if (big) {
68              nextBuf = Unpooled.wrappedBuffer(bodyPartBigBytes);
69          } else {
70              nextBuf = Unpooled.wrappedBuffer(intermediaryBytes);
71          }
72          DefaultHttpRequest req =
73                  new DefaultHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.POST, "/up");
74          req.headers().add(HttpHeaderNames.CONTENT_TYPE,
75                            "multipart/form-data; boundary=" + BOUNDARY);
76  
77          long start = System.nanoTime();
78  
79          DefaultHttpDataFactory defaultHttpDataFactory =
80                  new DefaultHttpDataFactory(noDisk? 1024 * 1024 : 16 * 1024);
81          HttpPostRequestDecoder decoder =
82                  new HttpPostRequestDecoder(defaultHttpDataFactory, req);
83          firstBuf.retain();
84          decoder.offer(new DefaultHttpContent(firstBuf));
85          firstBuf.release();
86          for (int i = 1; i < chunkNumber; i++) {
87              nextBuf.retain();
88              decoder.offer(new DefaultHttpContent(nextBuf));
89              nextBuf.release();
90              nextBuf.readerIndex(0);
91          }
92          finalBuf.retain();
93          decoder.offer(new DefaultLastHttpContent(finalBuf));
94          finalBuf.release();
95          while (decoder.hasNext()) {
96              InterfaceHttpData httpData = decoder.next();
97          }
98          while (finalBuf.refCnt() > 0) {
99              finalBuf.release();
100         }
101         while (nextBuf.refCnt() > 0) {
102             nextBuf.release();
103         }
104         while (finalBuf.refCnt() > 0) {
105             finalBuf.release();
106         }
107         long stop = System.nanoTime();
108         double time = (stop - start) / 1000000.0;
109         defaultHttpDataFactory.cleanAllHttpData();
110         defaultHttpDataFactory.cleanRequestHttpData(req);
111         decoder.destroy();
112         return time;
113     }
114 
115     @Benchmark
116     public double multipartRequestDecoderHighDisabledLevel() {
117         final Level level = ResourceLeakDetector.getLevel();
118         try {
119             ResourceLeakDetector.setLevel(Level.DISABLED);
120             return testHighNumberChunks(false, true);
121         } finally {
122             ResourceLeakDetector.setLevel(level);
123         }
124     }
125 
126     @Benchmark
127     public double multipartRequestDecoderBigDisabledLevel() {
128         final Level level = ResourceLeakDetector.getLevel();
129         try {
130             ResourceLeakDetector.setLevel(Level.DISABLED);
131             return testHighNumberChunks(true, true);
132         } finally {
133             ResourceLeakDetector.setLevel(level);
134         }
135     }
136 
137     @Benchmark
138     public double multipartRequestDecoderHighSimpleLevel() {
139         final Level level = ResourceLeakDetector.getLevel();
140         try {
141             ResourceLeakDetector.setLevel(Level.SIMPLE);
142             return testHighNumberChunks(false, true);
143         } finally {
144             ResourceLeakDetector.setLevel(level);
145         }
146     }
147 
148     @Benchmark
149     public double multipartRequestDecoderBigSimpleLevel() {
150         final Level level = ResourceLeakDetector.getLevel();
151         try {
152             ResourceLeakDetector.setLevel(Level.SIMPLE);
153             return testHighNumberChunks(true, true);
154         } finally {
155             ResourceLeakDetector.setLevel(level);
156         }
157     }
158 
159     @Benchmark
160     public double multipartRequestDecoderHighAdvancedLevel() {
161         final Level level = ResourceLeakDetector.getLevel();
162         try {
163             ResourceLeakDetector.setLevel(Level.ADVANCED);
164             return testHighNumberChunks(false, true);
165         } finally {
166             ResourceLeakDetector.setLevel(level);
167         }
168     }
169 
170     @Benchmark
171     public double multipartRequestDecoderBigAdvancedLevel() {
172         final Level level = ResourceLeakDetector.getLevel();
173         try {
174             ResourceLeakDetector.setLevel(Level.ADVANCED);
175             return testHighNumberChunks(true, true);
176         } finally {
177             ResourceLeakDetector.setLevel(level);
178         }
179     }
180 
181     @Benchmark
182     public double multipartRequestDecoderHighParanoidLevel() {
183         final Level level = ResourceLeakDetector.getLevel();
184         try {
185             ResourceLeakDetector.setLevel(Level.PARANOID);
186             return testHighNumberChunks(false, true);
187         } finally {
188             ResourceLeakDetector.setLevel(level);
189         }
190     }
191 
192     @Benchmark
193     public double multipartRequestDecoderBigParanoidLevel() {
194         final Level level = ResourceLeakDetector.getLevel();
195         try {
196             ResourceLeakDetector.setLevel(Level.PARANOID);
197             return testHighNumberChunks(true, true);
198         } finally {
199             ResourceLeakDetector.setLevel(level);
200         }
201     }
202 
203 }