2014-12-13 52 views
0

我正在写一个Java CGI客户端来与PHP-CGI交谈,但我碰到了一个凹凸,我的php $_POST没有填充任何数据,而我肯定发送了一些。我不知道为什么这样做,我无法在任何地方找到这个问题。PHP-CGI post empty

我使用干净的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也填充数据?

回答

1

那么,如果你看一下FastCGI-spec chapter 3.3 under the subtitle "Types of Record Types"它说:

一个流记录是流的一部分,即一系列的零个或多个非空的记录(!长度= 0)流式,然后是流类型的空记录(长度== 0)。

这意味着适当的流由至少一个非空的分组,由相同的流类型的空分组终止。

你也可以看看Appendix B (Typical Protocol Message Flow)看起来应该如何。

这与您的问题有什么关系?

PHP期望一个或多个非空“写入”到FCGI_PARAMS流,然后,到标记PARAMS流结束时,一个空的数据包FCGI_PARAMS

如果你看看你的doRequest方法,你正在写一个非空的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 KiB的有效载荷)。理论上你必须检查请求主体的长度并将其分成64 KiB(2^16字节)的块。所以即使这个版本的doRequest可以改进。

+0

哇,谢谢!今天我学到了一些新东西:)我会好好看看并做出改进。 – Gelunox 2014-12-13 15:27:14