Skip to content

Instantly share code, notes, and snippets.

@ereslibre
Last active May 23, 2023 14:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ereslibre/fdf25c2a0c322483ecd074a3676e8571 to your computer and use it in GitHub Desktop.
Save ereslibre/fdf25c2a0c322483ecd074a3676e8571 to your computer and use it in GitHub Desktop.
Apache APR through Wasm example run
=======================================================================
Step 1 'Same PHP script, which prints headers is configured to serve at two locations:':
=======================================================================
Alias /sample-mod-headers /usr/local/apache2/headers-filter/php-sample
Alias /sample-mod-wasm /usr/local/apache2/headers-filter/php-sample
$ curl localhost:8080/sample-mod-headers/index.php
<h1> HTTP Headers: </h1><ul>
<li><pre>Accept : */*</pre></li>
<li><pre>User-Agent : curl/7.74.0</pre></li>
<li><pre>Host : localhost:8080</pre></li>
</ul>
-----------------------------------------------------------------------
$ curl localhost:8080/sample-mod-wasm/index.php
<h1> HTTP Headers: </h1><ul>
<li><pre>Accept : */*</pre></li>
<li><pre>User-Agent : curl/7.74.0</pre></li>
<li><pre>Host : localhost:8080</pre></li>
</ul>
-----------------------------------------------------------------------
=======================================================================
Step 2 'Modifying 'target' header based on value of 'operation' header with mod_headers':
=======================================================================
httpd.conf:
<Location /sample-mod-headers>
SetEnvIf Operation "^add$" OPERATION_add
RequestHeader set "added" "added_value" env=OPERATION_add
SetEnvIf Operation "^modify$" OPERATION_modify
RequestHeader set "target" "modified_value" env=OPERATION_modify
SetEnvIf Operation "^delete$" OPERATION_delete
RequestHeader unset "target" env=OPERATION_delete
</Location>
$ curl localhost:8080/sample-mod-headers/index.php --no-progress-meter -H operation:add
<h1> HTTP Headers: </h1><ul>
<li><pre>Added : added_value</pre></li>
<li><pre>Operation : add</pre></li>
<li><pre>Accept : */*</pre></li>
<li><pre>User-Agent : curl/7.74.0</pre></li>
<li><pre>Host : localhost:8080</pre></li>
</ul>
-----------------------------------------------------------------------
$ curl localhost:8080/sample-mod-headers/index.php --no-progress-meter -H operation:delete -H target:value
<h1> HTTP Headers: </h1><ul>
<li><pre>Operation : delete</pre></li>
<li><pre>Accept : */*</pre></li>
<li><pre>User-Agent : curl/7.74.0</pre></li>
<li><pre>Host : localhost:8080</pre></li>
</ul>
-----------------------------------------------------------------------
$ curl localhost:8080/sample-mod-headers/index.php --no-progress-meter -H operation:modify -H target:value
<h1> HTTP Headers: </h1><ul>
<li><pre>Target : modified_value</pre></li>
<li><pre>Operation : modify</pre></li>
<li><pre>Accept : */*</pre></li>
<li><pre>User-Agent : curl/7.74.0</pre></li>
<li><pre>Host : localhost:8080</pre></li>
</ul>
-----------------------------------------------------------------------
=======================================================================
Step 3 'Modifying 'target' header based on value of 'operation' header with mod_wasm and edit_headers.wasm':
=======================================================================
httpd.conf:
<IfModule wasm_module>
<Location /sample-mod-wasm>
WasmFilter /usr/local/apache2/wasm_modules/rust-wasm/edit_headers.wasm
</Location>
</IfModule>
Rust source:
fn handle_operation_header(headers_handle: u64) {
if let Some(op) = apr::get_header(headers_handle, "operation") {
if op == "add" {
apr::set_header(headers_handle, "added", "added_value");
} else if op == "modify" {
apr::set_header(headers_handle, "target", "modified_value");
} else if op == "delete" {
apr::delete_header(headers_handle, "target");
}
}
}
=======================================================================
Step 4 'Evaluating a header's value with mod_wasm and edit_headers.wasm. Can't do with mod_headers':
=======================================================================
Rust source:
fn handle_eval_header(headers_handle: u64) {
if let Some(eval_me) = apr::get_header(headers_handle, "evaluate-me") {
let result = match eval(eval_me.as_str()) {
Ok(result) => result.to_string(),
Err(e) => format!("ERROR: {}", e.to_string()),
};
apr::set_header(headers_handle, "evaluate-result", result.as_str());
}
}
$ curl localhost:8080/sample-mod-wasm/index.php --no-progress-meter -H evaluate-me:3*8-2*7
<h1> HTTP Headers: </h1><ul>
<li><pre>Evaluate-Result : 10</pre></li>
<li><pre>Evaluate-Me : 3*8-2*7</pre></li>
<li><pre>Accept : */*</pre></li>
<li><pre>User-Agent : curl/7.74.0</pre></li>
<li><pre>Host : localhost:8080</pre></li>
</ul>
-----------------------------------------------------------------------
=======================================================================
Step 5 'Hash a header's value with mod_wasm and edit_headers.wasm. Can't do with mod_headers':
=======================================================================
Rust source:
fn handle_hash_header(headers_handle: u64) {
if let Some(hash_me) = apr::get_header(headers_handle, "hash-me") {
let result = compute(hash_me.as_bytes());
apr::set_header(headers_handle, "hash-result", format!("{:x}", result).as_str());
}
}
$ curl localhost:8080/sample-mod-wasm/index.php --no-progress-meter -H hash-me:The quick brown fox
<h1> HTTP Headers: </h1><ul>
<li><pre>Hash-Result : a2004f37730b9445670a738fa0fc9ee5</pre></li>
<li><pre>Hash-Me : The quick brown fox</pre></li>
<li><pre>Accept : */*</pre></li>
<li><pre>User-Agent : curl/7.74.0</pre></li>
<li><pre>Host : localhost:8080</pre></li>
</ul>
-----------------------------------------------------------------------
=======================================================================
Step 6 'Failures in mod_wasm and edit_headers.wasm don't affect Apache's stability. Can't do with a traditional module':
=======================================================================
Rust source:
fn handle_failure_header(headers_handle: u64) {
if let Some(fail_me) = apr::get_header(headers_handle, "fail-me") {
eprintln!("Simulating error '{}'...", fail_me);
if fail_me == "division-by-zero" {
let no_result = 1234 / 0;
apr::set_header(headers_handle, "dummy", format!("{}", no_result).as_str());
} else if fail_me == "filesystem-access" {
let no_contents = fs::read_to_string("/usr/local/apache2/conf/httpd.conf").unwrap();
apr::set_header(headers_handle, "dummy", no_contents.as_str());
} ...
}
}
$ curl localhost:8080/sample-mod-wasm/index.php --no-progress-meter -H fail-me:division-by-zero
curl: (52) Empty reply from server
-----------------------------------------------------------------------
$ tail -n 4 /usr/local/apache2/logs/error_log
Simulating error 'division-by-zero'...
thread '<unnamed>' panicked at 'attempt to divide by zero', src/lib.rs:46:29
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
qemu: uncaught target signal 4 (Illegal instruction) - core dumped
-----------------------------------------------------------------------
$ curl localhost:8080/sample-mod-wasm/index.php --no-progress-meter -H fail-me:filesystem-access
curl: (52) Empty reply from server
-----------------------------------------------------------------------
$ tail -n 4 /usr/local/apache2/logs/error_log
Simulating error 'filesystem-access'...
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: Custom { kind: Uncategorized, error: "failed to find a pre-opened file descriptor through which \"/usr/local/apache2/conf/httpd.conf\" could be opened" }', src/lib.rs:50:88
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
qemu: uncaught target signal 4 (Illegal instruction) - core dumped
-----------------------------------------------------------------------
Apache is still up even with the fatal failures in the Wasm code:
ps -eo comm,etime,user | grep root | grep httpd
httpd 00:15 root
^^^^^^^^^ Scroll back through the above output. It is self descriptive! ^^^^^^^^^^
mod-wasm-apr-demo
=======================================================================
Step 1 'Same PHP script, which prints headers is configured to serve at two locations:':
=======================================================================
Alias /sample-mod-headers /usr/local/apache2/headers-filter/php-sample
Alias /sample-mod-wasm /usr/local/apache2/headers-filter/php-sample
$ curl localhost:8080/sample-mod-headers/index.php
<h1> HTTP Headers: </h1><ul>
<li><pre>Host : localhost:8080</pre></li>
<li><pre>User-Agent : curl/7.74.0</pre></li>
<li><pre>Accept : */*</pre></li>
</ul>
-----------------------------------------------------------------------
$ curl localhost:8080/sample-mod-wasm/index.php
<h1> HTTP Headers: </h1><ul>
<li><pre>Host : localhost:8080</pre></li>
<li><pre>User-Agent : curl/7.74.0</pre></li>
<li><pre>Accept : */*</pre></li>
</ul>
-----------------------------------------------------------------------
=======================================================================
Step 2 'Modifying 'target' header based on value of 'operation' header with mod_headers':
=======================================================================
httpd.conf:
<Location /sample-mod-headers>
SetEnvIf Operation "^add$" OPERATION_add
RequestHeader set "added" "added_value" env=OPERATION_add
SetEnvIf Operation "^modify$" OPERATION_modify
RequestHeader set "target" "modified_value" env=OPERATION_modify
SetEnvIf Operation "^delete$" OPERATION_delete
RequestHeader unset "target" env=OPERATION_delete
</Location>
$ curl localhost:8080/sample-mod-headers/index.php --no-progress-meter -H operation:add
<h1> HTTP Headers: </h1><ul>
<li><pre>Host : localhost:8080</pre></li>
<li><pre>User-Agent : curl/7.74.0</pre></li>
<li><pre>Accept : */*</pre></li>
<li><pre>Operation : add</pre></li>
<li><pre>Added : added_value</pre></li>
</ul>
-----------------------------------------------------------------------
$ curl localhost:8080/sample-mod-headers/index.php --no-progress-meter -H operation:delete -H target:value
<h1> HTTP Headers: </h1><ul>
<li><pre>Host : localhost:8080</pre></li>
<li><pre>User-Agent : curl/7.74.0</pre></li>
<li><pre>Accept : */*</pre></li>
<li><pre>Operation : delete</pre></li>
</ul>
-----------------------------------------------------------------------
$ curl localhost:8080/sample-mod-headers/index.php --no-progress-meter -H operation:modify -H target:value
<h1> HTTP Headers: </h1><ul>
<li><pre>Host : localhost:8080</pre></li>
<li><pre>User-Agent : curl/7.74.0</pre></li>
<li><pre>Accept : */*</pre></li>
<li><pre>Operation : modify</pre></li>
<li><pre>Target : modified_value</pre></li>
</ul>
-----------------------------------------------------------------------
=======================================================================
Step 3 'Modifying 'target' header based on value of 'operation' header with mod_wasm and edit_headers.wasm':
=======================================================================
httpd.conf:
<IfModule wasm_module>
<Location /sample-mod-wasm>
WasmFilter /usr/local/apache2/wasm_modules/rust-wasm/edit_headers.wasm
</Location>
</IfModule>
Rust source:
fn handle_operation_header(headers_handle: u64) {
if let Some(op) = apr::get_header(headers_handle, "operation") {
if op == "add" {
apr::set_header(headers_handle, "added", "added_value");
} else if op == "modify" {
apr::set_header(headers_handle, "target", "modified_value");
} else if op == "delete" {
apr::delete_header(headers_handle, "target");
}
}
}
=======================================================================
Step 4 'Evaluating a header's value with mod_wasm and edit_headers.wasm. Can't do with mod_headers':
=======================================================================
Rust source:
fn handle_eval_header(headers_handle: u64) {
if let Some(eval_me) = apr::get_header(headers_handle, "evaluate-me") {
let result = match eval(eval_me.as_str()) {
Ok(result) => result.to_string(),
Err(e) => format!("ERROR: {}", e.to_string()),
};
apr::set_header(headers_handle, "evaluate-result", result.as_str());
}
}
$ curl localhost:8080/sample-mod-wasm/index.php --no-progress-meter -H evaluate-me:3*8-2*7
<h1> HTTP Headers: </h1><ul>
<li><pre>Host : localhost:8080</pre></li>
<li><pre>User-Agent : curl/7.74.0</pre></li>
<li><pre>Accept : */*</pre></li>
<li><pre>Evaluate-Me : 3*8-2*7</pre></li>
<li><pre>Evaluate-Result : 10</pre></li>
</ul>
-----------------------------------------------------------------------
=======================================================================
Step 5 'Hash a header's value with mod_wasm and edit_headers.wasm. Can't do with mod_headers':
=======================================================================
Rust source:
fn handle_hash_header(headers_handle: u64) {
if let Some(hash_me) = apr::get_header(headers_handle, "hash-me") {
let result = compute(hash_me.as_bytes());
apr::set_header(headers_handle, "hash-result", format!("{:x}", result).as_str());
}
}
$ curl localhost:8080/sample-mod-wasm/index.php --no-progress-meter -H hash-me:The quick brown fox
<h1> HTTP Headers: </h1><ul>
<li><pre>Host : localhost:8080</pre></li>
<li><pre>User-Agent : curl/7.74.0</pre></li>
<li><pre>Accept : */*</pre></li>
<li><pre>Hash-Me : The quick brown fox</pre></li>
<li><pre>Hash-Result : a2004f37730b9445670a738fa0fc9ee5</pre></li>
</ul>
-----------------------------------------------------------------------
=======================================================================
Step 6 'Failures in mod_wasm and edit_headers.wasm don't affect Apache's stability. Can't do with a traditional module':
=======================================================================
Rust source:
fn handle_failure_header(headers_handle: u64) {
if let Some(fail_me) = apr::get_header(headers_handle, "fail-me") {
eprintln!("Simulating error '{}'...", fail_me);
if fail_me == "division-by-zero" {
let no_result = 1234 / 0;
apr::set_header(headers_handle, "dummy", format!("{}", no_result).as_str());
} else if fail_me == "filesystem-access" {
let no_contents = fs::read_to_string("/usr/local/apache2/conf/httpd.conf").unwrap();
apr::set_header(headers_handle, "dummy", no_contents.as_str());
} ...
}
}
$ curl localhost:8080/sample-mod-wasm/index.php --no-progress-meter -H fail-me:division-by-zero
curl: (52) Empty reply from server
-----------------------------------------------------------------------
$ tail -n 4 /usr/local/apache2/logs/error_log
[Tue May 23 13:19:26.247359 2023] [wasm:error] [pid 14:tid 140149312698112] (-1)Unknown error -1: [client ::1:39812] content_handler() - ERROR! Couldn't deallocate Wasm execution context: '57BDC7A3'
Simulating error 'division-by-zero'...
thread '<unnamed>' panicked at 'attempt to divide by zero', src/lib.rs:46:29
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-----------------------------------------------------------------------
$ curl localhost:8080/sample-mod-wasm/index.php --no-progress-meter -H fail-me:filesystem-access
curl: (52) Empty reply from server
-----------------------------------------------------------------------
$ tail -n 4 /usr/local/apache2/logs/error_log
Simulating error 'filesystem-access'...
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: Custom { kind: Uncategorized, error: "failed to find a pre-opened file descriptor through which \"/usr/local/apache2/conf/httpd.conf\" could be opened" }', src/lib.rs:50:88
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
[Tue May 23 13:19:35.149990 2023] [core:notice] [pid 1:tid 140149973830976] AH00051: child pid 24 exit signal Illegal instruction (4), possible coredump in /usr/local/apache2
-----------------------------------------------------------------------
Apache is still up even with the fatal failures in the Wasm code:
ps -eo comm,etime,user | grep root | grep httpd
httpd 00:13 root
^^^^^^^^^ Scroll back through the above output. It is self descriptive! ^^^^^^^^^^
mod-wasm-apr-demo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment