python-websockets / websockets

Library for building WebSocket servers and clients in Python
https://websockets.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
5.23k stars 518 forks source link

Client throws "Malformed HTTP message" exception if a server doesn't give HTTP "reason phrase" #159

Closed ncraike closed 7 years ago

ncraike commented 7 years ago

During the initial connection handshake, a server typically responds to the upgrade request with a response like:

HTTP/1.1 101 Switching Protocols
Connection: upgrade
Upgrade: websocket
Sec-WebSocket-Accept: ...

However, if the server omits the human-readable text after the HTTP/1.1 101 (conventionally "Switching Protocols"), the websockets.connect call throws an exception like this:

  File ".../lib/python3.5/site-packages/websockets/client.py", line 165, in connect
    extra_headers=extra_headers)
  File ".../lib/python3.5/site-packages/websockets/client.py", line 81, in handshake
    raise InvalidHandshake("Malformed HTTP message") from exc
websockets.exceptions.InvalidHandshake: Malformed HTTP message

I understand it's debatable as to whether omitting the human-readable text (the "reason phrase") is valid for a server, but I think in the interests of working with different WebSockets server implementations, the client should accept the HTTP response even if the "reason phrase" is omitted, and not throw an exception.

RFC6455 Section 4.2.2 mentions the status line of the server response during the handshake, but directs the reader to RFC2616 (HTTP/1.1 ) for a full definition.

RFC2616 Section 6.1 describes the "status line" of a response including the human-readable message, described as a "reason phrase".


For reference:

The above exception was thrown on my laptop running macOS 10.11.6 (El Capitan) and Homebrew-installed Python 3.5, with websockets installed in a virtualenv. Output from pip:

$ pip show websockets
Name: websockets
Version: 3.2
Summary: An implementation of the WebSocket Protocol (RFC 6455)
Home-page: https://github.com/aaugustin/websockets
Author: Aymeric Augustin
Author-email: aymeric.augustin@m4x.org
License: BSD
Location: /Users/nathan/.python3_virtualenvs/myproject/lib/python3.5/site-packages
Requires:
kelunik commented 7 years ago

The omission of a reason phrase is always valid, that's not debatable. Reason-Phrase = *<TEXT, excluding CR, LF>. * is a sequence of tokens and there's no minimum defined. The space after the status code is required though.

ncraike commented 7 years ago

Okay. That may be the case, but I think both server and client implementations are going to have to be tolerant of some non-standard behaviour.

kelunik commented 7 years ago

Sure, it this case it's fine, as adding it doesn't break the spec.