Skip to content

Refactor multipart parser and improve routing/JSON serialization - #52

Merged
ghosthack merged 1 commit into
masterfrom
claude/vigilant-allen-k2k12x
Sep 27, 2026
Merged

ghosthack merged 1 commit into
masterfrom
claude/vigilant-allen-k2k12x

Conversation

@ghosthack

Copy link
Copy Markdown
Owner

Summary

This PR significantly refactors the multipart form data parser for better correctness and maintainability, improves HTTP routing with proper 405 Method Not Allowed responses, enhances JSON serialization to support records and enums, and adds graceful server shutdown.

Key Changes

Multipart Parser Refactoring

  • Complete rewrite of MultipartParser to use a simpler, more correct algorithm:

    • Reads entire body into memory (up to maxContentSize) instead of streaming with complex buffer management
    • Properly handles RFC 7578 multipart/form-data format with correct boundary detection
    • Eliminates use of ByteBuffer and CharsetDecoder in favor of straightforward byte array operations
    • Adds ContentTooLargeException for bodies exceeding the size limit
    • Supports unknown content lengths (e.g., chunked requests) by reading up to the limit
  • Header parsing improvements:

    • Properly parses Content-Disposition and Content-Type headers
    • Handles quoted parameter values with escaped characters (RFC 2047)
    • Case-insensitive header matching
    • Supports transport padding after boundaries (RFC 2046)
  • Comprehensive test suite added (MultipartParserTest) covering:

    • Fields and file uploads
    • Header order and case variations
    • Default content types for files
    • Boundary-like content in file bodies
    • Preamble, epilogue, and transport padding
    • Charset handling (UTF-8, ISO-8859-1)
    • Error cases (missing boundary, unterminated parts, size limits)

HTTP Routing Improvements

  • 405 Method Not Allowed responses: Routes that match a path but not the HTTP method now return 405 with an Allow header instead of 404
  • HEAD request handling: HEAD requests are automatically served by GET routes when no explicit HEAD route exists
  • Route validation: Added null checks and validation for route registration (method, path, action)
  • Encoded slash handling: Clarified that %2F (encoded slash) doesn't match path segments in exact routes (e.g., /admin%2Fsecret won't match /admin/secret)
  • Unmodifiable pattern results: PathPattern.match() now returns unmodifiable maps to prevent accidental mutations

JSON Serialization Enhancements

  • Record support: Records are serialized as JSON objects with component names as keys
  • Enum support: Enums are serialized by their name as strings
  • Character support: Character type is now serialized as a JSON string
  • Unified array handling: Refactored array serialization to use Array.get() for all primitive and object arrays, eliminating code duplication

Server Improvements

  • Graceful shutdown: Added stop(Duration grace) method that:
    • Refuses new connections immediately
    • Allows in-progress requests up to the grace period to complete
    • Interrupts remaining requests after the grace period expires
  • Logging: Added system logger for server operations

Documentation Updates

  • Updated README with clarifications on encoded slashes in paths
  • Enhanced JavaDoc for multipart parser, routing, and JSON serialization
  • Added notes about supported types in JSON serialization

Implementation Details

  • The multipart parser now uses a single-pass algorithm that finds boundaries using byte array searching rather than complex state machines
  • Header parsing is done with simple string operations after the body is fully read
  • The dispatch() method in MethodPathResolver provides a reusable pattern for implementing 405 responses and HEAD fallback
  • All changes maintain backward compatibility with existing APIs

https://claude.ai/code/session_01Leqha458EwWbRT5xWiVuyg

Security:
- Exact routes were looked up by the decoded path, so /admin%2Fsecret
  reached the /admin/secret route. Skip exact lookup (and Allow
  reporting) when the raw path contains an encoded slash.

Multipart:
- Rewrite MultipartParser: read the body as it arrives up to the limit
  instead of reserving Content-Length bytes up front, parse part headers
  properly (any order/case, extra headers, missing Content-Type,
  preamble, transport padding, quoted params), accept chunked uploads.
- Oversized bodies throw the new ContentTooLargeException; a missing
  boundary or unsupported charset is a ParseException instead of an
  IllegalArgumentException. MultipartFilter answers 413 / 400 rather
  than a 500.
- Default charset is now UTF-8, matching what browsers send.

Router and server:
- Log unhandled handler errors via System.Logger before sending 500.
- Add Server.stop(Duration) / Turismo.stop(Duration) for graceful stop.
- Validate route() arguments (non-null, path starts with '/') and
  notFound(null).
- toJson supports Character, enums, records and every array type.
- PathPattern returns unmodifiable maps as documented.

Servlet resolvers:
- ListResolver/MapResolver serve HEAD from GET routes and answer
  wrong-method requests with 405 + Allow, like the embedded server.

Adds tests for all of the above, including the first multipart tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Leqha458EwWbRT5xWiVuyg
@ghosthack
ghosthack merged commit 30d779e into master Sep 27, 2026
6 checks passed
@ghosthack
ghosthack deleted the claude/vigilant-allen-k2k12x branch September 27, 2026 05:04
ghosthack pushed a commit that referenced this pull request Sep 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants