Frequently Asked Questions about WebSpeed

<Perpetually Under Construction sogivemeabreakalright?>
How come none of my images display in my WebSpeed app?
Don't worry - this is a typical newbie problem.

It is important to understand that unlike ASP or Server Side Include etc, WebSpeed programs do not belong anywhere in the Web Server directory structure. Only static files like your home page, and images etc belong there. You can grab some phones and ask your and ask Web Server personnel about it.

When accessing a Web Object, use something like http://www.company.com/scripts/wsisa.dll/program.w

When accessing an Image, use something like http://www.company.com/images/image1.gif

Now, comes the hard bit - getting relative URLs right. You generally cannot use normal relative paths - better to use server relative paths. (A normal relative path might be "../../images/image1.gif" whereas a server relative might be "/images/image1.gif")

So in your HTML, you should use <IMG SRC="/images/image1.gif">

Do not try to just use <IMG SRC="image1.gif"> as this would use the "current server path" that was used to display the WebSpeed generated page - giving /scripts/wsisa.dll/image1.gif and this will actually try to run a WS program called image1.gif or image1.r (compiled version).

Be aware that putting you WS code (compiled or source) in the Web Server directory structure means a relatively interested person could actually download the code to look at.

Hope this makes some sense,
Kean Maizels
Adept Business Systems - NSW Australia
How can I tell if my WS 2.1 program is being run from HTTPS instead of HTTP?
As most probably know by now, the global variable SERVER_PORT does NOT return 443 when the connection is secure.

The HTTPS global variable also does not work. Neither does hosturl or appurl.

You can try this trick though:

IF INDEX(WEB-CONTEXT:CURRENT-ENVIRONMENT,"SERVER_PORT_SECURE=1") > 0 THEN ASSIGN HTTPS = "YES".


or conversely, you could change the "1" to a "0" and "Yes" to a "No"

This seems to work with my setup. (NT 4.0ws, WS 2.1) Perhaps others could verify? I've already heard that it doesn't work in some setups. Flip a coin and see if it works for you...

I'm thinking you could stick that line in your web-util.p and fix the bug entirely.
Steve Southwell,
Web Programmer / Consultant
BravePoint, Inc.
Why doesn't this website use WebSpeed?
This server does have a WebSpeed service, but I only have a small development license, which I use for my paying job. At a cost of around $1000 per agent, I can't really afford to WebSpeedize this site right now. However, if anyone from Progress would like to donate some license to a worthy cause, I could whip something up pretty quickly. (hint, hint...)
- Steve Southwell,
Web Programmer / Consultant
BravePoint, Inc.
I am trying to set cookies, but I can't seem to get at them until the second submit.

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...)

- Steve Southwell,
Web Programmer / Consultant
BravePoint, Inc.
How do I get WebSpeed to output a PDF file?
Use the following code in a CGI-wrapper type object:
 { 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.)
- Robert Mirro,
WebSpeed Consultant
What permissions are required to start WS 3.0 under NT?
If you create an appserver or WebSpeed broker through the Progress Explorer, you must supply an Account and Password of a user on the local system with the following rights:
  • Administrator.
  • Log on as a service.
  • Log on as batch job.
  • Act as part of the operating system.
  • Increase quotas.
  • Create a token object.
  • Replace a process level token.
- Kris Murphy,
Progress Technical Support
How do I stop users from using the "Back" button in their browser
Good user interface design can somewhat reduce the tendency of users to use the back button. Good application design can prevent most of the contextual problems that clicking the back button can cause. From my post to the WebSpeed PEG, 12/12/99:
"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?"
- Steve Southwell,
BravePoint, Inc.
Can you suggest me some ways by which we can implement security in webspeed. I would prefer not to use cookies.
This is a very, very large topic that probably doesn't fit in a single email. But some quick suggestions -

There are 3 ways to pass info between pages on the Web - cookies, hidden fields, and the URL. No matter which way you choose to pass info, the issues are all the same. (by the way, via URL always shows the variable so you either want to encrypt it or chose another method - hidden fields are in the clear in the page but you can either encrypt the field *or* encrypt the entire HTTP conversation)

No matter which way you do things, never show the user anything in clear text. Other people can get to the computer they're on as well as the potential for interception over the wire. When you do send the security string, only send one single, small string. All of the rest of the pieces, user ID, password, potentially which domain or other verification info, etc. should be stored in the database with a timeout on it. Log violations in the database, let the user have a table driven variable that determines just how many and what types of violations are logged.
- Geoff Crawford,
Innovative Client Servers
My app shows lots of confidential data. How can I remove my pages from the browser's history so that in places like web kiosks, other users can't back into my user's session and see my users' sensitive data?
If security is more important than functionality, you could use the old standby HTTP headers:
pragma: no-cache
cache-control: no-cache
expires: 0
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.

However, I don't recommend the use of the expires: header at all, and I don't recommend the pragma, or cache-control directives for responses to POST requests. From personal bad experience, I know that this wreaks all sorts of havoc with printing and navigation in Netscape browsers, and some problems in IE as well.

HTTP 1.1 defines an "Age" header which proxy servers can use to inform the user-agent (browser) how old a particular document is. I haven't tried it, but it may be usable as a substitute for the expires: directive.

Because the expires: directive, which is the proper HTTP solution to this problem causes so many errors, there comes a tradeoff between data security and usability.

Here are some of the alternatives that come to mind: (not in any particular order)
  • Expires: Wed, 01 Dec 1999 16:00:00 GMT
    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.
  • Javascript / Logged-out-cookie / location.replace();
    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.
  • Use a child-window with no URL bar for sensitive info
    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.
General Suggestions:
  • ALWAYS use the POST method for forms with sensitive data.
    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.
  • Educate your users about proper security.
    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.
  • Design your apps to show as little sensitive info as possible
    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.
- Steve Southwell,
BravePoint, Inc.
Want something added to the FAQ?