Skip to content

Commit 4f31f58

Browse files
authored
Fix + #154 (#155)
1 parent 36316e7 commit 4f31f58

File tree

7 files changed

+143
-9
lines changed

7 files changed

+143
-9
lines changed

AGENTS.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
# General Info
3+
4+
basic-webserver is a Roc platform:
5+
6+
Every Roc application has exactly one platform. That platform provides all the I/O primitives that the application can use; Roc's standard library provides no I/O operations, and the only way for a Roc application to execute functions in other languages is if the platform offers a way to do that.
7+
8+
Applications only interact with the Roc API portion of a platform, but there is also a host portion (written in a different language) that works behind the scenes. The host determines how the program starts, how memory is allocated and deallocated, and how I/O primitives are implemented.
9+
10+
basic-webserver is implemented in Rust and Roc.
11+
12+
# Useful Commands
13+
14+
If you are in a nix dev shell, you can run `buildcmd` to build basic-webserver and `testcmd` to run all tests. Check if you are inside a nix shell with `echo $IN_NIX_SHELL`, enter one with `nix develop`. Or, run a command inside nix with `nix develop -c command`
15+
16+
# Tests
17+
18+
Note that if something is tested in ./examples, it may not have another test in ./tests.
19+
20+
Run an individual test with:
21+
```
22+
roc build --linker=legacy tests/issue_154.roc
23+
TESTS_DIR=tests/ expect ci/expect_scripts/issue_154.exp
24+
```
25+
26+
# Style
27+
28+
- Prefer simple solutions.
29+
- Try to achieve a single source of truth when sensible.

CONTRIBUTING.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
We are committed to providing a friendly, safe and welcoming environment for all. Make sure to take a look at the Code of Conduct!
66

7+
## Tips
8+
9+
AGENTS.md is the place to find common commands and useful info. Handy for humans and AI agents.
10+
711
## How to generate docs?
812

913
You can generate the documentation locally and then start a web server to host your files.

ci/expect_scripts/form-url-encoded.exp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ expect "Listening on <http://127.0.0.1:8000>\r\n" {
1515

1616
set curlOutput [exec cat curl_form_output.txt]
1717

18-
if { [string match "*Form Data Received*" $curlOutput] && [string match "*Test+User*" $curlOutput] } {
18+
if { [string match "*Form Data Received*" $curlOutput] && [string match "*Test User*" $curlOutput] } {
1919
exit 0
2020
} else {
2121
puts "Error: curl output was different than expected: $curlOutput"
@@ -24,4 +24,4 @@ expect "Listening on <http://127.0.0.1:8000>\r\n" {
2424
}
2525

2626
puts stderr "\nError: output was different than expected."
27-
exit 1
27+
exit 1

ci/expect_scripts/issue_154.exp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/expect
2+
3+
# uncomment line below for debugging
4+
# exp_internal 1
5+
6+
set timeout 7
7+
8+
source ./ci/expect_scripts/shared-code.exp
9+
10+
spawn $env(TESTS_DIR)issue_154
11+
12+
set expected_output [normalize_output "
13+
Testing parse_form_url_encoded preserves literal plus signs.
14+
Encoded input: message=This\\+%2B\\+is\\+a\\+plus
15+
Decoded message: This \\+ is a plus
16+
Expected message: This \\+ is a plus
17+
Message decoded as expected.
18+
Ran all tests.
19+
"]
20+
21+
expect -re $expected_output {
22+
expect eof {
23+
check_exit_and_segfault
24+
}
25+
}
26+
27+
puts stderr "\nExpect script failed: output was not as expected. Diff the output with expected_output in this script. Alternatively, uncomment `exp_internal 1` to debug."
28+
exit 1

flake.nix

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,16 @@
2929
rust =
3030
pkgs.rust-bin.fromRustupToolchainFile "${toString ./rust-toolchain.toml}";
3131

32-
aliases = ''
33-
alias buildcmd='bash jump-start.sh && roc ./build.roc -- --roc roc'
34-
alias testcmd='export ROC=roc && export EXAMPLES_DIR=./examples/ && ./ci/all_tests.sh'
32+
shellFunctions = ''
33+
buildcmd() {
34+
bash jump-start.sh && roc ./build.roc -- --roc roc "$@"
35+
}
36+
export -f buildcmd
37+
38+
testcmd() {
39+
export EXAMPLES_DIR=./examples/ && ./ci/all_tests.sh "$@"
40+
}
41+
export -f testcmd
3542
'';
3643

3744
linuxInputs = with pkgs;
@@ -64,14 +71,19 @@
6471
if pkgs.stdenv.isLinux then "${pkgs.glibc.out}/lib" else "";
6572

6673
shellHook = ''
67-
${aliases}
74+
export ROC=roc
75+
76+
${shellFunctions}
6877
69-
echo "Some convenient command aliases:"
70-
echo "${aliases}" | grep -E "alias .*" -o | sed 's/alias / /' | sed 's/=/ = /'
78+
echo "Some convenient command functions:"
79+
echo "${shellFunctions}" | grep -E '^\s*[a-zA-Z_][a-zA-Z0-9_]*\(\)' | sed 's/().*//' | sed 's/^[[:space:]]*/ /' | while read func; do
80+
body=$(echo "${shellFunctions}" | sed -n "/''${func}()/,/^[[:space:]]*}/p" | sed '1d;$d' | tr '\n' ';' | sed 's/;$//' | sed 's/[[:space:]]*$//')
81+
echo " $func = $body"
82+
done
7183
echo ""
7284
'';
7385
};
7486

7587
formatter = pkgs.nixpkgs-fmt;
7688
});
77-
}
89+
}

platform/MultipartFormData.roc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,9 @@ parse_form_url_encoded = |bytes|
375375
help(tail, ParsingKey, [], [], Dict.insert(dict, key_str, value_str)),
376376
),
377377
)
378+
['+', ..] ->
379+
# '+' is a space in application/x-www-form-urlencoded payloads
380+
help(tail, state, key, List.append(chomped, ' '), dict)
378381

379382
['%', second_byte, third_byte, ..] ->
380383
hex = Num.to_u8(hex_bytes_to_u32([second_byte, third_byte]))

tests/issue_154.roc

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
app [Model, init!, respond!] {
2+
pf: platform "../platform/main.roc",
3+
}
4+
5+
import pf.Stdout
6+
import pf.Http exposing [Request, Response]
7+
import pf.MultipartFormData
8+
9+
Model : {}
10+
11+
init! : {} => Result Model _
12+
init! = |{}|
13+
when run_tests!({}) is
14+
Ok(_) ->
15+
Err(Exit(0, "Ran all tests."))
16+
17+
Err(err) ->
18+
Err(Exit(1, "Test run failed:\n\t${Inspect.to_str(err)}"))
19+
20+
run_tests! : {} => Result {} _
21+
run_tests! = |{}|
22+
Stdout.line!("Testing parse_form_url_encoded preserves literal plus signs.")?
23+
24+
encoded = "message=This+%2B+is+a+plus"
25+
Stdout.line!("Encoded input: ${encoded}")?
26+
27+
dict =
28+
(
29+
encoded
30+
|> Str.to_utf8
31+
|> MultipartFormData.parse_form_url_encoded
32+
)?
33+
34+
message =
35+
Dict.get(dict, "message")?
36+
37+
Stdout.line!("Decoded message: ${message}")?
38+
39+
expected = "This + is a plus"
40+
Stdout.line!("Expected message: ${expected}")?
41+
42+
if message == expected then
43+
Stdout.line!("Message decoded as expected.")?
44+
Ok({})
45+
else
46+
Stdout.line!("Message decoded incorrectly.")?
47+
48+
Err(StrErr("Decoded message mismatch: expected '${expected}' but got '${message}'."))
49+
50+
respond! : Request, Model => Result Response _
51+
respond! = |_, _|
52+
Ok(
53+
{
54+
status: 404,
55+
headers: [],
56+
body: [],
57+
},
58+
)

0 commit comments

Comments
 (0)