Skip to content

Conversation

@majiayu000
Copy link

Summary

  • Return HTTP 405 Method Not Allowed for non-GET requests (HEAD, POST, etc.) instead of raising an exception
  • This reduces log noise from bots and crawlers hitting WebSocket endpoints

Changes

  • Added InvalidMethod exception to websockets.exceptions
  • Modified Request.parse() in http11.py to raise InvalidMethod instead of ValueError for unsupported methods
  • Updated ServerProtocol.parse() in server.py to catch InvalidMethod and return a proper 405 response with Allow: GET header
  • Updated tests to verify the new behavior

Test plan

  • Added tests for HEAD and POST requests returning 405
  • Updated existing tests to expect InvalidMethod instead of ValueError
  • All tests pass

Fixes #1677

Instead of raising an exception when receiving HEAD, POST, or other
non-GET HTTP methods, the server now properly returns a 405 Method
Not Allowed response with an Allow: GET header.

This change:
- Adds InvalidMethod exception for unsupported HTTP methods
- Modifies Request.parse() to raise InvalidMethod instead of ValueError
- Handles InvalidMethod in ServerProtocol.parse() to return 405 response
- Updates tests accordingly

Fixes python-websockets#1677
Copy link
Member

@aaugustin aaugustin left a comment

Choose a reason for hiding this comment

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

See last comment for why I stopped reviewing.

* :exc:`ProxyError`
* :exc:`InvalidProxyMessage`
* :exc:`InvalidProxyStatus`
* :exc:`InvalidMethod`
Copy link
Member

Choose a reason for hiding this comment

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

Should be after InvalidMessage -- you can only tell which method is used after you're successfully parsed the message.

"ProxyError",
"InvalidProxyMessage",
"InvalidProxyStatus",
"InvalidMethod",
Copy link
Member

Choose a reason for hiding this comment

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

Same.

def __str__(self) -> str:
return f"invalid HTTP method; expected GET; got {self.method}"


Copy link
Member

Choose a reason for hiding this comment

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

Same.

self.parser = self.discard()
next(self.parser) # start coroutine
yield
return
Copy link
Member

Choose a reason for hiding this comment

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

Here you're calling reject(), a high level function, from the parser, a low level component.

This doesn't make sense from a layering perspective. Can you explain why you're done that?

I suspect the honest answer is "you just threw AI at the issue and didn't bothing reviewing" :-(

I can throw AI at an issue myself, thank you. Stopping the review there until I hear back.

- Move InvalidMethod exception after InvalidMessage in hierarchy,
  docstrings, and __all__ (since method is known only after parsing)
- Build 405 response directly in parse() without calling reject()
  to maintain proper layering between low-level parser and high-level API
- Remove redundant discard() setup (send_response already handles it)
- Add InvalidMethod to Request.parse() docstring

Signed-off-by: majiayu000 <1835304752@qq.com>
@majiayu000
Copy link
Author

Thanks for the feedback. You're right about the layering issue - I've fixed it:

  1. Moved InvalidMethod after InvalidMessage in all three places
  2. Removed the call to reject() - now building the Response directly in parse()
  3. Removed redundant discard() setup since send_response() already handles it
  4. Updated the docstring in http11.py

I apologize for the initial oversight. The code has been updated.

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.

HEAD raises an exception

2 participants