我目前遇到了终止FastCGI请求的问题。当前我拥有的代码如下:
use std::os::unix::net::{UnixStream};
use std::io::{Read, Write};
use std::str;
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_STDERR: u8 = 7;
const FCGI_RESPONDER: u16 = 1;
const FCGI_PARAMS: u8 = 4;
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();
// write the FCGI_PARAMS
let param1_name = "SCRIPT_FILENAME".as_bytes();
let param1_value = "/var/www/public/index.php".as_bytes();
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"GET";
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 params_len = params1_len + params2_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,
];
socket.write_all (¶msRequest).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();
// get the response
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();
// read the response
let mut responseHeader = [0u8; 8];
socket.read_exact (&mut responseHeader).unwrap();
// read the padding
let mut pad = vec![0; responseHeader[7] as usize];
socket.read_exact (&mut pad).unwrap();
// read the body
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!("Output: {:?}", std::str::from_utf8(&responseBody));
}
我的代码(简而言之)是使用
php-fpm
服务向.php
文件发出FastCGI请求。这段代码运行良好(到一定程度...),实际上我得到了预期的输出,如下所示:顺便说一下,我的php文件只有以下内容:Ok("X-Powered-By: PHP/8.1.11\r\nContent-type: text/html; charset=UTF-8\r\n\r\nFirst file")
<?php
echo "First file";
?>
到目前为止,一切都很好。但现在我想要做的是接收记录FCGI_END_REQUEST。为此,根据之前问题的答案(由我提出,像这样),我需要制作一个循环并读取FCGI_STDIN
的所有内容,直到我收到记录FCGI_END_REQUEST
。如果我理解有误,请告诉我,我认为应该这么做:
let mut output: String = String::new();
loop {
// 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();
// read the response
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");
break;
} else {
println!("NOT FCGI_END_REQUEST");
break;
}
}
// read the padding
let mut pad = vec![0; responseHeader[7] as usize];
socket.read_exact (&mut pad).unwrap();
// read the body
let responseLength = ((responseHeader[4] as usize) << 8) | (responseHeader[5] as usize);
let mut responseBody = Vec::new();
responseBody.resize (responseLength, 0);
let format = format!("{}", String::from_utf8_lossy(&responseBody));
output.push_str(format.as_str());
}
println!("Output: {:?}", output);
这段代码替换了我之前展示的从“获取响应”注释到文件结尾的所有第一段代码。我的做法是循环读取
FCGI_STDIN
,当遇到除FCGI_STDOUT
或FCGI_STDERR
之外的记录时,如果找到FCGI_END_REQUEST
类型的记录,则打印。现在这就是我的完整代码:use std::os::unix::net::{UnixStream};
use std::io::{Read, Write};
use std::str;
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_STDERR: u8 = 7;
const FCGI_RESPONDER: u16 = 1;
const FCGI_PARAMS: u8 = 4;
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();
// write the FCGI_PARAMS
let param1_name = "SCRIPT_FILENAME".as_bytes();
let param1_value = "/var/www/public/index.php".as_bytes();
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"GET";
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 params_len = params1_len + params2_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,
];
socket.write_all (¶msRequest).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();
let mut output: String = String::new();
loop {
// 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();
// read the response
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");
break;
} else {
println!("NOT FCGI_END_REQUEST: {}", responseHeader[1]);
break;
}
}
// read the padding
let mut pad = vec![0; responseHeader[7] as usize];
socket.read_exact (&mut pad).unwrap();
// read the body
let responseLength = ((responseHeader[4] as usize) << 8) | (responseHeader[5] as usize);
let mut responseBody = Vec::new();
responseBody.resize (responseLength, 0);
let format = format!("{}", String::from_utf8_lossy(&responseBody));
output.push_str(format.as_str());
}
println!("Output: {:?}", output);
}
问题在于这个程序的输出结果是这样的:
那么我做错了什么?我的代码有什么问题吗?NOT FCGI_END_REQUEST Output: "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
我按照评论中的指示执行了操作,打印了
responseHeader[1]
的值。因此将println!("NOT FCGI_END_REQUEST");
改为println!("NOT FCGI_END_REQUEST: {}", responseHeader[1]);
。然后我得到了以下输出:
不是 FCGI_END_REQUEST:45 输出: "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
根据我从聊天中了解到的内容,我打算在每个 socket.read_exact (&mut responseHeader)
后添加一个 println!("{:?}", foo);
。这样我的循环会是这样的:
loop {
// 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();
// read the response
let mut responseHeader = [0u8; 8];
socket.read_exact (&mut responseHeader).unwrap();
// "debug"
println!("{:?}", responseHeader);
if responseHeader[1] != FCGI_STDOUT && responseHeader[1] != FCGI_STDERR{
if responseHeader[1] == FCGI_END_REQUEST {
println!("FCGI_END_REQUEST");
break;
} else {
println!("NOT FCGI_END_REQUEST: {}", responseHeader[1]);
break;
}
}
// read the padding
let mut pad = vec![0; responseHeader[6] as usize];
socket.read_exact (&mut pad).unwrap();
// "debug";
println!("{:?}", pad);
// read the body
let responseLength = ((responseHeader[4] as usize) << 8) | (responseHeader[5] as usize);
let mut responseBody = Vec::new();
responseBody.resize (responseLength, 0);
let format = format!("{}", String::from_utf8_lossy(&responseBody));
output.push_str(format.as_str());
}
使用那段代码,我得到了这个输出:
[1, 6, 0, 1, 0, 78, 2, 0]
[88, 45]
[80, 111, 119, 101, 114, 101, 100, 45]
NOT FCGI_END_REQUEST: 111
Output: "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
根据聊天记录,我了解到我的代码存在其他问题,但我还没有完全理解它们的含义以及如何进行更正。
FCGI_STDIN
,只能向其写入内容(这也是你在此处所做的),但实际上你并不需要这样做。为了理解发生了什么,你需要打印从 FCGI 服务器接收到的消息类型(即responseHeader[1]
),因为显然它不是你预期的任何一种类型... - JmbFCGI_STDIN
是一个消息,用于在有数据要写入时发送。输出应从接收到的FCGI_STDOUT
(或FCGI_STDERR
)消息中读取。 - Jmb