Jekyll2021-07-13T12:51:34+00:00https://www.toddandrae.com/feed.xmlToddAndrae.comPreventing 502/520s with CodeIgniter and MaxCDN/CloudFlare2014-12-20T15:09:52+00:002014-12-20T15:09:52+00:00https://www.toddandrae.com/preventing-502520s-with-codeigniter-and-maxcdncloudflare<p>Where I work, we finally outgrew our shared hosting britches and moved over to a VPS. Most of this growth came not from the need for processing power, but from our need to store a large number of images. The standard real estate listing went from averaging 8 images to 15 and storing multiple file sizes led to us needing to store 120,000 images. The plan was to activate <a href="http://code.tutsplus.com/tutorials/activating-ludicrous-speed-combine-cloudflare-with-a-cdn-on-your-blog--wp-24586">ludicrous speed</a>.</p>
<!--more-->
<p>I got everything set up, RackSpace for hosting, used GlobalSign for SSL, CloudFlare for DNS and MaxCDN for images. Everything went well and we immediately saw a decrease in first byte time and overall page load times were decreased. I noticed some intermittent issues with both CloudFlare and MaxCDN, but the nginx access logs showed that everything was being resolved fine and ending with a 200 status code. I moved the main application back to the shared host but left resolving images with MaxCDN and started troubleshooting.</p>
<p>MaxCDN support thought the issue could be with our certificate so I set down the path of testing the SSL configuration. I used <a href="https://www.ssllabs.com/ssltest/">SSLLabs</a> and a <a href="https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html">tutorial from Remy van Elst</a> to get a solid A+ rating. I ran a battery of other SSL tests against the server and they all agreed that SSL was solid. I went back to MaxCDN and they ran a few more tests and insisted I add the Root CA to the certificate chain. I went back to GlobalSign Support to get their take. They didn’t think adding the Root CA to the chain would make a difference, but they sent the cert and I added it. My SSL rating went to an A for containing an anchor in the chain, but if it solves MaxCDN 502 issue, I’ll take it. I purged the cache and ran it again. 502!</p>
<p>After some troubleshooting, I found that I could 502 in Chrome, but in Firefox I would get the file. But I would only get the first file. Additional files in Firefox would 502. I jumped over to the command line to cURL the image URLs. Each and every time the file would be resolved and returned. I jumped back to Chrome and opened an incognito tab. 200! I attempted a second file and returned a 502. I compared the response headers across the various attempts as well as accessing the files directly. That is when it hit me. Cookies!</p>
<p>I went back to MaxCDN with my theory of the number and size of cookies being sent by CodeIgniter causing the 502. I was told it had to be because the server wasn’t performing TLS 1.1/1.2 properly and that our host was blocking their requests and that they were going to continue looking in to the issue. At this point, I gave up on MaxCDN providing a solution, so I went about testing my own solution. I searched for other persons having the same issue and came across a <a href="https://ellislab.com/forums/viewthread/228023/">thread</a> on EllisLabs that had the <a href="https://ellislab.com/forums/viewthread/228023/#1035436">exact solution I needed</a>. I added the custom session class and the post_controller hook and the 502s disappeared. Completely. I turned CloudFlare back on and the 520s disappeared as well.</p>
<p>So, if you are having a 502 error with MaxCDN or a 520 with CloudFlare and can’t figure out where it is coming from, check your cookies. Despite the blue muppet’s persistence, there is a chance that you can have too many cookies or your cookies may be too big.</p>
<p><strong>UPDATE:</strong> The Ellis Lab forum links are dead, so here is the solution to the problem. I haven’t ran CI 3.0 yet, so I don’t know if this update is required there or not.</p>
<p>In your application/libraries directory, extend CI_Session. For my install, my prefix is RS.</p>
<pre class="brush: php; title: ; notranslate" title="">class RS_Session extends CI_Session {
private $cookie_data = array();
public function _set_cookie($cookie_data = NULL){
if(is_null($cookie_data)){
$cookie_data = $this->userdata;
}
$cookie_data = $this->_serialize($cookie_data);
if($this->sess_encrypt_cookie == true){
$cookie_data = $this->CI->encrypt->encode($cookie_data);
} else {
$cookie_data = $cookie_data . md5($cookie_data . $this->encryption_key);
}
$this->cookie_data[] = $cookie_data;
}
public function finalize_session(){
if(!empty($this->cookie_data)){
$cookie_data = array_pop($this->cookie_data);
$expire = ($this->sess_expire_on_close === true) ? 0 : $this->sess_expiration + time();
setcookie(
$this->sess_cookie_name,
$cookie_data,
$expire,
$this->cookie_path,
$this->cookie_domain,
$this->cookie_secure
);
$this->cookie_data = array();
}
}</pre>
<p>You also need to add a hook to finalize the session. I created a file, “finalizeSession.php”, in my application/hooks directory.</p>
<pre class="brush: php; title: ; notranslate" title="">function finalize_session(){
$CI =& get_instance();
$CI->session->finalize_session();
}</pre>
<p>And then added this to the application/config/hooks.php file</p>
<pre class="brush: php; title: ; notranslate" title="">$hook['post_controller'][] = array(
"class" => "",
"function" => "finalize_session",
"filename" => "finalizeSession.php",
"filepath" => "hooks"
);</pre>Todd AndraeWhere I work, we finally outgrew our shared hosting britches and moved over to a VPS. Most of this growth came not from the need for processing power, but from our need to store a large number of images. The standard real estate listing went from averaging 8 images to 15 and storing multiple file sizes led to us needing to store 120,000 images. The plan was to activate ludicrous speed.Wildcard Domains, Nginx and DNSMasq on OS X 10.10 Yosemite2014-10-19T14:45:05+00:002014-10-19T14:45:05+00:00https://www.toddandrae.com/wildcard-domains-nginx-and-dnsmasq-on-os-x-10-10-yosemite<p>So I’m sure everyone that is showing up here recently blew away their entire system to perform a clean install of 10.10 and now you need to get your devving back on. This is the first part of my dev set up. In the next session, I’ll go over signing certs and setting up a VPN locally for mobile testing.</p>
<!--more-->
<p>One thing I always forget until I need it is to show hidden files. You may want to do that before you get started. Or you can wait until you forget and need it later like me. Your choice.</p>
<pre>defaults write com.apple.finder AppleShowAllFiles -boolean true ; killall Finder</pre>
<h2 id="install-xcode-and-command-line-developer-tools">Install XCode and Command Line Developer Tools</h2>
<p>According to the <a href="https://github.com/Homebrew/homebrew/wiki/Installation">Homebrew wiki</a>, you don’t need the XCode IDE, but I was already downloading it for native app development and decided to include it. Feel free to skip this step and post your results if things go awry. You will still need to grab the Command Line Tools (or you can let Homebrew grab those for you).</p>
<p>For the moment the XCode in the App Store is version 6.01. To get 6.1, download it from <a href="https://developer.apple.com/downloads/index.action">https://developer.apple.com/downloads/index.action</a>. It will hopefully live at <a href="https://itunes.apple.com/us/app/xcode/id497799835">https://itunes.apple.com/us/app/xcode/id497799835</a> in the coming days.</p>
<p>You will need to download the XCode Command Line Developer Tools. You can grab the 6.1 version from <a href="https://developer.apple.com/downloads/index.action?name=for%20Xcode%20-#">https://developer.apple.com/downloads/index.action?name=for%20Xcode%20-#</a>.</p>
<h2 id="install-homebrew">Install <a href="http://brew.sh/" target="_blank">Homebrew</a></h2>
<p>Now it is time to launch Terminal and start copying and pasting. First we need to grab Homebrew. Paste the following in to your terminal window.</p>
<pre>ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"</pre>
<p>That should have completed with no warnings or messages. Once finished run</p>
<pre>brew doctor</pre>
<p>Again, that should have run with 0 warnings. If you are warning-less at this point, congratulations, you can move on to installing your brews.</p>
<h2 id="brewdnsmasq">Brew DNSMasq</h2>
<p>DNSMasq, in conjunction with resolver will allow all hostnames with a .dev TLD to resolve to your local machine. If you are looking for an in-depth description of what is going on, check out <a href="http://passingcuriosity.com/2013/dnsmasq-dev-osx/">Thomas Sutton’s post on installing DNSMasq using Homebrew</a> (I actually borrowed his tee command for this post)</p>
<pre>brew install dnsmasq</pre>
<p>I don’t remember having to do this with Mavericks or lower, but with Yosemite, I had to first create the /usr/local/etc directory before creating my configuration files. Even if you don’t use DNSMasq, you may still need to create the directory for other formulae.</p>
<pre>mkdir /usr/local/etc</pre>
<p>Go ahead and copy the dnsmasq example configuration file to our new directory</p>
<pre>cp /usr/local/opt/dnsmasq/dnsmasq.conf.example /usr/local/etc/dnsmasq.conf</pre>
<p>Edit your file and add this line. This will direct all lookups for hostnames with a top level domain of dev to resolve to 127.0.0.1. If you want to use a different top level domain name, just replace dev with your chosen TLD.</p>
<pre>address=/dev/127.0.0.1</pre>
<p>Next make a link for the provided plists.</p>
<pre>ln -sfv /usr/local/opt/dnsmasq/*.plist /Library/LaunchDaemons</pre>
<p>Next we need to tell OS X where to resolve our .dev addresses. This is unbelievably easy. First create a directory for resolver in /etc/</p>
<pre class="sourceCode bash"><code class="sourceCode bash"><span class="kw">sudo</span> mkdir -p /etc/resolver</code></pre>
<p>Then create a file named dev. If you went with another test level domain, use that for your filename instead.</p>
<pre class="sourceCode bash"><code class="sourceCode bash"><span class="kw">sudo</span> tee /etc/resolver/dev <span class="kw">></span>/dev/null <<EOF
nameserver 127.0.0.1
EOF</code></pre>
<p>Launch dnsmasq</p>
<pre>sudo launchctl load /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist</pre>
<h2 id="brewnginx-full"><a href="https://github.com/Homebrew/homebrew-nginx">Brew Nginx-Full</a></h2>
<p>I started using this branch of nginx on my previous box because I needed to build with http-additions.</p>
<pre>brew tap homebrew/nginx</pre>
<p>The advantage of this formula is the available options. Run the following command to find out what you have available.</p>
<pre>brew options nginx-full</pre>
<p>To install, run the following. (Omit the square brackets, if you just need a base install)</p>
<pre>brew install nginx-full [--with-additions]</pre>
<p>Next, you probably want to run on port 80 so we need to do some ownership and permissions changes.</p>
<pre>sudo chown root:wheel /usr/local/Cellar/nginx-full/1.6.2/bin/nginx
sudo chmod u+s /usr/local/Cellar/nginx-full/1.6.2/bin/nginx</pre>
<p>Create links for the provided plists</p>
<pre>ln -sfv /usr/local/opt/nginx-full/*.plist ~/Library/LaunchAgents</pre>
<p>To start on port 80 edit nginx.conf located in /usr/local/etc/nginx/ and change the server section to resemble the following</p>
<pre>listen 80;
server_name *.dev;</pre>
<p>You are ready to start nginx</p>
<pre>sudo nginx</pre>
<h2 id="brewphp-fpm"><a href="https://github.com/Homebrew/homebrew-php">Brew PHP-FPM</a></h2>
<p>Brewing PHP-FPM is fairly straightforward. Run these two commands and you should be off and running.</p>
<pre>brew install php56 --with-fpm --with-homebrew-curl --with-homebrew-openssl --withimap --withpgsql</pre>
<pre>ln -sfv /usr/local/Cellar/php56/5.6.1/homebrew.mxcl.php56.plist ~/Library/LaunchAgents/</pre>Todd AndraeSo I’m sure everyone that is showing up here recently blew away their entire system to perform a clean install of 10.10 and now you need to get your devving back on. This is the first part of my dev set up. In the next session, I’ll go over signing certs and setting up a VPN locally for mobile testing.Request Specific CodeIgniter Methods2011-08-10T08:14:21+00:002011-08-10T08:14:21+00:00https://www.toddandrae.com/request-specific-codeigniter-methods<p>When I was looking through frameworks to use, I was weighing some of the choice based on the frameworks ability to route calls based on HTTP Request Methods. Call me a pedant, but I believe that a POST to a page should be a whole different plate of spaghetti from a GET of the page. GET’d pages should get and retrieve information and POST’d pages should perform actions.<!--more--></p>
<p>From the MVCs available, there weren’t any available (that I saw), that off the shelf allowed for this kind of functionality. I chose CodeIgniter and figured I would just have separate named methods for POST or GET.</p>
<p>As I said in a previous post, my exposure to CodeIgniter was based on old tutorials I had pulled from various tutorial sites. Once, I found some more recent tutorials that led to many an aha moment. This is one of those moments.</p>
<p>By extending the CI_Router class with our MY_Router class, we can modify the fetch_method method to return our method name and the HTTP Request Method. In 9 lines of code, you can now have an index_get method and an index_post method to handle exactly what they need to handle.</p>
<p>Add this to or create your /application/core/MY_Controller.php file</p>
<pre class="brush: php; title: ; notranslate" title="">class MY_Router extends CI_Router {
function fetch_method(){
$request = strtolower($_SERVER['REQUEST_METHOD']);
if ($this->method == $this->fetch_class()) {
$method = 'index_' . $request;
} else {
$method = $this->method . '_' . $request;
}
return $method;
}
}
</pre>
<p>Next, just identify the Request Method you would like to allow on your class methods by adding “_get” or “_post” to the end of the method name. That should be it. You can now create individual methods that look like this:</p>
<pre class="brush: php; title: ; notranslate" title="">public function index_post() {
/*Drop database schema*/
redirect(uri_string(), 'location', 301);
}
public function index_get() {
/*Display what just happened*/
}
</pre>
<p>This prevents the POST’d page from being refreshed and keeps the retrieval logic separate. I haven’t tested how CI handles HEAD requests, but the next step is to keep logic from being performed and only 404 if the database cannot be connected.</p>Todd AndraeWhen I was looking through frameworks to use, I was weighing some of the choice based on the frameworks ability to route calls based on HTTP Request Methods. Call me a pedant, but I believe that a POST to a page should be a whole different plate of spaghetti from a GET of the page. GET’d pages should get and retrieve information and POST’d pages should perform actions.Quick Update and Some CodeIgniter Tips2011-08-09T09:11:48+00:002011-08-09T09:11:48+00:00https://www.toddandrae.com/quick-update-and-some-codeigniter-tips<p>So a lot has changed in the past few months. I won’t bore you with details, but I did move back over to the east coast and no longer work with an Oracle based company. I started back with a previous employer, a multi-office real estate company, and was tasked with getting everything back up to speed. This meant examining everything that they were previously doing and streamlining the whole shebang. In doing that, I decided to scrap the wordpress/drupal/open-realty mashup that had been put in place and build a ground up system that could seamlessly share information between all the different areas.<!--more--></p>
<p>The first few days, I was filled with programmer’s pride and was dead set on designing my own custom MVC framework. Soon, I came to my senses and realized that I had to put pride aside and put something in place that had a proven track record and would yield more time to concentrate on the business logic and features required. I had become familiar with CodeIgniter through some interviews in the Denver area and decided to load it up. It was an immediate friendship.</p>
<p>The folks that contribute to CodeIgniter have thought this whole thing out pretty well. I came into it and started tearing apart the system folder, mucking up the core libraries and generally just causing total code anarchy based on a few tutorials I had seen online. Don’t do this. Most of the tutorials are old and there seems to have been a huge jump in recent versions to move the application folder out of the system folder. This makes thing so much better.</p>
<p>Now, let’s get down to business! The first objective I had to tackle with CodeIgniter was how to share my models, and occasionally views, across multiple subdomains and applications. I finally stumbled on the answer by using a combination of Apache virtual hosts, custom php landing pages and an extended router class.</p>
<p>First, the Apache changes:</p>
<pre class="brush: plain; title: ; notranslate" title=""><VirtualHost *:80>
DocumentRoot "/var/www/"
ServerName localhost
ServerAlias blog.domain.tld
<Directory "/var/www/">
Options +Indexes
Options FollowSymLinks
AllowOverride All
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} ^system.*
RewriteRule ^(.*)$ blog.php/$1 [L]
RewriteCond %{REQUEST_URI} ^application.*
RewriteRule ^(.*)$ blog.php/$1 [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ blog.php?/$1 [L]
</IfModule>
<IfModule !mod_rewrite.c>
ErrorDocument 404 /index.php
</IfModule>
</Directory>
</VirtualHost>
</pre>
<p>Currently, each subdomain has to be set up with custom rewrite rules. I am going to come back to this later and see if I can’t do it with a *.domain.tld virtualhost.</p>
<p>Next the little php snippet:</p>
<pre class="brush: php; title: ; notranslate" title=""><?php define('SUBDOMAIN', 'blog'); require_once 'index.php' ?>
</pre>
<p>That’s all there is to the php file. We are just going to define a constant for our subdomain for use later.</p>
<p>And then our extended router class:</p>
<pre class="brush: php; title: ; notranslate" title=""><?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class MY_Router extends CI_Router {
function _set_request($segments = array()){
if(SUBDOMAIN === 'blog') {
$controller_directory = 'blog';
} else {
$controller_directory = '';
}
$segments = array_reverse($segments);
$segments[] = $controller_directory;
$segments = array_reverse($segments);
parent::_set_request($segments);
}
}
</pre>
<p>And this is the meat. Place this code in a file called MY_Router.php in your /application/core/ directory. This extends the base router and allows us to add our ‘controller directory’ to the segment array and pass that back to the CI_Router _set_request method to trick it into thinking it is already in the URI. With that new segment in there, CodeIgniter goes to our application folder, does a check for is_dir and grabs our controller files from the subdirectory we inserted. Nothing to it! And this way, I get to share my models amongst my controllers without having to rewrite them in separate application folders or using symbolic links.</p>
<p>Granted, this might not amaze some of the current users of CodeIgniter, but this immediately sold me on CI’s usage as my MVC of choice and led me to the next step of setting POST and GET specific methods as well as custom subdomains for users, but I’ll save those for next time.</p>Todd AndraeSo a lot has changed in the past few months. I won’t bore you with details, but I did move back over to the east coast and no longer work with an Oracle based company. I started back with a previous employer, a multi-office real estate company, and was tasked with getting everything back up to speed. This meant examining everything that they were previously doing and streamlining the whole shebang. In doing that, I decided to scrap the wordpress/drupal/open-realty mashup that had been put in place and build a ground up system that could seamlessly share information between all the different areas.Creating an MVC for mod_plsql2011-03-09T10:47:00+00:002011-03-09T10:47:00+00:00https://www.toddandrae.com/creating-a-mvc-for-mod_plsql<p>I first got introduced to Oracle’s mod_plsql, and PL/SQL in general, a year ago and have spent a good amount of time since then testing the limits of what I can do with it. If you aren’t familiar with mod_plsql, it is an Apache module that connects directly to the Oracle database and allows you to run packages that you have stored there and spit out some crass HTML4 pages (this is the default action).<!--more--></p>
<p>Before I go any further, I need to say this: mod_plsql is not without its faults. You can make it fairly secure, but it is still a connection directly to your database. The user that you use to create your DAD should only have the permissions needed to perform the operations required by your application and should only have access to the packages required to interact with your database! Always filter your data! Never trust user input! Etc!</p>
<p>The first step (assuming you already have Oracle and OHS installed) for working with mod_plsql is setting up a DAD (database access descriptor). This is a series of directives contained in a location block that are loaded by Apache at runtime. For most installations of Oracle, these directives can be added in the dads.conf file located in your ORACLE_HOME/apache/modplsql/ directory.</p>
<pre class="brush: plain; title: ; notranslate" title="">SetHandler pls_handler
Order deny,allow
Allow from all
AllowOverride All
PlsqlDatabaseUsername mvc_user
PlsqlDatabasePassword mvc_password
PlsqlDatabaseConnectString 127.0.0.1:1521:orcl
PlsqlAuthenticationMode PerPackageOwa
</pre>
<p>There are only three lines that you will need to change from this: PlsqlDatabaseUsername, PlsqlDatabasePassword, PlsqlDatabaseConnectString. The database connect string can be the short name (orcl) or it can be the full string as seen above. After putting this information in, run the dadTool.pl script located in the same directory. This will obfuscate the password in the conf file and gives you a feeling of security. Use opmnctl to restart the OHS service. If you attempt to load the URL (http://127.0.0.1/plsql_mvc or however it maps on your system), you should get a 404. This is a good sign.</p>
<p>So let’s start with the package spec for our plsql_mvc:</p>
<pre class="brush: sql; title: ; notranslate" title="">create or replace package plsql_mvc as
function authorize return boolean;
procedure controller(name_array in owa.vc_arr, value_array in owa.vc_arr);
end plsql_mvc;</pre>
<p>Looks pretty simple. Four lines, one function and one procedure. I’m going to go back to basics for those that started out like me (no knowledge of mod_plsql at all). In our dads.conf, we set a directive for PlsqlAuthenticationMode to be PerPackageOwa. What this enables is a function (always called authorize) that is executed everytime a procedure is called from our package. If this function returns a boolean false, then the http response of 401 unauthorized is returned before further processing is done.</p>
<p>Another nice feature that took me a while to track down is the use of name_array and value_array. These two variables pass in an array of the variable names and variable values. It doesn’t make total sense to pass in two arrays to handle the passed variables, but you work with what you are given. The caveat with these passed parameters is that they are only available if you enable flexible parameter passing. This is a fancy way of saying, you prepend the package name in your URL with an exclamation point (!). This comes in handy since Oracle procedures and functions expect to know everything being passed in or else they fail with a signature parameter mismatch error.</p>
<pre class="brush: sql; title: ; notranslate" title="">create or replace package body plsql_mvc as
function authorize return boolean is
begin
htp.print('authorize');
return true;
end authorize;
procedure controller(name_array in owa.vc_arr, value_array in owa.vc_arr) is
begin
htp.print('controller');
end controller;
begin
htp.print('block');
end plsql_mvc;</pre>
<p>The package body looks just as simple as the specification. The only change in flow is in a begin statement after the controller procedure. This block of code is the initialization block and is executed whenever a procedure or function is called from this package. This adds yet another very important layer to our prototype.</p>
<p>Now, earlier, I said that the PerPackageOwa directive automatically runs the authorize function before any other piece of code. That was a lie. In actuality, the initialization part gets run first and foremost before any security or routing has been performed. To see how this is processed direct your browser to your configured DAD, i.e. http://127.0.0.1/plsql_mvc/!plsql_mvc.controller. You should get something that looks like this:</p>
<pre class="brush: plain; title: ; notranslate" title="">block
authorize
controller</pre>
<p>At this point, our MVC is nothing more than a 10k package that takes input and does nothing. Its a long way from finished, but the shell is almost there. Next let’s add a new data type for our associative array, a private function to swap the variables into the array and retrieve them from the array.</p>
<p>In our package spec add the following before authorize function:</p>
<pre class="brush: sql; title: ; notranslate" title="">type associative_array is table of varchar2(256) indexed by varchar2(256);</pre>
<p>With that done, we now need a way to populate that array. In our package body, before our authorize function, we are going to add a set of private functions and a private variable.</p>
<pre class="brush: sql; title: ; notranslate" title=""> passed_variables associative_array;
function populate_array(name_array in owa.vc_arr, value_array in owa.vc_arr) return boolean;
function set_value_of(key in varchar2, value in varchar2) return boolean;
function get_value_of(key in varchar2) return varchar2;</pre>
<p>Declaring your private functions and procedures first before using them is another trick that I wish was told to everyone the first day they started working with PL/SQL. Doing this allows you to keep your initial package spec cleaner and allows you to reference private functions internally regardless of the order you declare them in.</p>
<p>Now that the declaration is out of the way, we need to define the actual code in the private functions.</p>
<pre class="brush: sql; title: ; notranslate" title=""> function populate_array(name_array in owa.vc_arr, value_array in owa.vc_arr) return boolean is
begin
for i in 1.. name_array.count loop
if set_value_of(name_array(i), value_array(i)) then
null;
else
return false;
end if;
end loop;
return true;
end;
function set_value_of(key in varchar2, value in varchar2) return boolean is
begin
passed_variables(key) := value;
if passed_variables(key) = value then
return true;
else
return false;
end if;
end;
function get_value_of(key in varchar2) return varchar2 is
begin
if passed_variables.exists(key) then
return passed_variables(key);
else
return null;
end if;
end;</pre>
<p>Our first function, populate_array, does exactly what you think it might do: populates an array. With the name_array and value_array being passed in, the variable name is matched with the same index to the value_array. We loop through the number of values in name_array and then call a function, set_value_of, to do the actual setting. I decided to split to split out this function since I thought it could be a useful private function later down the road. The set_value_of function takes the passed in key, creates an index in our passed variables table with this key and then sets the value passed in. Next, it just does a simple spot check to make sure that the passed value is equal to the value in table. It hasn’t failed with a false yet, but I’m sure it will some day.</p>
<p>The last function is the get_value_of function. This function takes a key passed in and returns the value in the table. If the key is not found, it returns a null. There will probably be some question as to why I chose to store and return varchar2s instead of some other datatype. When information is passed from HTML forms or via HTTP, there isn’t really any designation as to the type of information being passed, which means it is all plain text. When we actually go to use the variable, we will cast it to the type that we need.</p>
<p>We now have three private functions, but they don’t really do anything yet. In our controller procedure, add a boolean variable in the declaration called passed_variable_inited and set the default value to the return value of our populate_array function</p>
<pre class="brush: sql; title: ; notranslate" title="">passed_variables_inited boolean := populate_array(name_array, value_array);</pre>
<p>Next enclose our htp.print(‘controller’) section in the controller with an if statement</p>
<pre class="brush: sql; title: ; notranslate" title=""> if passed_variables_inited then
htp.print('controller');
end if;</pre>
<p>If for some reason our variables that have been passed in cannot be inserted into our passed_variables table, our function will return a false and no further processing will be done.</p>
<p>And that is the very basic building block for our framework. In the next installment, we will look at the flow of router -> security -> controller and modify our passed variables to differentiate between POST and GET variables.</p>Todd AndraeI first got introduced to Oracle’s mod_plsql, and PL/SQL in general, a year ago and have spent a good amount of time since then testing the limits of what I can do with it. If you aren’t familiar with mod_plsql, it is an Apache module that connects directly to the Oracle database and allows you to run packages that you have stored there and spit out some crass HTML4 pages (this is the default action).