Improving Our Program
Although this program isn't bad, it might be useful if it were a little more flexible. One thing we might
want to do is allow it to take parameters, so that it can do not only a GET
operation, but
also a POST
. We could also decide to allow the program to output the header information that
it received from the web server, which can be very handy when trying to debug routines that retrieve data
from a web server. A method of handling and validating command line parameters might also be useful. Let's
add support for not only the output file, but also a flag to decide if the header is output, and also
support for passing variables through using the POST
method.
As a first step, we can update our usage()
function with the new information. The
new version looks like this:
usage()
Functionfunction usage() string s s = "smprun[32.exe] urldump.smp <url> [--outfile=<filename>] \ [--showheader]{d}{a}" s = s + " [--vars=<varlist>]{d}{a}{d}{a}" s = s + " Where the vars need to already be URL-encoded and \ if they violate the{d}{a}" s = s + " shell rules they will also need to be escaped to \ be hidden from the{d}{a}" s = s + " shell. It is recommended to place quotes around \ the --vars= entry.{d}{a}" s = s + " The equals sign and what follows CANNOT allow \ spaces! If necessary,{d}{a}" s = s + " surround any entry with quotes.{d}{a}" end function s
Now that we have decided what the parameters are going to be (and incidentally also the format), we can
add the code to handle the parameters. This is probably best done using a specifically designed data
type. This will allow us to offload most of the work to the type itself, without cluttering our existing
main()
function with all the associated code. It will also make it easier to lift
it and use it again in another program, or even in the future to create a more versatile type that is
more universal. Let's see what that code looks like:
type parameters embed string outfilename boolean showheader string variables string operation reference function getparam end type function parameters.new(parameters me) me.outfilename = "" me.showheader = .false me.variables = "" me.operation = "GET" end function me function parameters.getparam(parameters me, string parameter) if parameter <= "" // do nothing else if .like1(parameter, "--outfile=*") me.outfilename = .substr(parameter, .instr(parameter, "=") + \ 1, .inf) else if .like1(parameter, "--showheader") me.showheader = .true else if .like1(parameter, "--vars=*") me.variables = .substr(parameter, .instr(parameter, "=") + \ 1, .inf) end if end function
The parameters type has properties to store all the information that we will use for this
call to the program. It defaults to running in GET
mode, and will not return the header from
the web server. The way the getparam()
method has been coded requires that each
parameter that has a value component must be separated from the value by an equals (=
)
sign and no white space to either side. Part of the reason for this is that allowing white space would
require a more complex algorithm, since each of the white space separated items would arrive as separate
parameter values from the shell to the main()
function.
We now have a method of handling the various parameters to the program, and one of the nice features of
this approach is that the order of the parameters does not matter. The only parameter that has a fixed
position is the URL itself, since it must be first. Using this approach requires some changes to the
main()
function as well. Let's have a look at those now.
main()
Functionfunction main(string sUrl, string param1, string param2, \ string param3) string errtext integer e fsfileoutputstream fpo httpresponse response parameters params e = 0 if sUrl <= "" errtext = usage() else params =@ parameters.new() params.getparam(param1) params.getparam(param2) params.getparam(param3) errtext = "" if params.variables > "" response =@ httppost(sUrl, params.variables) else response =@ httpget(sUrl) end if if response !@= .nul if response.errorstatus > "" errtext = response.errorstatus else if params.outfilename > "" fpo =@ fsfileoutputstream.new(params.outfilename, error=e) if fpo =@= .nul or e != 0 errtext = sERRTXT_FILEOPENFAILED + sCRLF else if response.statuscode < 200 or \ response.statuscode >= 300 errtext = sERRTXT_PAGE + sUrl + sERRTXT_NOTFOUND + \ sCRLF else errtext = sERRTXT_PAGE + sUrl + sERRTXT_SUCCESS + sCRLF end if fpo.putstring(.if(params.showheader, \ makenotnull(response.fullheader) + \ sCRLF + sCRLF, "") + \ .if(response.entitybody != .nul, \ response.entitybody.getstring(1, \ .inf, 1),""), 1) end if else if response.statuscode < 200 or response.statuscode >= 300 errtext = sERRTXT_PAGE + sUrl + sERRTXT_NOTFOUND + sCRLF else errtext = "" end if errtext = errtext + makenotnull(.if(params.showheader, \ response.fullheader + \ sCRLF + sCRLF, "")) + \ .if(response.entitybody != .nul, \ response.entitybody.getstring(1, \ .inf, 1),"") end if end if end if end function errtext
As we can see from the previous code, not a lot has changed from the original version. We now support
the POST
operation if we were given variables (which must be URL-encoded when they are
passed in). We can also optionally return the entire header from the web server if requested to do
so. All of the actual handling of the parameters is done by the parameter type and its
getparam()
method.