Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

# General Info

basic-webserver is a Roc platform:

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.

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.

basic-webserver is implemented in Rust and Roc.

# Useful Commands

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`

# Tests

Note that if something is tested in ./examples, it may not have another test in ./tests.

Run an individual test with:
```
roc build --linker=legacy tests/issue_154.roc
TESTS_DIR=tests/ expect ci/expect_scripts/issue_154.exp
```

# Style

- Prefer simple solutions.
- Try to achieve a single source of truth when sensible.
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

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

## Tips

AGENTS.md is the place to find common commands and useful info. Handy for humans and AI agents.

## How to generate docs?

You can generate the documentation locally and then start a web server to host your files.
Expand Down
4 changes: 2 additions & 2 deletions ci/expect_scripts/form-url-encoded.exp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ expect "Listening on <http://127.0.0.1:8000>\r\n" {

set curlOutput [exec cat curl_form_output.txt]

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

puts stderr "\nError: output was different than expected."
exit 1
exit 1
28 changes: 28 additions & 0 deletions ci/expect_scripts/issue_154.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/expect

# uncomment line below for debugging
# exp_internal 1

set timeout 7

source ./ci/expect_scripts/shared-code.exp

spawn $env(TESTS_DIR)issue_154

set expected_output [normalize_output "
Testing parse_form_url_encoded preserves literal plus signs.
Encoded input: message=This\\+%2B\\+is\\+a\\+plus
Decoded message: This \\+ is a plus
Expected message: This \\+ is a plus
Message decoded as expected.
Ran all tests.
"]

expect -re $expected_output {
expect eof {
check_exit_and_segfault
}
}

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."
exit 1
26 changes: 19 additions & 7 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,16 @@
rust =
pkgs.rust-bin.fromRustupToolchainFile "${toString ./rust-toolchain.toml}";

aliases = ''
alias buildcmd='bash jump-start.sh && roc ./build.roc -- --roc roc'
alias testcmd='export ROC=roc && export EXAMPLES_DIR=./examples/ && ./ci/all_tests.sh'
shellFunctions = ''
buildcmd() {
bash jump-start.sh && roc ./build.roc -- --roc roc "$@"
}
export -f buildcmd

testcmd() {
export EXAMPLES_DIR=./examples/ && ./ci/all_tests.sh "$@"
}
export -f testcmd
'';

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

shellHook = ''
${aliases}
export ROC=roc

${shellFunctions}

echo "Some convenient command aliases:"
echo "${aliases}" | grep -E "alias .*" -o | sed 's/alias / /' | sed 's/=/ = /'
echo "Some convenient command functions:"
echo "${shellFunctions}" | grep -E '^\s*[a-zA-Z_][a-zA-Z0-9_]*\(\)' | sed 's/().*//' | sed 's/^[[:space:]]*/ /' | while read func; do
body=$(echo "${shellFunctions}" | sed -n "/''${func}()/,/^[[:space:]]*}/p" | sed '1d;$d' | tr '\n' ';' | sed 's/;$//' | sed 's/[[:space:]]*$//')
echo " $func = $body"
done
echo ""
'';
};

formatter = pkgs.nixpkgs-fmt;
});
}
}
3 changes: 3 additions & 0 deletions platform/MultipartFormData.roc
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,9 @@ parse_form_url_encoded = |bytes|
help(tail, ParsingKey, [], [], Dict.insert(dict, key_str, value_str)),
),
)
['+', ..] ->
# '+' is a space in application/x-www-form-urlencoded payloads
help(tail, state, key, List.append(chomped, ' '), dict)

['%', second_byte, third_byte, ..] ->
hex = Num.to_u8(hex_bytes_to_u32([second_byte, third_byte]))
Expand Down
58 changes: 58 additions & 0 deletions tests/issue_154.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
app [Model, init!, respond!] {
pf: platform "../platform/main.roc",
}

import pf.Stdout
import pf.Http exposing [Request, Response]
import pf.MultipartFormData

Model : {}

init! : {} => Result Model _
init! = |{}|
when run_tests!({}) is
Ok(_) ->
Err(Exit(0, "Ran all tests."))

Err(err) ->
Err(Exit(1, "Test run failed:\n\t${Inspect.to_str(err)}"))

run_tests! : {} => Result {} _
run_tests! = |{}|
Stdout.line!("Testing parse_form_url_encoded preserves literal plus signs.")?

encoded = "message=This+%2B+is+a+plus"
Stdout.line!("Encoded input: ${encoded}")?

dict =
(
encoded
|> Str.to_utf8
|> MultipartFormData.parse_form_url_encoded
)?

message =
Dict.get(dict, "message")?

Stdout.line!("Decoded message: ${message}")?

expected = "This + is a plus"
Stdout.line!("Expected message: ${expected}")?

if message == expected then
Stdout.line!("Message decoded as expected.")?
Ok({})
else
Stdout.line!("Message decoded incorrectly.")?

Err(StrErr("Decoded message mismatch: expected '${expected}' but got '${message}'."))

respond! : Request, Model => Result Response _
respond! = |_, _|
Ok(
{
status: 404,
headers: [],
body: [],
},
)