11package fr .breadeater .javaphp ;
22
3+ import com .sun .net .httpserver .Headers ;
4+
35import java .io .ByteArrayOutputStream ;
46import java .io .InputStream ;
5- import java .net .Socket ;
67import java .nio .ByteBuffer ;
8+ import java .nio .channels .SocketChannel ;
79import java .nio .charset .StandardCharsets ;
10+ import java .util .HashMap ;
11+ import java .util .List ;
812import java .util .Map ;
913
1014class FastCGIUtils {
11- public static byte [] buildRecord (Status type , int requestId , byte [] content ){
15+ /**
16+ * Builds record and returns it
17+ * @param type The record type
18+ * @param requestId The request ID
19+ * @param content The content
20+ * @return The built record
21+ */
22+ static byte [] buildRecord (Status type , int requestId , byte [] content ){
1223 int length = content .length ;
1324 int padding = (8 - (length % 8 )) % 8 ;
1425
@@ -29,7 +40,13 @@ public static byte[] buildRecord(Status type, int requestId, byte[] content){
2940 return buffer .array ();
3041 }
3142
32- public static byte [] encodeLength (int length ){
43+
44+ /**
45+ * Encodes byte array length to be sent to FastCGI server
46+ * @param length The length to be encoded.
47+ * @return A byte array containing the encoded length
48+ */
49+ static byte [] encodeLength (int length ){
3350 if (length < 128 ) return new byte []{ (byte ) length };
3451
3552 return new byte []{
@@ -40,33 +57,63 @@ public static byte[] encodeLength(int length){
4057 };
4158 }
4259
43- public static byte [] buildParams (boolean empty , int requestId , Map <String , String > params ) throws Exception {
60+
61+ /**
62+ * Build params and sends them to FastCGI server
63+ * @param empty Should it sends Empty Params
64+ * @param requestId
65+ * @param params
66+ * @return
67+ * @throws Exception
68+ */
69+ static byte [] buildParams (boolean empty , int requestId , Map <String , String > params ) throws Exception {
4470 if (empty ) return buildRecord (Status .FCGI_PARAMS , requestId , new byte [0 ]);
4571
4672 ByteArrayOutputStream out = new ByteArrayOutputStream ();
4773
4874 for (Map .Entry <String , String > entry : params .entrySet ()){
49- byte [] key = entry .getKey (). getBytes ( StandardCharsets . UTF_8 );
50- byte [] value = entry .getValue (). getBytes ( StandardCharsets . UTF_8 );
75+ String key = entry .getKey ();
76+ String value = entry .getValue ();
5177
52- out .write (encodeLength (key .length ));
53- out .write (encodeLength (value .length ));
54- out .write (key );
55- out .write (value );
78+ if (value == null ) continue ;
79+
80+ out .write (encodeLength (key .getBytes (StandardCharsets .UTF_8 ).length ));
81+ out .write (encodeLength (value .getBytes (StandardCharsets .UTF_8 ).length ));
82+ out .write (key .getBytes (StandardCharsets .UTF_8 ));
83+ out .write (value .getBytes (StandardCharsets .UTF_8 ));
5684 }
5785
5886 return buildRecord (Status .FCGI_PARAMS , requestId , out .toByteArray ());
5987 }
6088
61- public static byte [] buildStdin (int requestId , byte [] body ){
89+
90+ /**
91+ * Builds stdin containing a byte array and returns it
92+ * @param requestId The request ID
93+ * @param body
94+ * @return The built stdin
95+ */
96+ static byte [] buildStdin (int requestId , byte [] body ){
6297 return buildRecord (Status .FCGI_STDIN , requestId , body );
6398 }
6499
65- public static byte [] buildEmptyStdin (int requestId ){
100+
101+ /**
102+ * Builds a empty stdin and returns it
103+ * @param requestId The request ID
104+ * @return The built stdin
105+ */
106+ static byte [] buildEmptyStdin (int requestId ){
66107 return buildRecord (Status .FCGI_STDIN , requestId , new byte [0 ]);
67108 }
68109
69- public static byte [] buildRequest (int requestId ){
110+
111+ /**
112+ * Builds request and returns it
113+ * @param requestId The request ID
114+ * @return The request
115+ */
116+ static byte [] buildRequest (int requestId ){
70117 byte [] body = new byte [8 ];
71118
72119 body [0 ] = 0 ;
@@ -76,10 +123,17 @@ public static byte[] buildRequest(int requestId){
76123 return buildRecord (Status .FCGI_BEGIN_REQUEST , requestId , body );
77124 }
78125
79- public static String parseFastCGIRequest (Socket client , InputStream in ) throws Exception {
126+
127+ /**
128+ * Parses the FastCGI response and return its body
129+ * @param client The client socket
130+ * @param in The input stream of the client
131+ * @return The body of the response
132+ */
133+ static String parseFastCGIRequest (SocketChannel client , InputStream in ) throws Exception {
80134 String result = null ;
81135
82- while (! client .isClosed ()){
136+ while (client .isOpen ()){
83137 int version = in .read ();
84138 int type = in .read ();
85139 int requestIdHigh = in .read ();
@@ -112,4 +166,59 @@ public static String parseFastCGIRequest(Socket client, InputStream in) throws E
112166
113167 return result ;
114168 }
169+
170+
171+ /**
172+ * Sets all FastCGI params and returns a Map containing all the params
173+ * @param runOptions The JavaPHP options to run a PHP file
174+ * @param request An Http Request instance containing request method, request headers, etc...
175+ * @return The Map containing all the params
176+ */
177+ static Map <String , String > setFastCGIParams (JavaPHP .Options runOptions , Request request ) {
178+ Map <String , String > fastCGIheaders = new HashMap <>();
179+ Headers reqHeaders = request .getRequestHeaders ();
180+ String httpsEnabled = "off" ;
181+
182+ String [] splittedURI = request .getRequestPath ().split ("\\ ?" , 2 );
183+
184+ if (splittedURI .length == 2 ) fastCGIheaders .put ("QUERY_STRING" , splittedURI [1 ]);
185+ if (request .isHttps ()) httpsEnabled = "on" ;
186+
187+ fastCGIheaders .put ("SCRIPT_FILENAME" , runOptions .PHP_FILEPATH );
188+ fastCGIheaders .put ("GATEWAY_INTERFACE" , "CGI/1.1" );
189+ fastCGIheaders .put ("SERVER_PROTOCOL" , request .getRequestHttpVersion ());
190+ fastCGIheaders .put ("REQUEST_METHOD" , request .getRequestMethod ());
191+ fastCGIheaders .put ("SCRIPT_NAME" , splittedURI [0 ]);
192+ fastCGIheaders .put ("REQUEST_URI" , request .getRequestPath ());
193+ fastCGIheaders .put ("DOCUMENT_ROOT" , runOptions .PHP_DOC_ROOT );
194+ fastCGIheaders .put ("SERVER_SOFTWARE" , runOptions .PHP_SERVERSOFTWARE );
195+ fastCGIheaders .put ("REMOTE_ADDR" , request .getRequestAddress ().getHostName ());
196+ fastCGIheaders .put ("REMOTE_PORT" , Integer .toString (request .getRequestAddress ().getPort ()));
197+ fastCGIheaders .put ("SERVER_ADDR" , runOptions .PHP_SERVERADDR );
198+ fastCGIheaders .put ("SERVER_PORT" , Integer .toString (runOptions .PHP_SERVERPORT ));
199+ fastCGIheaders .put ("HTTPS" , httpsEnabled );
200+
201+ for (Map .Entry <String , List <String >> entry : reqHeaders .entrySet ()){
202+ String key = entry .getKey ();
203+ String value = entry .getValue ().get (0 );
204+
205+ if (key .equals ("Content-Length" )) continue ;
206+
207+ if (key .equals ("Content-Type" )){
208+ fastCGIheaders .put ("CONTENT_TYPE" , value );
209+ continue ;
210+ }
211+
212+ if (request .getRequestBody () != null ){
213+ int reqBodyLength = request .getRequestBody ().getBytes ().length ;
214+
215+ fastCGIheaders .put ("CONTENT_LENGTH" , Integer .toString (reqBodyLength ));
216+ continue ;
217+ }
218+
219+ fastCGIheaders .put ("HTTP_" + key .replaceAll ("-" , "_" ).toUpperCase (), value );
220+ }
221+
222+ return fastCGIheaders ;
223+ }
115224}
0 commit comments