ecdavis / pants

A lightweight framework for writing asynchronous network applications in Python.
http://pantspowered.org/
Apache License 2.0
167 stars 9 forks source link

Added fixes for WebSocket issues, and extended FileServer #62

Open nikola opened 10 years ago

nikola commented 10 years ago

I have seen error 53 (ECONNABORTED) pop up a few times when sending data over WebSockets. My investigation yields that the error is actually harmless, but still annoying when littering the log, so I've extended the error handling code to cover this gracefully.

Also, on at least one occasion length was an unpacked tuple with 1 item. Unpacking results in correct behavior, i.e. no truncation of data.

The third change is with regards to the default fallback headers in send_file, which always take effect when serving from the FileServer because there was no actual propagation of headers defined elsewhere. More specifically, the default 'max-age=604800' turned out to be an issue in my application, so instead of changing the default I have added a headers= kwarg to FileServer().

ecdavis commented 10 years ago

Thanks for your contribution. I'm adding some quick comments now but I'll take a more in-depth look when I get a chance to.

nikola commented 10 years ago

This check should be in the blocks between lines 1269 and 1279.

length being a tuple must be a result of lines 1269-1279, because in 1267 it's implicitly cast to an integer, that's why I have placed my modification at 1290, as the last check before further usage.

Mutable types should not be used as default values for parameters.

You are correct, of course. Do you want me to PR a fixed version?

ecdavis commented 10 years ago

Re: the length issue. From the Python docs for struct.unpack:

Unpack the string [...] according to the given format. The result is a tuple even if it contains exactly one item.

https://docs.python.org/2/library/struct.html#struct.unpack

So the return value should always be unpacked on lines 1272 and 1278 - also line 1311.

Good catch!

EDIT: Actually, the bug doesn't exist on 1311 because the assignment is indexed. It's worth deciding on a consistent approach to this. Indexing looks nicer but could be more error prone. I'll think about it.

nikola commented 10 years ago

@ecdavis

On mutable dicts, ironically, I was encountering a strange issue a while ago where subsequent requests against pants responded with seemingly random data. After a while I found the root cause: to prevent repeating myself I had put my default header constants into a single module-level dict, and re-used it throughout my routes, i.e. I modified the header dict in each route, which really was the module-level dict.

Unfortunately, it seems that this also happens a lot in pants, e.g. code that reads like this:

class Foo(headers=None):
    if headers is None:
        self.headers = {}
    else:
        self.headers = headers

should rather read:

class Foo(headers=None):
    if headers is None:
        self.headers = {}
    else:
        self.headers = headers.copy()

Of couse pants should not try to hold the user's hands wrt Python's language features, but in this case passing the same header dict will manifest in behavior that obfuscates the real mistake.

ecdavis commented 10 years ago

So, to summarize my comments on this PR:

Once you've made these changes please submit a new PR and I'll take a look and hopefully be able to merge. I may be somewhat pedantic about the documentation, so if you'd prefer me to write it myself let me know.

Thanks!

nikola commented 10 years ago

Ok, thanks for the feedback, I'll submit a new PR.