PHP-CGI сообщение пусто

Я пишу Java CGI-клиент для общения с PHP-CGI, но я столкнулся с проблемой, мой php $_POST не заполняет никакими данными, в то время как я определенно отправляю некоторые. Я понятия не имею, почему это делает, и я не могу найти эту проблему нигде.

Я использую чистые бинарные файлы PHP прямо из windows.php.net без каких-либо правок.

Вот код, который я использую для тестирования прямо сейчас:

public static void main(String[] args)
{
HashMap<String, String> map = new HashMap<String, String>();

String body = "data=Foo+Bar";

String queryString = "yes=no&a=b";
String requestMethod = "POST";
String contentType = "application/x-www-form-urlencoded";
String contentLength = Integer.toString( body.length() );

String docRoot =  "D:/http test";

String scriptName = "/index.php";
String scriptFileName = docRoot + scriptName;
String pathInfo = "";
String pathTranslated = docRoot + pathInfo;
String requestUri = scriptName + pathInfo + ("?" + queryString);
String documentUri = scriptName + pathInfo;
String serverProtocol = "HTTP/1.1";

String gatewayInterface = "CGI/1.1";

map.put( "QUERY_STRING" , queryString);
map.put( "REQUEST_METHOD" , requestMethod);
map.put( "CONTENT_TYPE" , contentType);
map.put( "CONTENT_LENGTH" , contentLength);

map.put( "SCRIPT_FILENAME" , scriptFileName);
map.put( "SCRIPT_NAME" , scriptName);
map.put( "PATH_INFO" , pathInfo);
map.put( "PATH_TRANSLATED" , pathTranslated);
map.put( "REQUEST_URI" , requestUri);
map.put( "DOCUMENT_URI" , documentUri);
map.put( "DOCUMENT_ROOT" , docRoot);

map.put( "SERVER_NAME" , "localhost" );
map.put( "SERVER_PROTOCOL" , serverProtocol);

map.put( "GATEWAY_INTERFACE" , gatewayInterface);

Client c = new Client( "127.0.0.1" , 8090 );

System.out.println("\n" + c.doRequest( map , body ));
}

Client.java:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.Map.Entry;

public class Client
{
private Socket socket;

public Client( String host, int port ) throws UnknownHostException, IOException
{
socket = new Socket( host, port );
}

public String doRequest( Map<String, String> params, String content ) throws IOException
{
ByteArrayOutputStream paramBytes = new ByteArrayOutputStream();

for ( Entry<String, String> param: params.entrySet() )
paramBytes.write( nvpair( param.getKey() , param.getValue() ) );

Packet beginRequest = new Packet( FCGI.BEGIN_REQUEST, FCGI.NULL_REQUEST_ID, new byte[] { 0, FCGI.RESPONDER, FCGI.KEEP_CONN, 0, 0, 0, 0, 0 } );
Packet requestParams = new Packet( FCGI.PARAMS, FCGI.NULL_REQUEST_ID, paramBytes.toByteArray() );
Packet requestContent = new Packet( FCGI.STDIN, FCGI.NULL_REQUEST_ID, content.getBytes( "UTF-8" ) );

OutputStream stream = socket.getOutputStream();

stream.write( beginRequest.getBytes() );
stream.write( requestParams.getBytes() );
stream.write( requestContent.getBytes() );

return readResponse();
}

private String readResponse() throws IOException
{
InputStream stream = socket.getInputStream();

// TODO buffering

String out = null;
for ( Packet p = new Packet( stream ); p.getType() != FCGI.END_REQUEST; p = new Packet( stream ) )
{
System.out.print( p.getType() + ", " );
if ( p.getType() == FCGI.STDOUT )
out = new String( p.getContent() );
}
return out;
}

public byte[] nvpair( String name, String value )
{
try
{
int nl = name.length();
int vl = value.length();

ByteArrayOutputStream bytes = new ByteArrayOutputStream( nl + vl + 10 );

if ( nl < 128 )
bytes.write( b( nl ) );
else
bytes.write( new byte[] { b( nl >> 24 ), b( nl >> 16 ), b( nl >> 8 ), b( nl ) } );

if ( vl < 128 )
bytes.write( b( vl ) );
else
bytes.write( new byte[] { b( vl >> 24 ), b( vl >> 16 ), b( vl >> 8 ), b( vl ) } );

bytes.write( name.getBytes( "UTF-8" ) );
bytes.write( value.getBytes( "UTF-8" ) );

return bytes.toByteArray();
}
catch( IOException e )
{
e.printStackTrace();
}
return null;
}

public byte b( int i )
{
return (byte) i;
}
}

index.php:

<pre><?php
echo "Hello World\n";

echo "REQUEST = "; print_r($_REQUEST);
echo "GET = "; print_r($_GET);
echo "POST = "; print_r($_POST);
echo php_ini_loaded_file(), "\n";
echo file_get_contents("php://input"), "\n";
echo php_sapi_name();

?></pre>

И его результат я получаю:

6,
X-Powered-By: PHP/5.6.3
Content-type: text/html; charset=UTF-8

<pre>Hello World
REQUEST = Array
(
[yes] => no
[a] => b
)
GET = Array
(
[yes] => no
[a] => b
)
POST = Array
(
)
D:\Programs\PHP 5.6.3 x64 TS\php.ini

cgi-fcgi</pre>

Что я хочу для $_POST содержать Array( [data] => Foo Bar ) (что я и отправляю).

Кто-нибудь знает, как я могу это исправить, чтобы $_POST также заполняет данные?

0

Решение

Хорошо, если вы посмотрите на FastCGI-spec глава 3.3 под подзаголовком «Типы типов записей» это говорит:

Потоковая запись является частью потока, то есть серии из нуля или более непустых записей (длина! = 0) типа потока, за которыми следует пустая запись (длина == 0) типа потока.

Это означает, что надлежащий поток состоит по меньшей мере из одного непустого пакета, оканчивающегося пустым пакетом того же типа потока.

Вы также можете проверить Приложение B (Типичный протокольный поток сообщений) как это должно выглядеть.

Какое отношение это имеет к вашей проблеме?

PHP ожидает одну или несколько непустых «записей» в FCGI_PARAMS поток, а затем, чтобы отметить конец потока params, пустой FCGI_PARAMS пакет.

Если вы посмотрите на свой doRequest метод вы пишете один не emtpy FCGI_PARAMS пакет, а затем сразу после этого ваш FCGI_STDIN пакет.

Что делает PHP, так это читает ваш первый FCGI_PARAMS запись, а затем, если ваш FCGI_STDIN Пакет прибывает, он только читает заголовок и останавливается. Итак, следующий fcgi_read читает неверные данные и молча терпит неудачу — но PHP продолжает обрабатывать запрос с пустым потоком STDIN. Кроме того, если ваше тело запроса слишком маленькое (например, body = «a»), PHP будет блокироваться навсегда.

Если вы измените свой doRequest Метод и завершить поток с пустой записи (я буду называть их EOF-пакетами), он должен работать:

public String doRequest( Map<String, String> params, String content ) throws IOException
{
ByteArrayOutputStream paramBytes = new ByteArrayOutputStream();

for ( Entry<String, String> param: params.entrySet() )
paramBytes.write( nvpair( param.getKey() , param.getValue() ) );

Packet beginRequest = new Packet( FCGI.BEGIN_REQUEST, FCGI.NULL_REQUEST_ID, new byte[] { 0, FCGI.RESPONDER, FCGI.KEEP_CONN, 0, 0, 0, 0, 0 } );
Packet requestParams = new Packet( FCGI.PARAMS, FCGI.NULL_REQUEST_ID, paramBytes.toByteArray() );
Packet requestParamsEOF = new Packet( FCGI.PARAMS, FCGI.NULL_REQUEST_ID, new byte[] {} );
Packet requestContent = new Packet( FCGI.STDIN, FCGI.NULL_REQUEST_ID, content.getBytes( "UTF-8" ) );
Packet requestContentEOF = new Packet( FCGI.STDIN, FCGI.NULL_REQUEST_ID, new byte[] {} );

OutputStream stream = socket.getOutputStream();

stream.write( beginRequest.getBytes() );
stream.write( requestParams.getBytes() );
stream.write( requestParamsEOF.getBytes() );
stream.write( requestContent.getBytes() );
stream.write( requestContentEOF.getBytes() );

return readResponse();
}

Это сделано потому, что вы можете разделить большие потоки на несколько пакетов (поскольку каждый пакет может содержать только 64 КБ). Теоретически вам придется проверить длину тела запроса и разделить его на куски по 64 КиБ (2 ^ 16 байт). Так что даже эта версия doRequest может быть улучшено.

1

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector