Skip to content

Commit e017bf3

Browse files
committed
Fixed FCGI implementation in PHP plugin
1 parent 9bd7964 commit e017bf3

File tree

4 files changed

+67
-26
lines changed

4 files changed

+67
-26
lines changed

plugins/php/src/fcgi/params.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@ impl Params for HashMap<String, String> {
1616
result.push(name.len() as u8);
1717
} else {
1818
result.extend((name.len() as u32).to_be_bytes());
19+
let len = result.len();
20+
result[len - 4] |= 0x80;
1921
}
2022

2123
if value.len() < 128 {
2224
result.push(value.len() as u8);
2325
} else {
2426
result.extend((value.len() as u32).to_be_bytes());
27+
let len = result.len();
28+
result[len - 4] |= 0x80;
2529
}
2630

2731
result.extend(name.as_bytes());

plugins/php/src/fcgi/record.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,19 +85,19 @@ impl FcgiRecord {
8585
}
8686
}
8787

88-
impl Into<Vec<u8>> for FcgiRecord {
89-
fn into(self) -> Vec<u8> {
90-
let length = FCGI_HEADER_SIZE as u16 + self.content_length + self.padding_length as u16;
88+
impl From<FcgiRecord> for Vec<u8> {
89+
fn from(val: FcgiRecord) -> Self {
90+
let length = FCGI_HEADER_SIZE as u16 + val.content_length + val.padding_length as u16;
9191
let mut result: Vec<u8> = Vec::with_capacity(length as usize);
9292

93-
result.push(self.version);
94-
result.push(self.fcgi_type as u8);
95-
result.extend(self.request_id.to_be_bytes());
96-
result.extend(self.content_length.to_be_bytes());
97-
result.push(self.padding_length);
98-
result.push(self.reserved);
99-
result.extend(self.content_data);
100-
result.extend(self.padding_data);
93+
result.push(val.version);
94+
result.push(val.fcgi_type as u8);
95+
result.extend(val.request_id.to_be_bytes());
96+
result.extend(val.content_length.to_be_bytes());
97+
result.push(val.padding_length);
98+
result.push(val.reserved);
99+
result.extend(val.content_data);
100+
result.extend(val.padding_data);
101101

102102
result
103103
}

plugins/php/src/fcgi/request.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ impl<'a> FcgiRequest<'a> {
3232

3333
// Add the params record if it exists, then add an empty one
3434
let params: Vec<u8> = self.params.encode();
35-
if params.len() > 0 {
35+
if !params.is_empty() {
3636
let params_record: Vec<u8> =
3737
FcgiRecord::new(FcgiType::Params, &params, request_id).into();
3838
request.extend(params_record);
@@ -42,7 +42,7 @@ impl<'a> FcgiRequest<'a> {
4242
request.extend(blank_params_record);
4343

4444
// Add the content record if it exists, then add an empty one
45-
if self.content.len() > 0 {
45+
if !self.content.is_empty() {
4646
let content_record: Vec<u8> =
4747
FcgiRecord::new(FcgiType::Stdin, self.content, request_id).into();
4848
request.extend(content_record);

plugins/php/src/lib.rs

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
use humphrey::http::headers::ResponseHeaderMap;
1+
use humphrey::http::headers::{RequestHeader, ResponseHeaderMap};
22
use humphrey::http::{Request, Response, StatusCode};
33

44
use humphrey_server::config::extended_hashmap::ExtendedMap;
5-
use humphrey_server::config::Config;
65
use humphrey_server::declare_plugin;
76
use humphrey_server::plugins::plugin::{Plugin, PluginLoadResult};
87
use humphrey_server::route::try_find_path;
98
use humphrey_server::server::server::AppState;
109

1110
use std::collections::{BTreeMap, HashMap};
11+
use std::convert::TryFrom;
1212
use std::io::Write;
1313
use std::net::{Shutdown, TcpStream};
1414
use std::sync::atomic::{AtomicUsize, Ordering};
@@ -91,14 +91,17 @@ impl Plugin for PhpPlugin {
9191
let mut params: HashMap<String, String> = HashMap::new();
9292
params.insert("GATEWAY_INTERFACE".into(), "FastCGI/1.0".into());
9393
params.insert("REQUEST_METHOD".into(), request.method.to_string());
94-
params.insert("SCRIPT_NAME".into(), request.uri.clone());
94+
params.insert("REQUEST_URI".into(), format!("/{}", request.uri));
95+
params.insert("SCRIPT_NAME".into(), format!("/{}", request.uri));
9596
params.insert("SCRIPT_FILENAME".into(), file_name);
9697
params.insert("QUERY_STRING".into(), request.query.clone());
9798
params.insert("SERVER_SOFTWARE".into(), "Humphrey".into());
9899
params.insert("REMOTE_ADDR".into(), "127.0.0.1".into());
99-
params.insert("SERVER_NAME".into(), "127.0.0.1".into());
100+
params.insert("SERVER_NAME".into(), "localhost".into());
100101
params.insert("SERVER_PORT".into(), "80".into());
101102
params.insert("SERVER_PROTOCOL".into(), "HTTP/1.1".into());
103+
params.insert("PHP_SELF".into(), format!("/{}", request.uri));
104+
params.insert("DOCUMENT_ROOT".into(), directory.into());
102105
params.insert(
103106
"CONTENT_LENGTH".into(),
104107
request
@@ -109,6 +112,19 @@ impl Plugin for PhpPlugin {
109112
.to_string(),
110113
);
111114

115+
// Forward supported headers
116+
for (forwarded_header, param_name) in [
117+
(RequestHeader::Host, "HTTP_HOST"),
118+
(RequestHeader::ContentType, "CONTENT_TYPE"),
119+
(RequestHeader::Cookie, "HTTP_COOKIE"),
120+
(RequestHeader::UserAgent, "HTTP_USER_AGENT"),
121+
(RequestHeader::Authorization, "HTTP_AUTHORIZATION"),
122+
] {
123+
if let Some(value) = request.headers.get(&forwarded_header) {
124+
params.insert(param_name.into(), value.clone());
125+
}
126+
}
127+
112128
// Generate the FCGI request
113129
let empty_vec = Vec::new();
114130
let fcgi_request =
@@ -133,7 +149,9 @@ impl Plugin for PhpPlugin {
133149
loop {
134150
match FcgiRecord::read_from(&*stream) {
135151
Ok(record) => {
136-
if record.fcgi_type != FcgiType::Stdout {
152+
if record.fcgi_type != FcgiType::Stdout
153+
&& record.fcgi_type != FcgiType::Stderr
154+
{
137155
break;
138156
}
139157
records.push(record);
@@ -148,37 +166,56 @@ impl Plugin for PhpPlugin {
148166
}
149167
}
150168

151-
// Assume the content to be UTF-8 and parse the headers
152-
let content = std::str::from_utf8(&records[0].content_data).unwrap();
169+
// Assume the content to be UTF-8 and load it all
170+
let mut content_bytes: Vec<u8> = Vec::new();
171+
records.iter().fold(&mut content_bytes, |acc, val| {
172+
acc.extend(&val.content_data);
173+
acc
174+
});
175+
176+
// Parse the headers
177+
let content = std::str::from_utf8(&content_bytes).unwrap();
153178
let mut headers: ResponseHeaderMap = BTreeMap::new();
154179
let mut content_split = content.splitn(2, "\r\n\r\n");
180+
let mut status = StatusCode::OK;
155181

156182
for line in content_split.next().unwrap().lines() {
157-
let mut line_split = line.splitn(2, ":");
183+
let mut line_split = line.splitn(2, ':');
158184
let name = line_split.next().unwrap().trim();
159185
let value = line_split.next().map(|s| s.trim());
160186

161187
if let Some(value) = value {
162-
headers.insert(name.into(), value.into());
188+
if name == "Status" {
189+
if let Ok(new_status) = StatusCode::try_from(
190+
value.split(' ').next().unwrap().parse::<u16>().unwrap(),
191+
) {
192+
status = new_status;
193+
}
194+
} else {
195+
headers.insert(name.into(), value.into());
196+
}
163197
}
164198
}
165199

166200
// Create a response
167-
let mut response = Response::new(StatusCode::OK)
201+
let mut response = Response::new(status.clone())
168202
.with_bytes(content_split.next().unwrap_or("").as_bytes().to_vec());
169203

170204
// Add the headers
171205
response.headers = headers;
172206

207+
let status_code_number: u16 = status.clone().into();
208+
let status_code_string: &str = status.into();
209+
173210
state.logger.info(&format!(
174-
"{}: 200 OK (PHP Plugin) {}",
175-
request.address, request.uri
211+
"{}: {} {} (PHP Plugin) {}",
212+
request.address, status_code_number, status_code_string, request.uri
176213
));
177214

178215
// Add final headers and return the response
179216
Some(
180217
response
181-
.with_request_compatibility(&request)
218+
.with_request_compatibility(request)
182219
.with_generated_headers(),
183220
)
184221
} else {

0 commit comments

Comments
 (0)