Author Topic: errors when POSTing  (Read 11402 times)

Offline twoerner

  • Newbie
  • *
  • Posts: 16
    • View Profile
errors when POSTing
« on: July 13, 2007, 06:35:53 PM »
I have modified parts of http.c -> fnPostFrame() -> HTTP_STATE_POSTING_DATA to look like the following:

Code: [Select]
if (HTTP_session->ucState != HTTP_STATE_DUMPING_DATA) {
    MAX_FILE_LENGTH FileLength = HTTP_session->FileLength;
    HTTP_session->FileLength = usPortLen;
    status = fnHandleWeb(POSTING_DATA_TO_APP, (CHAR *)ucIp_Data, HTTP_session);
    if (status) {
            fnHandleWeb (INFORM_POST_FAILED, 0, HTTP_session);
            return (fnDoWebPage (0, HTTP_session));
    }
    HTTP_session->FileLength = FileLength;

    if ((usPortLen) && (HTTP_session->ptrFileStart == HTTP_session->ptrFile)) {
        HTTP_session->ptrFileStart += FILE_HEADER;
    }
}

My hope was that if I detected an error while receiving the POST'ed file (e.g. checksum error) I could pass that information up (from webInterface.c -> fnHandleWeb() -> POSTING_DATA_TO_APP) and report the error back to the user as soon as possible. If everything was okay then I'd just pass zero back to report success.

This works... however it takes a long time. If there's an error the web browser spins and spins and spins and spins and it takes a long time to display the error page. Any ideas what's going on and how I could get that error page to appear quicker?

BTW: what's the last if() doing in the above code (i.e. the one with the usPortLen)?

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Re: errors when POSTing
« Reply #1 on: July 13, 2007, 11:42:35 PM »
Hi

I have taken a look at this and simulated a few possible solutions. However what you are trying to do opens up a "can of worms" at both TCP and HTTP levels. I will try to explain all below. But first the more simple answer:

Quote
BTW: what's the last if() doing in the above code (i.e. the one with the usPortLen)?

Code: [Select]
    if ((usPortLen) && (HTTP_session->ptrFileStart == HTTP_session->ptrFile)) {
        HTTP_session->ptrFileStart += FILE_HEADER;
    }

This is simply checking for the very first data frame of the post sequence. There must be data (usPortLen != 0), since it is possible to have the very first frame of the post sequence without actually data content, and the file pointer is still at the first position of the file.
The line with += FILE_HEADER is moving the pointer which will be used to save data in a file to the first location after a short header. For more info about how the file system and this header work you can look at the document: http://www.utasker.com/docs/uTasker/uTaskerFileSystem.PDF


Now back to the more tricky stuff...

The best way to see exactly what is happening is to look at the network data using Ethereal.
The browser starts posting data and somewhere during the post sequence you return a web page informing that the post has failed during the transfer. This is OK. The page is served correctly but browsers don't display what has been sent while they are still in the process of posting (although the page is actually waiting to be displayed once the post has terminated).

What you want to do is to inform as fast as possible that the post has failed, even before the post has terminated, but the browser is against us here. What needs to be done is to stop the Browser posting so that it can finally display the error message.

When the HTTP server has served the error page it requests a close of the TCP connection (the HTTP server is working in HTTP1.0 mode and this is normal). And now we get into TCP teratory because you will see that a FIN is sent and the TCP state TCP_STATE_FIN_WAIT_1 is entered. However since the browser is busy posting data, the TCP connection will not close - this is allowed, so the Browser side will acknowledge the FIN but not send its own just yet. This puts TCP in the TCP_STATE_FIN_WAIT_2 state, waiting for the other side to finally finish what is is doing and terminate the connection.

Now we come to the question as to what do we actually do with the data which we are receiving although we don't want it. If we just ignore it (which is in fact what is happening in your test case) the Browser will get stubourn and repeat until it finally realises that the connection is in fact dead. This is the reason why it is taking a very long time.

It would be possible to immediately reset the connection so that the post really terminates immediately but the result is that the Browser displays its own error message rather than the nice side which you have just served. This is therefore also not a solution.

So you finally realise that the only way to display the error message is in fact to receive all of the data (dumping it on the way if you like). At the end, either the page which was served will finally be displayed by the browser or else you can serve it in response to the status of the download (which is how the uTasker demo does it).

My conclusion is that I can't find a way to stop the browser posting and display the message served without letting the post terminate normally. I wonder whether anyone else knows of a possibility!!

What I have done is modified the TCP file slightly so that it automatically acks all received data when in the FIN_WAIT_1 and FIN_WAIT_2 states - find the file at www.uTasker.com/software/test/tcp.zip (always use the project password). See the test define "MJB_13_7_2007". This will allow your code to work without the long delay, but it still won't actually be noticably faster than serving the page at the end of the complete sequence.

I am sorry that I can't suggest an alternative at the moment but I hope that what is seen is now understood.

Regards

Mark



Offline twoerner

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: errors when POSTing
« Reply #2 on: July 16, 2007, 10:42:17 PM »
Truly awesome Mark. Thanks for your explanation.

I decided to not use your modified tcp.c and work with the data the "correct" way in which it is being presented. To this end I created a "status" variable inside the POSTING_DATA_TO_APP case. If that variable is still true when the next block of data comes in (from the POST) then process this new data, otherwise discard it. While processing the current block, if anything goes wrong set the status to false so that no further processing will occur with any subsequent data from this same POST.

Back inside http.c's HTTP_STATE_POSTING_DATA, when we've come to the end of the POST (i.e.
Code: [Select]
if (HTTP_session->FileLength == 0)) I do one more fnHandleWeb() with a new case I created: POST_STATUS. The point of making this call is to retrieve the status from all the previous POSTING_DATA_TO_APP calls. Based on that information I call fnHandleWeb with either INFORM_POST_SUCCESS or INFORM_POST_FAILED.

It's starting to come together quite nicely. A couple more hurdles (which I'll probably end up having to bug you about yet again) and the project will be done :-)