Issue #847 resolved

safari not interacting correctly (cherrypy 3.1)

created an issue

On random requests, safari will issue GETs, seemingly recieve a reply, but will just display a empty (blank) page. Additionaly safari will show the default favicon.ico that comes with cherrypy (another favicon is set via path shunning and per html meta tag and loaded correctly for other requests).

Comments (11)

  1. Anonymous

    Can you provide any more detail here?

    I know that Safari can be VERY temperamental about renegotiating SSL handshakes under differing authentication requirements under Apache and produce the behavior you describe. (SSLOptions +!OptRenegotiate seems to help Apache with that.)

  2. Anonymous

    A user spotted this again on a setup where Apache is proxying to a Django app running CherryPy's wsgiserver (3.1.1).

    Versions reported by the user:

    Apache 2.x.x, Safari 3.1.2 and OSX 10.5.6

    Switching to Firefox worked for them, so they're not pursuing it further. Sorry.

  3. Anonymous

    I'm seeing this one as well. I don't believe I saw it before upgrading to 3.1.1, but maybe it happened with 3.1.0 as well. I'm not using SSL, so I don't think that is the problem.

    Safari returns a totally blank page. Safari logs this when it occurs: NOT_FOUND_ERR DOMException 8. No logging at all by CherryPy; it doesn't appear to get far enough to log the request at all. A Safari refresh normally works.

    I'm trying to nail down this one right now, but it is very difficult to reproduce since it doesn't happen often.

  4. Anonymous

    I tracked this one down and I have a proposed fix. It is a bit complicated. I was wrong as far as this being a new bug to 3.1.1; I was able to reproduce it in 3.0.0 as well.

    Here is a step-by-step that illustrates the problem:

    1) Safari makes a HTTP 1.1 request and CP responds correctly.

    2) CP leaves the connection open (is persistent).

    3) CP parse_request / readline times out after a few seconds when no further HTTP request comes in.

    4) CP handles the timeout by sending a 408 Request Timeout and closing the socket/connection.

    5) If, within a second or so after the timeout occurs, you have Safari make another CP request it will then receive the queued up 408 response. Safari doesn't handle this 408 response by resending the request. Instead, it shows a blank page and no error.

    If you wait more than a second after CP sends the 408, then it appears Safari realizes the socket has closed and the next HTTP request goes out on a new connection (page loads correctly). So there is a small window of opportunity in which the problem can be seen. I was able to reproduce it 100% of the time though by adding debugging logging when CP sends the simple_response(408), and then immediately having Safari reload the page. If you wait more than a second or so after the 408 though it doesn't occur.

    So now why is this occurring exactly? It seems that Safari should handle the 408 better. But the HTTP 1.1 spec says, "The client MAY repeat the request." In this case Safari is opting not to repeat the request. Note from Safari's perspective the 408 response is coming in response to the second HTTP request. The first request was filled successfully, then the timeout occurs and CP sends the 408. But at this point Safari isn't looking for any responses because it hasn't yet sent a request, so it doesn't yet see the 408. If Safari then does make a new HTTP request (browser refresh) it immediately sees the queued 408. Perhaps that is confusing Safari -- it hasn't been notified of the socket close yet so it sends a request and immediately gets a 408 without any delay and therefore decides it shouldn't resend the request. I don't know... I'm just guessing what Safari might be thinking.

    I think part of the problem here is that CP is sending a HTTP response when there is no outstanding HTTP request. This socket level timeout error can then be received by the *next* HTTP request the client makes because the socket shutdown can take a second to complete. My proposed solution is to change the timed out conditional in communicates()'s exception handler from:

       if errnum == 'timed out':


       if not req.ready and errnum == 'timed out':

    Now when a timeout occurs but no HTTP request is outstanding CP will just close down the socket/connection. This appears to fix the Safari case. I also tested FireFox and it too continues to work even if a request is made immediately after the timeout exception is hit.

    This also appears to be valid based on the verbiage of RFC2616. It says:

    "When a client or server wishes to time-out it SHOULD issue a graceful close on the transport connection. Clients and servers SHOULD both constantly watch for the other side of the transport close, and respond to it as appropriate. If a client or server does not detect the other side's close promptly it could cause unnecessary resource drain on the network.
    A client, server, or proxy MAY close the transport connection at any time. For example, a client might have started to send a new request at the same time that the server has decided to close the "idle" connection. From the server's point of view, the connection is being closed while it was idle, but from the client's point of view, a request is in progress.
    This means that clients, servers, and proxies MUST be able to recover from asynchronous close events. Client software SHOULD reopen the transport connection and retransmit the aborted sequence of requests without user interaction so long as the request sequence is idempotent (see section 9.1.2)."

    It doesn't mention that a 408 response should be sent, so I think just closing down the socket/connection should be enough *especially* in this case where there isn't an outstanding HTTP request.

    Opinions / thoughts?

    Matt Bendiksen

  5. Anonymous

    Typo above.... the conditional should be changed to:

    if req.ready and errnum == 'timed out':
  6. visteya

    Revised the fix to be similar to the fix which is in the /python3 branch [2272].

    This fix [2281] is slightly different from the one in /python3. It only sends a 408 if the timeout occurs while in the middle of a request. When no request has been seen on the connection, it does not send a 408; it just closes the connection.

  7. Log in to comment