Skip to content

Commit d310555

Browse files
committed
Refactor http-test, and finish example http-server.
1 parent 56d3429 commit d310555

File tree

9 files changed

+232
-135
lines changed

9 files changed

+232
-135
lines changed

examples/http-server/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ tokio = { version = "1.6.0", features = ["full"] }
1919

2020
[dev-dependencies]
2121
phper-test = { version = "0.2.0", path = "../../phper-test" }
22+
reqwest = "0.11.3"

examples/http-server/src/errors.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use hyper::header::{InvalidHeaderName, InvalidHeaderValue};
12
use phper::classes::{ClassEntry, DynamicClass};
23
use std::net::AddrParseError;
34

@@ -18,6 +19,12 @@ pub enum HttpServerError {
1819

1920
#[error(transparent)]
2021
Hyper(#[from] hyper::Error),
22+
23+
#[error(transparent)]
24+
InvalidHeaderName(#[from] InvalidHeaderName),
25+
26+
#[error(transparent)]
27+
InvalidHeaderValue(#[from] InvalidHeaderValue),
2128
}
2229

2330
pub fn make_exception_class() -> DynamicClass<()> {

examples/http-server/src/request.rs

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
use crate::errors::HttpServerError;
2-
use phper::{
3-
classes::{DynamicClass, Visibility},
4-
values::Val,
5-
};
1+
use phper::classes::{DynamicClass, Visibility};
62

73
pub const HTTP_REQUEST_CLASS_NAME: &'static str = "HttpServer\\HttpRequest";
84

@@ -13,17 +9,5 @@ pub fn make_request_class() -> DynamicClass<()> {
139
class.add_property("server", Visibility::Public, ());
1410
class.add_property("data", Visibility::Private, ());
1511

16-
class.add_method(
17-
"getData",
18-
Visibility::Public,
19-
|this, _| {
20-
if this.get_property("data").get_type().is_null() {
21-
this.set_property("data", Val::new("Some data here"));
22-
}
23-
Ok::<_, HttpServerError>(this.duplicate_property("data"))
24-
},
25-
vec![],
26-
);
27-
2812
class
2913
}

examples/http-server/src/response.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use hyper::{Body, Response};
1+
use crate::errors::HttpServerError;
2+
use hyper::{header::HeaderName, http::HeaderValue, Body, Response};
23
use phper::{
34
classes::{DynamicClass, Visibility},
45
functions::Argument,
@@ -9,6 +10,20 @@ pub const HTTP_RESPONSE_CLASS_NAME: &'static str = "HttpServer\\HttpResponse";
910
pub fn make_response_class() -> DynamicClass<Response<Body>> {
1011
let mut class = DynamicClass::new_with_default(HTTP_RESPONSE_CLASS_NAME);
1112

13+
class.add_method(
14+
"header",
15+
Visibility::Public,
16+
|this, arguments| {
17+
let response: &mut Response<Body> = this.as_mut_state();
18+
response.headers_mut().insert(
19+
HeaderName::from_bytes(arguments[0].as_string()?.as_bytes())?,
20+
HeaderValue::from_bytes(arguments[1].as_string()?.as_bytes())?,
21+
);
22+
Ok::<_, HttpServerError>(())
23+
},
24+
vec![Argument::by_val("data")],
25+
);
26+
1227
class.add_method(
1328
"end",
1429
Visibility::Public,
Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,33 @@
1-
use phper_test::test_php_scripts;
2-
use std::{env, path::Path};
1+
use hyper::header::CONTENT_TYPE;
2+
use phper_test::test_long_term_php_script_with_condition;
3+
use reqwest::Client;
4+
use std::{env, path::Path, thread::sleep, time::Duration};
5+
use tokio::runtime;
36

47
#[test]
58
fn test_php() {
6-
test_php_scripts(
9+
test_long_term_php_script_with_condition(
710
env!("CARGO_BIN_EXE_http-server"),
8-
&[&Path::new(env!("CARGO_MANIFEST_DIR"))
11+
Path::new(env!("CARGO_MANIFEST_DIR"))
912
.join("tests")
1013
.join("php")
11-
.join("test.php")],
14+
.join("test.php"),
15+
|_| {
16+
// wait for server startup.
17+
sleep(Duration::from_secs(3));
18+
19+
runtime::Builder::new_multi_thread()
20+
.enable_all()
21+
.build()
22+
.unwrap()
23+
.block_on(async {
24+
let client = Client::new();
25+
let response = client.get("http://127.0.0.1:9000/").send().await.unwrap();
26+
let content_type = response.headers().get(CONTENT_TYPE).unwrap();
27+
assert_eq!(content_type, "text/plain");
28+
let body = response.text().await.unwrap();
29+
assert_eq!(body, "Hello World\n");
30+
});
31+
},
1232
);
1333
}

examples/http-server/tests/php/test.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
$server = new HttpServer("127.0.0.1", 9000);
1010
$server->onRequest(function ($request, $response) {
11-
// $response->header('Content-Type', 'text/plain');
11+
$response->header('Content-Type', 'text/plain');
1212
$response->end("Hello World\n");
1313
});
1414
$server->start();

phper-test/src/context.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
use crate::utils;
2+
use once_cell::sync::OnceCell;
3+
use std::{
4+
env,
5+
fs::read_to_string,
6+
io::Write,
7+
ops::{Deref, DerefMut},
8+
path::Path,
9+
process::Command,
10+
};
11+
use tempfile::NamedTempFile;
12+
13+
pub struct Context {
14+
pub php_bin: String,
15+
pub ini_content: String,
16+
}
17+
18+
impl Context {
19+
pub fn get_global() -> &'static Context {
20+
static CONTEXT: OnceCell<Context> = OnceCell::new();
21+
&CONTEXT.get_or_init(|| {
22+
let mut ini_content = String::new();
23+
24+
let php_config = env::var("PHP_CONFIG").unwrap_or("php-config".to_string());
25+
let php_bin = utils::execute_command(&[php_config.as_str(), "--php-binary"]);
26+
let ini_file = utils::execute_command(&[
27+
php_bin.as_str(),
28+
"-d",
29+
"display_errors=stderr",
30+
"-r",
31+
"echo php_ini_loaded_file();",
32+
]);
33+
let ini_files = utils::execute_command(&[
34+
php_bin.as_str(),
35+
"-d",
36+
"display_errors=stderr",
37+
"-r",
38+
"echo php_ini_scanned_files();",
39+
]);
40+
41+
if !ini_file.is_empty() {
42+
ini_content.push_str(&read_to_string(ini_file).unwrap());
43+
}
44+
if !ini_files.is_empty() {
45+
for file in ini_files.split(',') {
46+
let file = file.trim();
47+
if !file.is_empty() {
48+
ini_content.push_str(&read_to_string(file).unwrap());
49+
}
50+
}
51+
}
52+
53+
Context {
54+
php_bin,
55+
ini_content,
56+
}
57+
})
58+
}
59+
60+
pub fn create_tmp_php_ini_file(&self, lib_path: impl AsRef<Path>) -> NamedTempFile {
61+
let mut out_ini_temp_file = NamedTempFile::new().unwrap();
62+
let out_ini_file = out_ini_temp_file.as_file_mut();
63+
64+
out_ini_file.write_all(self.ini_content.as_bytes()).unwrap();
65+
out_ini_file
66+
.write_fmt(format_args!(
67+
"extension={}\n",
68+
lib_path.as_ref().to_str().unwrap()
69+
))
70+
.unwrap();
71+
72+
out_ini_temp_file
73+
}
74+
75+
pub fn create_command_with_tmp_php_ini_args(
76+
&self,
77+
tmp_php_ini_file: &NamedTempFile,
78+
script: impl AsRef<Path>,
79+
) -> ContextCommand {
80+
let mut cmd = Command::new(&self.php_bin);
81+
let args = vec![
82+
"-n".to_owned(),
83+
"-c".to_owned(),
84+
tmp_php_ini_file.path().to_str().unwrap().to_owned(),
85+
script.as_ref().to_str().unwrap().to_owned(),
86+
];
87+
cmd.args(&args);
88+
ContextCommand { cmd, args }
89+
}
90+
}
91+
92+
pub struct ContextCommand {
93+
cmd: Command,
94+
args: Vec<String>,
95+
}
96+
97+
impl ContextCommand {
98+
pub fn get_args(&self) -> &[String] {
99+
&self.args
100+
}
101+
}
102+
103+
impl Deref for ContextCommand {
104+
type Target = Command;
105+
106+
fn deref(&self) -> &Self::Target {
107+
&self.cmd
108+
}
109+
}
110+
111+
impl DerefMut for ContextCommand {
112+
fn deref_mut(&mut self) -> &mut Self::Target {
113+
&mut self.cmd
114+
}
115+
}

0 commit comments

Comments
 (0)