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 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 }