rust中客户端FCGI请求的问题



我目前正在与一些同事在一个需要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)
}

该代码向php文件发送FCGI请求,该文件的内容如下:

<?php
var_dump($_POST);
?>

当我运行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) {
}

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

我做错了什么?为什么PHP不接收请求体?

更新当我运行这个问题的答案中剩下的代码,也就是下面的代码:

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

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

string(0) ""

那么我的rust代码中的问题是什么?为什么数据没有正确发送?

对不起,我从来没有使用过FastCGI,但是,我很有信心有一个看在有效载荷:)

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

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

$params = json_decode($pl, true);

希望这将是你正在寻找的答案,
最好的。

最新更新