Ignoring a double form submission caused by a timeout error is only a
small part of the problem.
A more important issue is resolving double form submissions that are a
direct result of browsing commands by the user. If a server receives
a `pizza order' form for a second time, this could mean
0) that some network timeout error occurred
1) that the user wants to order two pizza's
2) that the user wants to change his original order
3) that a browsing command caused the old form to be resubmitted
without the user being aware of this
Currently, a service author has almost no means to resolve this
ambiguity.
If 2) above sounds implausible, consider the following scenario:
- User fills in a pizza order form and presses `submit'
- Two seconds later, the user decides he'd rather not have onions on
the pizza, so he quickly presses `stop' on the browser, deselects
the onions in the form, and presses `submit' again.
- After 3 seconds, the browser screen shows `you now have ordered
a pizza ....no onions....'
- After a while, two pizza's are delivered to an astonished user.
There are several ways in which 3), an unintentional resubmission, can
happen. For example,
a) After ordering a pizza, the user wants to save the confirmation
message to disk for later reference. The user enables `save to local
disk' on the client, and presses the reload button. On most clients,
this will cause the form to be submitted for a second time, which will
cause a second pizza to be ordered.
b) Ten minutes after ordering a pizza, the user calls up the client
history list and selects the pizza confirmation message. In the mean
time, the client cache has dropped the confirmation message contents.
Some clients (Lynx, Netscape, ...), it will now resubmit the form in
order to get the confirmation message contents from the server. This
makes perfect sense in a stateless protocol, but also causes a second
pizza to be ordered.
It is relatively easy to implement a `reload guard' in the server to
handle these two cases. However, this `reload' guard will make the
`order pizza' button on the form into a `reload old confirmation
message, do not order a second pizza' button once pressed.
With very careful server programming, and very careful phrasing of
forms and form response messages, some ambiguous situations can be
avoided, but not all of them. Also, I've found that new clients
often add new ways in which things can go wrong.
- _ -
In my opinion, forms intended to change the state in the
user/service dialog should have the following functionality:
1) pressing the `submit' button on such a form will
a) cause the state to be changed, or
b) generate an error message about the form being filled in
incorrectly, or
c) generate an additional form asking for more information (you have
already ordered a pizza. Do you want to....), or
d) generate a `service could not be reached' client error message, or
e) generate a `your form was sent, but something went wrong, it
may or may not have been processed' client error message
2) any other browsing operation should never change the state. In
particular, reloading the form result may not change the state.
Ideally, a service should always allow the user to query the current
state (did I just order a pizza or not?) by following some link. This
is especially important if a 1e) error occurs. If money is involved,
a form submission protocol making 1e) very unlikely would be nice.
To allow service authors to implement the above functionality, some
extensions to the WWW protocols need to be made.
- _ -
Current Web technology cannot be used to build a 100% reliable and
intuitive statefull application.
I have not covered all statefullness problems above. A large
additional class of problems involves WWW pages whose contents change
as a result of user interaction. With current clients, it is hard to
bypass the old copies of the pages still present in the client cache.
Newer drafts of the HTTP specification say that client caches can be
disabled by using an Expires: <some date in the past> field, but
clients do not implement this functionality yet.
My Futplex system is a WWW service that contains a large number of
workarounds for current statefullness problems, see
http://gewis.win.tue.nl/applications/futplex
for pointers to the CGI sources.
- _ -
In my opinion, several extensions to the current WWW protocols and
software are needed before reliable and intuitive statefull Web
services can be made.
1) A `submit-pressed' flag is needed
If a client submits a form to a server, the request should contain a
flag indicating whether or not the submission was caused by the user
pressing the `submit' button of the form.
2) Some `style rules' for statefull services are needed
If statefull services are to become reliable and intuitive, some
basic rules for client and service design are needed. For example
a) services should change state only if a `submit' button was pressed
by the user
b) caching mechanisms may never interfere with the submission of a
state-affecting form
c) clients are required to show an error message if a
state-affecting form submission operation is interrupted halfway.
Note that some of these style rules need to be backed up by HTTP
protocol extensions. Rule a) needs the `submit-pressed' flag. A
mechanism for telling whether a form is state-affecting would be nice
for rules b) and c).
These style rules ensure that an interactive service will not
behave in a grossly counter-intuitive way, where `intuitive' is what
users have come to expect using other services.
The style rules should not be too restrictive, they should be
concerned with `low level' user interface issues only. For example,
I feel that
x) statefull services are required to provide a way of querying
the state
would be too restrictive.
3) Some form of session-ID or cookie mechanism is needed.
A statefull service running on a server (or group of servers) needs a
mechanism to distinguish between different clients running on a
remote host. This would allow session state to be kept at the server
end.
For many applications, the ID or cookie must have reasonable
security; it must be hard or impossible to pose as a particular
client.
4) Clients caches need to look at the HTTP Expires: field.
This way, service authors can force clients to fetch a fresh copy of
a (state-reporting) page, in stead of using the copy in the client
cache.
All of the above things require only small extensions to existing
protocols and client software. If anyone is working on formal drafts
for protocol extensions in this direction, please let me know.
Koen.