Digging deeper: - SSLSocket.read() returns a premature EOF, truncating the downloaded file, which makes the md5 hash to be different. - if I fish out the SSLSocket object and set suppress_ragged_eofs = False, then I get ssl.SSLError: [Errno 8] _ssl.c:1426: EOF occurred in violation of protocol - SSLSocket.read(8192) returns chunks of 8000 bytes except the very first one (7669 bytes) and, in cases when the download does _not_ fail, the last one (5634 bytes). When the download fails, it's missing one or two last chunks (it varies). - SSLSocket.read(16384) does the same; SSLSocket.read(4096) returns pairs of chunks of 4096 and 3904 bytes, hinting that the underlying SSL communication works in multiples of 8000. My experimental code: https://gist.github.com/mgedmin/7637559 I now have two Wireshark pcap files of two SSL conversations: one failed, one successful. It's a bit beyond my skills to analyze them. I think the server did send everything (I see a TLSv1 Application Data record of size 5654, which looks like the final chunk), but Wireshark marks it as [TCP Out-Of-Order], and then there are some desperate-looking [TCP Retransmission] packets at the end. (Incidentally, the captured download was truncated at 1303669 bytes -- i.e. it was missing the last three TLS application data chunks of 8000, 8000 and 5634 bytes.) Given that Firefox/curl seem to be able to download PyPI packages over HTTPS without problems, I'd be inclined to blame it on a bug in the CPython's ssl module, maybe. But doesn't Requests use it too? Confused, Marius Gedminas -- MCSE == Marginal Computer Software Enthusiast