1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
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
67 int limit = content.limit();
68 content.limit(content.position() + length);
69 ByteBuffer slice = content.slice();
70
71 result = result.append(slice, false);
72 content.position(content.limit());
73 content.limit(limit);
74 contentLength -= length;
75
76 if (recycle && contentLength == 0)
77 result = result.append(content, true);
78 }
79
80 return result;
81 }
82
83
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 }