View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2014 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      public static class Result implements Callback
84      {
85          private final List<Callback> callbacks = new ArrayList<>(2);
86          private final List<ByteBuffer> buffers = new ArrayList<>(8);
87          private final List<Boolean> recycles = new ArrayList<>(8);
88          private final ByteBufferPool byteBufferPool;
89  
90          public Result(ByteBufferPool byteBufferPool, Callback callback)
91          {
92              this.byteBufferPool = byteBufferPool;
93              this.callbacks.add(callback);
94          }
95  
96          public Result append(ByteBuffer buffer, boolean recycle)
97          {
98              if (buffer != null)
99              {
100                 buffers.add(buffer);
101                 recycles.add(recycle);
102             }
103             return this;
104         }
105 
106         public Result join(Result that)
107         {
108             callbacks.addAll(that.callbacks);
109             buffers.addAll(that.buffers);
110             recycles.addAll(that.recycles);
111             return this;
112         }
113 
114         public ByteBuffer[] getByteBuffers()
115         {
116             return buffers.toArray(new ByteBuffer[buffers.size()]);
117         }
118 
119         @Override
120         @SuppressWarnings("ForLoopReplaceableByForEach")
121         public void succeeded()
122         {
123             recycle();
124             for (int i = 0; i < callbacks.size(); ++i)
125             {
126                 Callback callback = callbacks.get(i);
127                 if (callback != null)
128                     callback.succeeded();
129             }
130         }
131 
132         @Override
133         @SuppressWarnings("ForLoopReplaceableByForEach")
134         public void failed(Throwable x)
135         {
136             recycle();
137             for (int i = 0; i < callbacks.size(); ++i)
138             {
139                 Callback callback = callbacks.get(i);
140                 if (callback != null)
141                     callback.failed(x);
142             }
143         }
144 
145         protected void recycle()
146         {
147             for (int i = 0; i < buffers.size(); ++i)
148             {
149                 ByteBuffer buffer = buffers.get(i);
150                 if (recycles.get(i))
151                     byteBufferPool.release(buffer);
152             }
153         }
154     }
155 }