When a browser first makes a request for a webpage, it always sends that request all at once, then waits for a response. Part of the request consists of cookies that the browser had received on previous visits. When your webserver receives the cookies, it places them into a CGI variable called HTTP_COOKIE. This variable is only populated when WebSpeed first receives word of the request, and forms the basis for the Get-Cookie() function.
When you use the Set-Cookie function to set a cookie in WebSpeed, you always do so in the output-headers section because in reality, a cookie is simply an HTTP response header. By sending this response header, you are basically instructing the browser to return the value back to you on the NEXT and subsequent hits. Since WebSpeed doesn't by default update the value of the HTTP_COOKIE field, using get-cookie() during the same hit will NOT return the value of the cookie that you just set, even though it may have been properly set.
The best way to determine whether a cookie was set properly is to set your browser to warn you about cookies. You can then examine the cookie to make sure that it's being set correctly on the right hit. (Of course, you don't want to do your general surfing this way...)
{ src/web/method/wrap-cgi.i }
def input param ip_filefullpath as char no-undo.
def var vfileline as raw no-undo.
def stream inputfile.
output-content-type("application/pdf").
input stream inputfile from value(ip_filefullpath) binary no-echo.
repeat:
length(vfileline) = 1024.
import stream inputfile unformatted vfileline.
put {&WEBSTREAM} control vfileline.
end. /* repeat */
length(vfileline) = 0.
input close.
(Says Steve: It will NOT work in an ESS (Embedded Speed Script)
program because ESS programs output their own garbage and headers, and it takes some major
kluging to get around, so don't bother unless you've got time to waste. Yes, I've done
it but it ain't pretty.)
"Everyone needs a context framework, and there are a lot of people here on this list who have developed good ones. (Mirro, Crawford, McIntyre, Abbott, and myself among others - take your pick)
While I certainly agree with the need for a good context framework, I don't think the back-button issue is its poster-child. You could probably solve the back button issue without any sort of good framework.
In general, I think this is an issue that people tend to over-think. In fact, many times the would-be solutions to this "problem" become real problems themselves. You can see this sort of fiasco when you visit poorly designed e-commerce sites which tend to reload a page every time you hit the back button, as if they were actually sitting there changing prices second-by-second and are afraid you'll see the wrong thing. It makes the site painfully slow.
Some sites make the mistake of sending "expires: 0" headers or the equivalent meta tag in response to posts as a way of keeping the browser from caching the page in memory. This usually results in the user seeing a "This page has expired - Hit Reload" message in the browser. Problem is - Reload What? What happens when they hit reload? Are they submitting an order a second time? Ever try to print a page in Netscape resulting from an expired post?
What is truly needed is proper planning, error-handling, and transaction encapulation. These things are brought about by habit and experience, and really no "framework" solution can stop a bad programmer from screwing things up by failing to plan, handle errors, and encapsulate.
Here is a situation where people say they want to disable the back button:
There's a screen showing some data, a user clicks to a second screen to edit a piece of that data. They click "back" to go to the original screen and the original data is still there. (Possibly with other links that rely on that record's original data)
My thought: This is not a real problem. Web users know that the back button goes back. They generally don't expect to see their changes when they back up. However, what if they genuinely do get confused when they back up? Well, first off, you have to design any page with the thought in mind that people WILL back into it. If the data on the page is time sensitive, you need to do some things like stick "Reload" or "Refresh" buttons in there. Make it clear to the user that the data will be fresher after a reload. Secondly, you have to design the programs that this page links to so that they properly validate any action before performing it. Not just "Does this record exist?" but "Was this record in the list I just sent them?" and "Would this record STILL be in the list I just sent?" and "Does this user have permission to operate on this record?" These are questions you have to answer each and every step of the way.
For example, lets say you have a page that lists a customer's record. This page has a button which when clicked sends an email to that customer's sales-rep. Another button takes you to a page where you can assign a sales-rep. You're on this page and click to assign a new sales rep. You hit "back" and then hit the "Email sales rep" button.
If you were thinking ahead, you would have the "email sales rep" button pointing to an object that looks up the customer's current sales rep's email address and then performs the action. Otherwise, you might have made the mistake of sending the email address through the query_string or something. (Which would email the old sales rep, instead of the new one) (I know, it's a dumb example...)
Another common situation is to have a list of records with links to record detail. You click into the detail record, hit the "delete" button, and the record is gone from the database, but still on the page when you hit "back". So what? If you programmed things correctly, and planned for errors, there would be no problem. Let's say you do click back and see your recently-deleted record still in the list. A moron user might be tempted to click the record again thinking it still hadn't been deleted. Hopefully, your second program is smart enough to detect that the record is gone, and give the user a good message, as well as spitting out a new version of the record list from which the user clicked. -No problem!
One of the questions that you have to answer for yourself is: "Where and when in my application are users going to hit the back button?" Then you have your web app spit out more appropriate (and prominent) links in these places. For instance, when you delete a record, don't just spit out a blank page saying "Record Deleted." when you know darn well, the user has to go somewhere, and it's most likely going to be the list of records again. At least stick a "Back to the list" link on there, and that way when your users click it (instead of back) they'll see an updated page.
Provide good navigation, and the use of the "Back" button will go way down!
All this being said, there is ONE place I can think of where you may need to disable the back button: The initial page which displays after a secure login which was submitted from a static webpage. If you allow users to back up into this page, then you run a security risk with people going to a user's desk while they're away (perhaps after your session has timed out) and backing up to that page and reloading it. (Essentially logging in again without knowing the password). It's a minimal problem, and I think most sites can get away without fixing it. However, it can easily be fixed by either providing a pre-login context so that with a given contextual value a login could only be made once from a given login screen. (Hidden field with sequential or randomized number actually in the login form)
The other fix is to use location.replace(); to quickly load another page after logging in. This removes the login from the browser's history altogether.
Hopefully this post will inspire some thought and debate.. ;) Have I left anything out?"
In fact, the first two of the above should be used in response to all GET requests that contain either confidential, or dynamic session sensitive data.pragma: no-cache
cache-control: no-cache
expires: 0
Sort of a compromise between using the expires: 0 header and not using it at all, this method has its drawbacks. For one, you have to compute a reasonable time and date, and get it into this RFC 1123 date format. Another problem is the disparity in date settings that individual users have on their computers. Ideally you would want to set this to expire in about 10 minutes or so after the initial hit. However, can you really trust that all of your users will have their PC clocks set correctly and even on the proper date? Experimentation is in order here, it's unknown to me whether you can use the Date: HTTP header to force the client to believe the date and time that you send it. This solution would expire a screen only after a given period of time, so obviously, if a second user could get on that console prior to the expiration, they could indeed see the page.
This one is really just farts in the wind right now, since I haven't tried it, but imagine this: First, set a cookie of any given name and value (as long as it's consistent) and carry it along in your application. On every screen, have a log-out button. When this log out button is pressed, it deletes or resets the value of the cookie to something that indicates logged out.
Now, on your sensitive info pages that you don't want backed into, have some JavaScript that basically checks the cookie to see if it's still there. This Javascript should be in the head or early part of your document. If the JavaScript detects that the cookie is gone or logged-out, then it uses a location.replace() with the URL of some "We're sorry, but you logged out" page.
In this case, if someone backed up to a sensitive post, the JavaScript would run, and the page would immediately be replaced by another webpage, and totally removed from the history.
This is not going to be practical for many sites where style and flow prohibit the use of pop-up windows, but, if you can use them, this is definitely a good solution. When you open a new window to show sensitive information, that window's opener window can close it with JavaScript. For added security, you can make it so that the parent window closes the child window whenever the parent window unloads. So in the case where the user gives up on your app and wants to surf to Yahoo! they have to go to the parent window (since it has the URL bar) and then when they "unload" the current page, your JavaScript "onUnload" code can fire and clean up the child window.
Posts generally are not cached by proxies or user agents. GET requests are often cached, and wires can get crossed if the query_string data is the same for different hits. GET requests result in all of the name=value pairs showing in the browser's URL bar, as well as in the link itself, if the user should happen to bookmark the page. (Which they often do...) Additionally, users often will email the URL of various pages to people, not knowing the security risk of doing so.
Not always possible in a public Internet environment, but can often be done successfully in an intranet environment. However, the way that you design your user interface can say a lot to the user about what's kosher and what's not.
For instance, if someone has just submitted you a credit card number, there's absolutely no reason to display it back to them. They already know what it is. If you need to show the first 4 digits, that's fine, but no need repeating the whole thing. Obviously, there are times when you do legitimately need to show sensitive data, but do use good judgement.