View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.fcgi.generator;
20  
21  import java.nio.ByteBuffer;
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import org.eclipse.jetty.fcgi.FCGI;
26  import org.eclipse.jetty.io.ByteBufferPool;
27  import org.eclipse.jetty.util.BufferUtil;
28  import org.eclipse.jetty.util.Callback;
29  
30  public class Generator
31  {
32      public static final int MAX_CONTENT_LENGTH = 0xFF_FF;
33  
34      protected final ByteBufferPool byteBufferPool;
35  
36      public Generator(ByteBufferPool byteBufferPool)
37      {
38          this.byteBufferPool = byteBufferPool;
39      }
40  
41      protected Result generateContent(int id, ByteBuffer content, boolean recycle, boolean lastContent, Callback callback, FCGI.FrameType frameType)
42      {
43          id &= 0xFF_FF;
44  
45          int contentLength = content == null ? 0 : content.remaining();
46          Result result = new Result(byteBufferPool, callback);
47  
48          while (contentLength > 0 || lastContent)
49          {
50              ByteBuffer buffer = byteBufferPool.acquire(8, false);
51              BufferUtil.clearToFill(buffer);
52              result = result.append(buffer, true);
53  
54              // Generate the frame header
55              buffer.put((byte)0x01);
56              buffer.put((byte)frameType.code);
57              buffer.putShort((short)id);
58              int length = Math.min(MAX_CONTENT_LENGTH, contentLength);
59              buffer.putShort((short)length);
60              buffer.putShort((short)0);
61              buffer.flip();
62  
63              if (contentLength == 0)
64                  break;
65  
66              // Slice the content to avoid copying
67              int limit = content.limit();
68              content.limit(content.position() + length);
69              ByteBuffer slice = content.slice();
70              // Don't recycle the slice
71              result = result.append(slice, false);
72              content.position(content.limit());
73              content.limit(limit);
74              contentLength -= length;
75              // Recycle the content buffer if needed
76              if (recycle && contentLength == 0)
77                  result = result.append(content, true);
78          }
79  
80          return result;
81      }
82  
83      // TODO: rewrite this class in light of ByteBufferPool.Lease.
84      public static class Result implements Callback
85      {
86          private final List<Callback> callbacks = new ArrayList<>(2);
87          private final List<ByteBuffer> buffers = new ArrayList<>(8);
88          private final List<Boolean> recycles = new ArrayList<>(8);
89          private final ByteBufferPool byteBufferPool;
90  
91          public Result(ByteBufferPool byteBufferPool, Callback callback)
92          {
93              this.byteBufferPool = byteBufferPool;
94              this.callbacks.add(callback);
95          }
96  
97          public Result append(ByteBuffer buffer, boolean recycle)
98          {
99              if (buffer != null)
100             {
101                 buffers.add(buffer);
102                 recycles.add(recycle);
103             }
104             return this;
105         }
106 
107         public Result join(Result that)
108         {
109             callbacks.addAll(that.callbacks);
110             buffers.addAll(that.buffers);
111             recycles.addAll(that.recycles);
112             return this;
113         }
114 
115         public ByteBuffer[] getByteBuffers()
116         {
117             return buffers.toArray(new ByteBuffer[buffers.size()]);
118         }
119 
120         @Override
121         @SuppressWarnings("ForLoopReplaceableByForEach")
122         public void succeeded()
123         {
124             recycle();
125             for (int i = 0; i < callbacks.size(); ++i)
126             {
127                 Callback callback = callbacks.get(i);
128                 if (callback != null)
129                     callback.succeeded();
130             }
131         }
132 
133         @Override
134         @SuppressWarnings("ForLoopReplaceableByForEach")
135         public void failed(Throwable x)
136         {
137             recycle();
138             for (int i = 0; i < callbacks.size(); ++i)
139             {
140                 Callback callback = callbacks.get(i);
141                 if (callback != null)
142                     callback.failed(x);
143             }
144         }
145 
146         protected void recycle()
147         {
148             for (int i = 0; i < buffers.size(); ++i)
149             {
150                 ByteBuffer buffer = buffers.get(i);
151                 if (recycles.get(i))
152                     byteBufferPool.release(buffer);
153             }
154         }
155     }
156 }