rust 如何使用FastCGI获得程序输出

4dbbbstv  于 2022-11-12  发布在  其他
关注(0)|答案(1)|浏览(148)

我想请您帮助解决我在Rust中使用FastCGI协议时遇到的一个问题。
这是我的代码(我有这个代码感谢我过去的问题的答案相关的主题,这是this和这个):


# ![allow(non_snake_case)]

# ![allow(unused_must_use)]

use std::os::unix::net::{UnixStream};
use std::io::{Read, Write};

fn main() {
    const FCGI_VERSION_1: u8    = 1;
    const FCGI_BEGIN_REQUEST:u8 = 1;
    const FCGI_END_REQUEST: u8 = 3;

    const FCGI_RESPONDER: u16  = 1;

    const FCGI_PARAMS: u8 = 4;

    const FCGI_GET_VALUES: u8 = 9;

    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
        }
    };

    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();

    let data = vec![
        (100) as u8,
    ];

    let contentLength = data.len();

    assert!(contentLength <= usize::MAX);

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

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

    let param_name = "SCRIPT_FILENAME".as_bytes();
    let param_value = "index.php".as_bytes();
    let lengths = [ param_name.len() as u8, param_value.len() as u8 ];
    socket.write_all (&lengths).unwrap();
    socket.write_all (param_name).unwrap();
    socket.write_all (param_value).unwrap();

    let mut response = String::new();

    socket.read_to_string(&mut response);

    print!("response: {}", response);
}

这段代码启动了一个请求,并在套接字中写入参数SCRIPT_FILENAME。我认为这段代码工作正常,在执行程序时,控制台显示如下:

response: DX-Powered-By: PHP/8.1.11
Content-type: text/html; charset=UTF-8

因此,我假设FastCGI没有执行我传递给它的PHP文件,所以我所做的是尝试找到如何执行这些文件,或者执行FastCGI规范索引中的请求。
但是我不知道怎么做。那么我怎么才能得到我传递给它的php文件的输出呢?我遗漏了什么呢?我想澄清一下,我对FastCGI是一个新手,我对二进制协议没有任何经验,所以我真的很抱歉,如果我遗漏了一些看起来很明显的东西。

mspsb9vt

mspsb9vt1#

您应该删除第二条FCGI_BEGIN_REQUEST消息,并在FCGI_PARAMS消息 * 之后 * 添加一条FCGI_STDIN消息(如果没有要发送的POST数据,请使用contentLength=0),然后使用read_exact读取响应的报头,提取其内容长度,并再次使用read_exact获取响应的内容:

use std::os::unix::net::{UnixStream};
use std::io::{Read, Write};

fn main() {
    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_RESPONDER: u16  = 1;

    const FCGI_PARAMS: u8 = 4;

    const FCGI_GET_VALUES: u8 = 9;

    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
        }
    };

    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();

    let param_name = "SCRIPT_FILENAME".as_bytes();
    let param_value = "index.php".as_bytes();
    let lengths = [ param_name.len() as u8, param_value.len() as u8 ];
    socket.write_all (&lengths).unwrap();
    socket.write_all (param_name).unwrap();
    socket.write_all (param_value).unwrap();

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

    let mut responseHeader = [0u8; 8];
    socket.read_exact (&mut responseHeader).unwrap();
    assert!(responseHeader[1] == FCGI_STDOUT);  // TODO: proper handling of message type
    let responseLength = ((responseHeader[4] as usize) << 8) | (responseHeader[5] as usize);
    let mut responseBody = Vec::new();
    responseBody.resize (responseLength, 0);
    socket.read_exact (&mut responseBody).unwrap();
    println!("{responseBody:?}");
}

备注:

  • 响应可能被拆分成几个FCGI_STDOUT消息,因此您需要一个循环来重新组装它们。
  • php可能会发送FCGI_STDOUT以外的其他消息类型,因此您需要添加对该消息类型的适当处理,以代替我使用的assert

相关问题