Skip to content

Add HTTPS detection E2E tests (#64)#112

Merged
s2x merged 9 commits intomasterfrom
fix/65-server-port
Apr 4, 2026
Merged

Add HTTPS detection E2E tests (#64)#112
s2x merged 9 commits intomasterfrom
fix/65-server-port

Conversation

@s2x
Copy link
Copy Markdown
Collaborator

@s2x s2x commented Apr 4, 2026

Summary

  • Adds E2E tests for HTTPS detection through the full SymfonyController stack
  • Tests verify HTTPS is detected from both port 443 and X-Forwarded-Proto header
  • Validates isSecure(), getScheme() return correct values

Changes

Issue

Closes #64

Piotr Hałas added 7 commits April 4, 2026 21:40
- Enables $request->getPort() for non-standard ports (8080, 8443)
- Falls back to port 80 when connection is not available
- Add CHANGELOG entry requirement to CONTRIBUTING.md
- Add SERVER_NAME to server bag to fix getPort() and getSchemeAndHttpHost()
- Extract createMockConnection() helper method to avoid DRY violation
- Add test for Host header without port scenario
- Add SERVER_NAME from getLocalIp() to fix getPort() when Host header has no port
- Add getLocalIp() to mock connection in tests
- Update CHANGELOG entry
- Detect HTTPS from connection port 443
- Detect HTTPS from X-Forwarded-Proto header
- Set HTTPS=on for HTTPS requests
- Add tests for HTTPS detection and URL generation

This fixes getScheme() returning 'http' for HTTPS requests.
- Added testHttpsE2E to verify HTTPS detection from port 443
- Added testXForwardedProtoHttpsE2E to verify X-Forwarded-Proto header detection
- Updated CHANGELOG with E2E tests entry for #64
Copy link
Copy Markdown
Collaborator Author

@s2x s2x left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Strengths

  • Full-stack E2E approach: Tests exercise the complete path (Workerman Request → SymfonyController → RequestConverter → SymfonyRequest), not just unit-level mocks
  • Both detection paths covered: Port 443 and X-Forwarded-Proto header each have dedicated tests
  • Multi-assertion validation: Each test verifies HTTPS server var, isSecure(), and getScheme() — catching regressions at multiple levels
  • Clean mock connections: Anonymous classes properly implement required methods (getLocalPort, getRemoteIp, etc.) with readonly typed properties
  • Follows existing conventions: Matches the established TestRequestTrackingKernel pattern used by other E2E tests in the file
  • CHANGELOG updated: Proper entry with issue link

Issues

Important (Should Fix)

1. No negative test for plain HTTP

  • Files: tests/SymfonyControllerTest.php
  • Problem: There is no test verifying that a regular HTTP request (port 80, no X-Forwarded-Proto) correctly returns isSecure() === false and getScheme() === 'http'.
  • Why it matters: Without a negative test, a regression that unconditionally sets HTTPS = 'on' (e.g. from an overeager refactor) would go undetected — all existing tests would still pass because they only assert the positive path.
  • How to fix: Add testHttpE2E() with a connection on port 80 and no forwarded header:
    $this->assertNull($symfonyRequest->server->get('HTTPS'));
    $this->assertFalse($symfonyRequest->isSecure());
    $this->assertSame('http', $symfonyRequest->getScheme());

2. No test for case-insensitive X-Forwarded-Proto

  • File: tests/SymfonyControllerTest.php:770
  • Problem: The implementation lowercases the header value (strtolower at RequestConverter.php:35), but there is no test confirming this works with values like "HTTPS" or "Https".
  • Why it matters: Some reverse proxies (e.g. older HAProxy configs, custom middleware) send uppercase values. Without a test, someone could remove the strtolower call and the test suite would not catch it.
  • How to fix: Either add a dedicated test case with X-Forwarded-Proto: HTTPS, or add an additional assertion in the existing test with a mixed-case value.

Minor (Nice to Have)

3. Duplicated mock connection classes

  • Files: tests/SymfonyControllerTest.php:726-748, tests/SymfonyControllerTest.php:776-798, and tests/RequestConverterTest.php:344-362
  • Problem: The anonymous TcpConnection subclass is copy-pasted across two tests in this file and a third time in RequestConverterTest.php.
  • Why it matters: If TcpConnection gains a new abstract method or changes its constructor signature, all three copies need updating. This is a maintenance burden and a source of future test breakage.
  • How to fix: Extract to a private helper method createMockConnection(int \$port): TcpConnection in each test class, or better — share it via a trait or a common base test class. RequestConverterTest.php already has a createMockConnection() helper that could be reused.

4. No E2E verification of SERVER_PORT value

  • Files: RequestConverter.php:44, tests/SymfonyControllerTest.php
  • Problem: The converter sets SERVER_PORT based on the HTTPS detection logic (\$localPort ?? (\$isHttps ? 443 : 80)). Unit tests in RequestConverterTest cover this, but the E2E tests do not verify it propagates through the full stack.
  • Why it matters: getPort() is used by getSchemeAndHttpHost() and URL generation. If the port mapping breaks somewhere between RequestConverter and the final Request object, the E2E tests would not catch it.
  • How to fix: Add assertions to the HTTPS E2E tests:
    \$this->assertSame(443, \$symfonyRequest->getPort());
    \$this->assertSame(80, \$symfonyRequest->getPort()); // in the HTTP negative test

Assessment

Ready to merge: No — with fixes

Reasoning: The implementation is solid and the positive-path E2E tests are well-written. The missing negative test (plain HTTP should stay HTTP) is a genuine test gap that could mask a regression where all requests are incorrectly marked as HTTPS. The case-insensitivity test is lower priority but still valuable for defense-in-depth.

Copy link
Copy Markdown
Collaborator Author

@s2x s2x left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Strengths

  • Full-stack E2E approach: Tests exercise the complete path (Workerman Request → SymfonyController → RequestConverter → SymfonyRequest), not just unit-level mocks
  • Both detection paths covered: Port 443 and X-Forwarded-Proto header each have dedicated tests
  • Multi-assertion validation: Each test verifies HTTPS server var, isSecure(), and getScheme() — catching regressions at multiple levels
  • Clean mock connections: Anonymous classes properly implement required methods (getLocalPort, getRemoteIp, etc.) with readonly typed properties
  • Follows existing conventions: Matches the established TestRequestTrackingKernel pattern used by other E2E tests in the file
  • CHANGELOG updated: Proper entry with issue link

Issues

Important (Should Fix)

1. No negative test for plain HTTP

  • Files: tests/SymfonyControllerTest.php
  • Problem: There is no test verifying that a regular HTTP request (port 80, no X-Forwarded-Proto) correctly returns isSecure() === false and getScheme() === 'http'.
  • Why it matters: Without a negative test, a regression that unconditionally sets HTTPS = 'on' (e.g. from an overeager refactor) would go undetected — all existing tests would still pass because they only assert the positive path.
  • How to fix: Add testHttpE2E() with a connection on port 80 and no forwarded header:
    $this->assertNull($symfonyRequest->server->get('HTTPS'));
    $this->assertFalse($symfonyRequest->isSecure());
    $this->assertSame('http', $symfonyRequest->getScheme());

2. No test for case-insensitive X-Forwarded-Proto

  • File: tests/SymfonyControllerTest.php:770
  • Problem: The implementation lowercases the header value (strtolower at RequestConverter.php:35), but there is no test confirming this works with values like "HTTPS" or "Https".
  • Why it matters: Some reverse proxies (e.g. older HAProxy configs, custom middleware) send uppercase values. Without a test, someone could remove the strtolower call and the test suite would not catch it.
  • How to fix: Either add a dedicated test case with X-Forwarded-Proto: HTTPS, or add an additional assertion in the existing test with a mixed-case value.

Minor (Nice to Have)

3. Duplicated mock connection classes

  • Files: tests/SymfonyControllerTest.php:726-748, tests/SymfonyControllerTest.php:776-798, and tests/RequestConverterTest.php:344-362
  • Problem: The anonymous TcpConnection subclass is copy-pasted across two tests in this file and a third time in RequestConverterTest.php.
  • Why it matters: If TcpConnection gains a new abstract method or changes its constructor signature, all three copies need updating. This is a maintenance burden and a source of future test breakage.
  • How to fix: Extract to a private helper method createMockConnection(int \$port): TcpConnection in each test class, or better — share it via a trait or a common base test class. RequestConverterTest.php already has a createMockConnection() helper that could be reused.

4. No E2E verification of SERVER_PORT value

  • Files: RequestConverter.php:44, tests/SymfonyControllerTest.php
  • Problem: The converter sets SERVER_PORT based on the HTTPS detection logic (\$localPort ?? (\$isHttps ? 443 : 80)). Unit tests in RequestConverterTest cover this, but the E2E tests do not verify it propagates through the full stack.
  • Why it matters: getPort() is used by getSchemeAndHttpHost() and URL generation. If the port mapping breaks somewhere between RequestConverter and the final Request object, the E2E tests would not catch it.
  • How to fix: Add assertions to the HTTPS E2E tests:
    \$this->assertSame(443, \$symfonyRequest->getPort());
    \$this->assertSame(80, \$symfonyRequest->getPort()); // in the HTTP negative test

Assessment

Ready to merge: No — with fixes

Reasoning: The implementation is solid and the positive-path E2E tests are well-written. The missing negative test (plain HTTP should stay HTTP) is a genuine test gap that could mask a regression where all requests are incorrectly marked as HTTPS. The case-insensitivity test is lower priority but still valuable for defense-in-depth.

Piotr Hałas added 2 commits April 4, 2026 23:02
- Added testHttpE2E() for plain HTTP (port 80, no HTTPS)
- Added testXForwardedProtoCaseInsensitiveE2E() for uppercase header values
- Added createMockConnection() helper to reduce duplication
- Added SERVER_PORT assertions to verify getPort() works through full stack
@s2x s2x merged commit aca7d0f into master Apr 4, 2026
1 check passed
@s2x s2x deleted the fix/65-server-port branch April 4, 2026 21:07
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.

RequestConverter — Missing HTTPS flag breaks isSecure(), getScheme(), and URL generation

1 participant