Rust 客户端发出 FCGI 请求的问题

Agn*_*i21 6 php fastcgi rust

我目前正在与一些同事合作开发一个小型个人项目,该项目需要 FastCGI 请求,目前这是我们的代码:


#![allow(non_snake_case)]
use std::os::unix::net::{UnixStream};
use std::io::{Read, Write};

fn main() {
    // here we create the request body, which should be sent in the FCGI_STDIN record
    let data = "foo=bar";

    // here we define the constants that FastCGI needs
    const FCGI_VERSION_1: u8    = 1;

    const FCGI_BEGIN_REQUEST:u8 = 1;
    const FCGI_END_REQUEST: u8  = 3;
    const FCGI_STDIN: u8        = 5;
    const FCGI_STDOUT: u8       = 6;
    const FCGI_STDERR: u8       = 7;

    const FCGI_RESPONDER: u16  = 1;

    const FCGI_PARAMS: u8 = 4;

    // here we connect to the socket
    let socket_path = "/run/php-fpm/php-fpm.sock";

    let mut socket = match UnixStream::connect(socket_path) {
        Ok(sock) => sock,
        Err(e) => {
            println!("Couldn't connect: {e:?}");
            return
        }
    };

    // we send the record FCGI_BEGIN_REQUEST
    let requestId: u16 = 1;

    let role: u16 = FCGI_RESPONDER;

    let beginRequest = vec![
       // FCGI_Header
       FCGI_VERSION_1, FCGI_BEGIN_REQUEST,
       (requestId >> 8) as u8, (requestId & 0xFF) as u8,
       0x00, 0x08, // This is the size of `FCGI_BeginRequestBody`
       0, 0,
       // FCGI_BeginRequestBody
       (role >> 8) as u8, (role & 0xFF) as u8,
       0, // Flags
       0, 0, 0, 0, 0, // Reserved
    ];

    socket.write_all(&beginRequest).unwrap();

    // send the FCGI_PARAMS

    let param1_name = b"SCRIPT_FILENAME";
    let param1_value = b"/var/www/public/index.php";
    let lengths1 = [ param1_name.len() as u8, param1_value.len() as u8 ];
    let params1_len: u16 = (param1_name.len() + param1_value.len() + lengths1.len()) as u16;

    let param2_name = b"REQUEST_METHOD";
    let param2_value = b"POST";
    let lengths2 = [ param2_name.len() as u8, param2_value.len() as u8 ];
    let params2_len: u16 = (param2_name.len() + param2_value.len() + lengths2.len()) as u16;

    let param3_name = b"CONTENT_LENGTH";
    let content = data.len().to_string();
    let param3_value = content.as_bytes();
    let lengths3 = [ param3_name.len() as u8, param3_value.len() as u8 ];
    let params3_len: u16 = (param3_name.len() + param3_value.len() + lengths3.len()) as u16;

    let param4_name = b"CONTENT_TYPE";
    let param4_value = "application/x-www-form-urlencoded".as_bytes();
    let lengths4 = [ param4_name.len() as u8, param4_value.len() as u8 ];
    let params4_len: u16 = (param4_name.len() + param4_value.len() + lengths4.len()) as u16;

    let params_len = params1_len + params2_len + params3_len + params4_len;

    let paramsRequest = vec![
       FCGI_VERSION_1, FCGI_PARAMS,
       (requestId >> 8) as u8, (requestId & 0xFF) as u8,
       (params_len >> 8) as u8, (params_len & 0xFF) as u8,
       0, 0,
    ];

    /*
     * here we write the parameters SCRIPT_FILENAME, REQUEST_METHOD, CONTENT_LENGTH and CONTENT_TYPE (which are sent correctly)
     */
    socket.write_all (&paramsRequest).unwrap();
    socket.write_all (&lengths1).unwrap();
    socket.write_all (param1_name).unwrap();
    socket.write_all (param1_value).unwrap();
    socket.write_all (&lengths2).unwrap();
    socket.write_all (param2_name).unwrap();
    socket.write_all (param2_value).unwrap();
    socket.write_all (&lengths3).unwrap();
    socket.write_all (param3_name).unwrap();
    socket.write_all (param3_value).unwrap();
    socket.write_all (&lengths4).unwrap();
    socket.write_all (param4_name).unwrap();
    socket.write_all (param4_value).unwrap();

    /*
     * From here we start working with FCGI_STDIN. 
     * 
     * Frst we calculate the length of the content and then we send the FCGI_STDIN record together with the request body
     */
    let contentLength = data.as_bytes().len();

    let requestHeader = vec![
       FCGI_VERSION_1, FCGI_STDIN,
       (requestId >> 8) as u8, (requestId & 0xFF) as u8,
       (contentLength >> 8) as u8, (contentLength & 0xFF) as u8,
       0, 0,
    ];

    socket.write_all (&requestHeader).unwrap();

    socket.write_all (&data.as_bytes()).unwrap();


    let mut stdout: String = String::new();

    // get the response
    let requestHeader = vec![
        FCGI_VERSION_1, FCGI_STDOUT,
        (requestId >> 8) as u8, (requestId & 0xFF) as u8,
        0, 0,
        0, 0,
    ];

    socket.write_all(&requestHeader).unwrap();

    loop {
        // read the response header
        let mut responseHeader = [0u8; 8];

        socket.read_exact (&mut responseHeader).unwrap();

        if responseHeader[1] != FCGI_STDOUT && responseHeader[1] != FCGI_STDERR{

            if responseHeader[1] == FCGI_END_REQUEST {
                println!("FCGI_END_REQUEST: {:?}", responseHeader);
                break;
            } else {
                println!("NOT FCGI_END_REQUEST: {}", responseHeader[1]);
                break;
            }
        }

        // read the body
        let responseLength = ((responseHeader[4] as usize) << 8) | (responseHeader[5] as usize);

        let mut responseBody = vec![0; responseLength];

        socket.read_exact (&mut responseBody).unwrap();

        stdout.push_str(&String::from_utf8_lossy(&responseBody));

        // read the padding
        let mut pad = vec![0; responseHeader[6] as usize];

        socket.read_exact (&mut pad).unwrap();
    }
    
    println!("Output: {}", stdout)
}


Run Code Online (Sandbox Code Playgroud)

该代码将 FCGI 请求发送到 php 文件,其中包含以下内容:

<?php

var_dump($_POST);

?>
Run Code Online (Sandbox Code Playgroud)

当我运行 Rust 代码时,我得到以下输出:

FCGI_END_REQUEST: [1, 3, 0, 1, 0, 8, 0, 0]
Output: X-Powered-By: PHP/8.1.13
Content-type: text/html; charset=UTF-8

array(0) {
}
Run Code Online (Sandbox Code Playgroud)

除了 PHP 不接收请求正文之外,一切正常,在我看来, 的值$_POST不应该为空。所以我猜问题出在FCGI_STDIN记录上。我查看了FastCGI 规范,我发现的唯一相关的事情是,如果 CONTENT_LENGTH 的值与 FCGI_STDIN 接收到的字节不同,连接将被关闭,但即使我只是发送记录FCGI_STDIN而不发送请求主体我没有看到任何事情发生

我究竟做错了什么?为什么PHP收不到请求体?

更新

当我运行这个问题的答案中留下的代码时,即这段代码:

$pl = file_get_contents('php://input');

var_dump($pl);
Run Code Online (Sandbox Code Playgroud)

当从我的 rust 代码执行该 PHP 文件时,我得到一个空字符串,我得到:

 string(0) ""
Run Code Online (Sandbox Code Playgroud)

那么我的 Rust 代码有什么问题呢?为什么数据发送不正确?

Wiz*_*dNx 0

抱歉,我从未使用过 FastCGI,但是,我对查看有效负载非常有信心:)

// returns json
$pl = file_get_contents('php://input');

var_dump($pl);
Run Code Online (Sandbox Code Playgroud)

然后,您可以将结果存储在数组中以更好地处理它们:

$params = json_decode($pl, true);
Run Code Online (Sandbox Code Playgroud)

希望这将是您正在寻找的答案,
贝斯特。