Native Request/Response Parity Contract
This contract records the current shell/native drift surface for request parsing and native-owned response defaults.
The goal is not to claim the native listener and every compiled host are already backed by one implementation. The goal is to make the current boundary explicit so request parsing and fallback response behavior do not drift silently.
The current boundary should be treated as transitional rather than fundamental. Local and CI validation should increasingly exercise the native listener together with compiled-host behavior so drift trends toward zero.
Reference surface
Current native reference behavior lives in:
src/native_runtime/c/pharos_runtime.csrc/native_runtime/c/native_http_log.csrc/native_runtime/c/native_http_response.csrc/native_runtime/c/native_http_access_log.csrc/native_runtime/c/native_static_serve.c
Current shell/runtime compiled-host wrapper behavior lives in:
runtime/pharos_app_runtime_layer.sh
Key native functions/paths:
parse_http_requestnative_send_text_responsehttp_status_line_from_response_nativehttp_status_code_from_response_native- higher-level fail-closed request paths in
handle_app_or_runtime_request - static artifact response ownership in
native_serve_static_file
Contract behaviors to preserve or explicitly revise
1. Request line parsing
The native listener should continue to parse and preserve:
- HTTP method
- request target/path
- header/body boundary from either
\r\n\r\nor\n\n
Shell/runtime listener wrappers should normalize an empty or missing extracted request target to / rather than forwarding an empty path deeper into the stack.
Malformed request lines currently fail closed.
Shell/runtime listener wrappers should also fail closed when a request line is malformed instead of treating the missing fields as an implied method/path.
2. Header normalization for forwarded app requests
The native listener should continue to forward headers as newline-separated key:value lines and preserve the current extraction posture for:
Content-LengthContent-TypeCookie
Shell/runtime forwarding should preserve header values after the first : rather than truncating values that themselves contain additional colons.
For duplicate forwarded headers, the current parity rule should remain explicit:
- the forwarded
headersblob preserves duplicate lines in arrival order - scalar extracted request metadata (
Cookie,Content-Type,Content-Length) uses the last matching header occurrence case-insensitively - if this policy changes later, it must change intentionally across both native and shell/runtime forwarding surfaces
3. Header block completion and partial-request handling
The current request parser boundary should remain fail-closed when the listener never receives a completed header block.
Shell/runtime listener wrappers should not continue request dispatch after EOF if they never observed the terminating blank header line.
4. Content-Length validation and body completeness
Content-Length handling should remain fail-closed at the request parser boundary.
Shell/runtime listeners should reject:
- non-numeric
Content-Lengthvalues - partial bodies that do not deliver the promised byte count
They should not silently treat malformed or truncated bodies as a valid empty request body.
5. Default request metadata
When request metadata is absent, current native behavior should remain explicit:
- missing content type →
application/x-www-form-urlencoded - missing cookie header → empty string
- multipart detection comes from
Content-Typecontainingmultipart/form-data
6. Native-owned response shaping
When the native listener, static server, or native runtime orchestration owns the response directly, current defaults should remain explicit:
- plain-text fallback responses
charset=utf-8Connection: close- explicit
Content-Length - extensionless
/api/...artifact responses that are framework JSON surfaces should not silently degrade toapplication/octet-stream
7. Request body transport shaping
For forwarded compiled app requests, the shell/native boundary should keep the request body transport rule explicit:
- non-multipart request bodies travel through
--form - multipart request bodies travel through
--body-file - when both are present at a compiled-host wrapper boundary,
--body-fileshould take precedence over stale inline form text
8. Compiled app failure defaults
When the compiled app request path fails, current fail-closed behavior should remain explicit across native and shell/runtime forwarding surfaces:
- child failure or empty response →
502 Bad Gateway - detail text may be preserved for request-error logging
- native success path forwards child bytes unchanged rather than rewriting them
- shell/runtime forwarding should not silently downgrade compiled-host execution failures into
500 Internal Server Errorwhen the failure is at the app-host boundary
Allowed divergence rule
If shell/runtime helpers, compiled hosts, and the native listener intentionally diverge for one of these behaviors, that divergence must be:
- documented explicitly
- justified by runtime ownership or platform constraints
- reflected in checked contracts/tests/docs
Silent request/response drift is out of contract.
Preferred convergence check path
Where the host can build a runnable native target, prefer:
scripts/check.sh native-compiled-flow
That command makes the combined path a first-class validation surface instead of a late packaging-only concern.