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