Make All Pages Runnable
3 comments
Tuesday, April 29, 2008
When you start using Yuma, you'll quickly find that most of your code is in include files, and not generally meant to be executed directly. But occasionally, people will try to execute them directly — and you can make that do something useful.
First, how would someone get in this situation? Well, every include file has to be in the web documents directory on your server. That means that with the right URL, someone could access an include file directly, rather than accessing some file that includes it.
Granted, it's often unlikely that someone would stumble across the URL of some obscure include file just by chance (and hopefully, your web site is configured to not list all files when somebody requests the directory). But we're going to be sharing a lot of code, and some include files will become popular. Some people will try likely paths to common include files, just to see what will happen. Also, once you've grokked this blog, you'll be accessing your include files directly yourself on occasion, for the sake of testing.
So, what does happen when a user directly requests an include file? That depends on two things: how it's written, and how the server is set up. If you use a separate extension for include files (e.g. ".inc"), and the server is not set up to process these as Yuma files, then it will probably serve the contents of that file as plain text for all to see. As Brad points out in this blog entry, that's a bad idea; you should configure your server to process these as Yuma files, or else stick to file extensions already so configured. That leads us to the first rule for safe include file design:
Rule #1: Make sure your include files are processed as Yuma code.
Once you've got the file being processed by Yuma, then what happens depends on how it's written. If the file depends on other classes or methods being defined, but fails to include those files — that is, it assumes that whatever's including it will also include the files it depends on — then by itself, it will not compile. The user will be faced with a big pile of compile errors, including snippets of code. Also not good! To avoid this, follow rule #2:
Rule #2: Include everything this file needs to compile (even if you think the caller would probably include it anyway).
Remember, there's no harm in including a file multiple times in Yuma; it only gets included once regardless, and in most cases, order doesn't matter. So including everything the file depends on is both safe and sensible.
If you follow rules 1 and 2, and go no further, then anyone accessing the include file directly will get a blank page. That's a fine outcome, and you could stop there. But it's possible to go one step further, and include some code that executes only when the include file is accessed directly.
Rule #3: Consider adding some code to execute when the file is accessed directly.
The trick is to look at the SCRIPT_URL server parameter. This is the URL to the Yuma script that's being executed, with any query parameters stripped off. If that URL indicates the file that's being included, then you know it's being accessed directly; if it's anything else, then it is not.
As an example, consider the BBCode.yuma file in the examples/includes folder that comes with the Yuma download. It's an include file, meant as a helper for other apps (it's used to process the BBCode in this blog, for example). You could implement Rule #3 by adding code like this to the end of the file:
The "if true" block here is just used to give us a local namespace, so that the "myName" constant and "script" string doesn't loiter around globally and bump into other identifiers from other include files that may be doing the same thing. Then we look up the SCRIPT_URL server parameter, and see whether the last field (delimited by "/", the standard URL path separator) is equal to the name of this file (which unfortunately we have to hard-code as a constant; there's no way to get it automatically).
Now the guts of that inner "if" block will execute only if this file is accessed directly, and not when it's included from some other file. Those guts could do anything — display a simple explanation, as in this example, or run some unit tests, or whatever you like.
So now you know how to make your include files deal with being accessed directly — not only refraining from spewing their guts all over the user's screen, but even having extra functionality in that case.
First, how would someone get in this situation? Well, every include file has to be in the web documents directory on your server. That means that with the right URL, someone could access an include file directly, rather than accessing some file that includes it.
Granted, it's often unlikely that someone would stumble across the URL of some obscure include file just by chance (and hopefully, your web site is configured to not list all files when somebody requests the directory). But we're going to be sharing a lot of code, and some include files will become popular. Some people will try likely paths to common include files, just to see what will happen. Also, once you've grokked this blog, you'll be accessing your include files directly yourself on occasion, for the sake of testing.
So, what does happen when a user directly requests an include file? That depends on two things: how it's written, and how the server is set up. If you use a separate extension for include files (e.g. ".inc"), and the server is not set up to process these as Yuma files, then it will probably serve the contents of that file as plain text for all to see. As Brad points out in this blog entry, that's a bad idea; you should configure your server to process these as Yuma files, or else stick to file extensions already so configured. That leads us to the first rule for safe include file design:
Rule #1: Make sure your include files are processed as Yuma code.
Once you've got the file being processed by Yuma, then what happens depends on how it's written. If the file depends on other classes or methods being defined, but fails to include those files — that is, it assumes that whatever's including it will also include the files it depends on — then by itself, it will not compile. The user will be faced with a big pile of compile errors, including snippets of code. Also not good! To avoid this, follow rule #2:
Rule #2: Include everything this file needs to compile (even if you think the caller would probably include it anyway).
Remember, there's no harm in including a file multiple times in Yuma; it only gets included once regardless, and in most cases, order doesn't matter. So including everything the file depends on is both safe and sensible.
If you follow rules 1 and 2, and go no further, then anyone accessing the include file directly will get a blank page. That's a fine outcome, and you could stop there. But it's possible to go one step further, and include some code that executes only when the include file is accessed directly.
Rule #3: Consider adding some code to execute when the file is accessed directly.
The trick is to look at the SCRIPT_URL server parameter. This is the URL to the Yuma script that's being executed, with any query parameters stripped off. If that URL indicates the file that's being included, then you know it's being accessed directly; if it's anything else, then it is not.
As an example, consider the BBCode.yuma file in the examples/includes folder that comes with the Yuma download. It's an include file, meant as a helper for other apps (it's used to process the BBCode in this blog, for example). You could implement Rule #3 by adding code like this to the end of the file:
// Handle the case where this file is accessed directly.
if true then
const myName = "BBCode.yuma"
Dim script As String = ServerParams.Lookup("SCRIPT_URL", "")
if NthField( script, "/", CountFields( script, "/" ) ) = myName then
Print myName + " is not meant to be accessed directly."
end if
end if
if true then
const myName = "BBCode.yuma"
Dim script As String = ServerParams.Lookup("SCRIPT_URL", "")
if NthField( script, "/", CountFields( script, "/" ) ) = myName then
Print myName + " is not meant to be accessed directly."
end if
end if
The "if true" block here is just used to give us a local namespace, so that the "myName" constant and "script" string doesn't loiter around globally and bump into other identifiers from other include files that may be doing the same thing. Then we look up the SCRIPT_URL server parameter, and see whether the last field (delimited by "/", the standard URL path separator) is equal to the name of this file (which unfortunately we have to hard-code as a constant; there's no way to get it automatically).
Now the guts of that inner "if" block will execute only if this file is accessed directly, and not when it's included from some other file. Those guts could do anything — display a simple explanation, as in this example, or run some unit tests, or whatever you like.
So now you know how to make your include files deal with being accessed directly — not only refraining from spewing their guts all over the user's screen, but even having extra functionality in that case.
Comments
Steve Garman
Wednesday, April 30, 2008
I appreciate that there are all sorts of clever things you could do in Yuma instead of printing a message but, in the case of your code, isn't that just the equivalent of putting at the end of the include file, after the final ?>
BBCode.yuma is not meant to be accessed directly.
Steve Garman
Wednesday, April 30, 2008
Please ignore me.
Heaven alone knows what I was thinking about.
Wednesday, April 30, 2008
Looks like you already saw your own answer, but in case others are wondering the same thing:
If you were to put such a message at the end of the file, then it would appear on every page that includes BBCode.yuma, wherever the #include line appears. Putting it inside those "if" blocks ensures that it runs only when BBCode.yuma really is accessed directly.

