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.nio.charset.Charset;
23 import java.util.ArrayList;
24 import java.util.List;
25
26 import org.eclipse.jetty.fcgi.FCGI;
27 import org.eclipse.jetty.http.HttpField;
28 import org.eclipse.jetty.http.HttpFields;
29 import org.eclipse.jetty.io.ByteBufferPool;
30 import org.eclipse.jetty.util.BufferUtil;
31 import org.eclipse.jetty.util.Callback;
32
33 public class ClientGenerator extends Generator
34 {
35
36
37
38 public static final int MAX_PARAM_LENGTH = 0x7F_FF - 4;
39
40 public ClientGenerator(ByteBufferPool byteBufferPool)
41 {
42 super(byteBufferPool);
43 }
44
45 public Result generateRequestHeaders(int request, HttpFields fields, Callback callback)
46 {
47 request &= 0xFF_FF;
48
49 Charset utf8 = Charset.forName("UTF-8");
50 List<byte[]> bytes = new ArrayList<>(fields.size() * 2);
51 int fieldsLength = 0;
52 for (HttpField field : fields)
53 {
54 String name = field.getName();
55 byte[] nameBytes = name.getBytes(utf8);
56 if (nameBytes.length > MAX_PARAM_LENGTH)
57 throw new IllegalArgumentException("Field name " + name + " exceeds max length " + MAX_PARAM_LENGTH);
58 bytes.add(nameBytes);
59
60 String value = field.getValue();
61 byte[] valueBytes = value.getBytes(utf8);
62 if (valueBytes.length > MAX_PARAM_LENGTH)
63 throw new IllegalArgumentException("Field value " + value + " exceeds max length " + MAX_PARAM_LENGTH);
64 bytes.add(valueBytes);
65
66 int nameLength = nameBytes.length;
67 fieldsLength += bytesForLength(nameLength);
68
69 int valueLength = valueBytes.length;
70 fieldsLength += bytesForLength(valueLength);
71
72 fieldsLength += nameLength;
73 fieldsLength += valueLength;
74 }
75
76
77 int maxCapacity = 4 + 4 + 2 * MAX_PARAM_LENGTH;
78
79
80
81 ByteBuffer beginRequestBuffer = byteBufferPool.acquire(16, false);
82 BufferUtil.clearToFill(beginRequestBuffer);
83 Result result = new Result(byteBufferPool, callback);
84 result = result.append(beginRequestBuffer, true);
85
86
87 beginRequestBuffer.putInt(0x01_01_00_00 + request);
88 beginRequestBuffer.putInt(0x00_08_00_00);
89
90 beginRequestBuffer.putLong(0x00_01_01_00_00_00_00_00L);
91 beginRequestBuffer.flip();
92
93 int index = 0;
94 while (fieldsLength > 0)
95 {
96 int capacity = 8 + Math.min(maxCapacity, fieldsLength);
97 ByteBuffer buffer = byteBufferPool.acquire(capacity, true);
98 BufferUtil.clearToFill(buffer);
99 result = result.append(buffer, true);
100
101
102 buffer.putInt(0x01_04_00_00 + request);
103 buffer.putShort((short)0);
104 buffer.putShort((short)0);
105 capacity -= 8;
106
107 int length = 0;
108 while (index < bytes.size())
109 {
110 byte[] nameBytes = bytes.get(index);
111 int nameLength = nameBytes.length;
112 byte[] valueBytes = bytes.get(index + 1);
113 int valueLength = valueBytes.length;
114
115 int required = bytesForLength(nameLength) + bytesForLength(valueLength) + nameLength + valueLength;
116 if (required > capacity)
117 break;
118
119 putParamLength(buffer, nameLength);
120 putParamLength(buffer, valueLength);
121 buffer.put(nameBytes);
122 buffer.put(valueBytes);
123
124 length += required;
125 fieldsLength -= required;
126 capacity -= required;
127 index += 2;
128 }
129
130 buffer.putShort(4, (short)length);
131 buffer.flip();
132 }
133
134
135 ByteBuffer lastParamsBuffer = byteBufferPool.acquire(8, false);
136 BufferUtil.clearToFill(lastParamsBuffer);
137 result = result.append(lastParamsBuffer, true);
138
139
140 lastParamsBuffer.putInt(0x01_04_00_00 + request);
141 lastParamsBuffer.putInt(0x00_00_00_00);
142 lastParamsBuffer.flip();
143
144 return result;
145 }
146
147 private int putParamLength(ByteBuffer buffer, int length)
148 {
149 int result = bytesForLength(length);
150 if (result == 4)
151 buffer.putInt(length | 0x80_00_00_00);
152 else
153 buffer.put((byte)length);
154 return result;
155 }
156
157 private int bytesForLength(int length)
158 {
159 return length > 127 ? 4 : 1;
160 }
161
162 public Result generateRequestContent(int request, ByteBuffer content, boolean lastContent, Callback callback)
163 {
164 return generateContent(request, content, false, lastContent, callback, FCGI.FrameType.STDIN);
165 }
166 }