|
|
|
How to write a MODULE_LOCATION ?
Name
How to write a MODULE_LOCATION. Caudium 1.0, 1.2, 1.3 A MODULE_LOCATION is one of the more powerful modules and is not that difficult to implement. A MODULE_LOCATION can easily replace a CGI script with many performance advantages. Some of the advantages include:
Script is resident and becomes part of the web server which eliminates the
typical CGI load, interpret, execute, unload cycle associated with a Perl
script:
Rather than call a CGI that needs to be interpreted, use a MODULE_LOCATION. In addition to the MODULE_LOCATION remaining resident in the Web Server, you can maintain persistent connections with databases and can eliminate the connection/teardown time that you would normally experience with a CGI that establishes connections with a MySQL or other SQL server. For a busy site, this can mean the difference between needing more hardware to keep CGI responses quick with a simple rewrite. A real world example is that a Perl script that was expected to do near real-time data tracking and logging was 1320 lines. The script was rewritten as a MODULE_LOCATION and is handling 63% more traffic with a respectable drop in the system load. The persistent database connections accounted for most of the performance gains. The script could have used mod_perl with Apache with minor modifications, but converting the script from perl to Pike allowed the use of internal graphic manipulation, the Caudium Cache and a number of other performance tweaks that would not have been as easily added. In addition, Pike has some objects that make CGI scripting quite a bit simpler. What I'll document is a MODULE_LOCATION called SQLFORMEDIT which allows you to set up a simple form that updates an SQL database without having to hardcode the script to handle the data. While I can assure you there are bugs and things I didn't address, this module serves as a good starting point both for a MODULE_LOCATION and for accessing an SQL server within a Caudium module. In the module, there are declarations that must be made for Caudium to recognize it. Documentation can be held within a module and displayed in the Configuration Interface. For now, we will focus on the actual module declaration that defines how the MODULE_LOCATION handles input. In the create() routine, we have a defvar for the location. This defvar is one of the most important to consider as it tells the web server what URL this module should handle. Be careful with the location as it has some interesting quirks that make it very versatile. For Instance, if you specify a location of /a, your module will be called for the URL http://domain.com/a, but will also be called if the URL is http://domain.com/apple. For this reason, specifying a location without a trailing slash may answer more queries than you originally anticipated. Caudium will execute the module with the longest path first. If you have a MODULE_LOCATION with a location of /abc/ and a MODULE_LOCATION in the same virtual server with a location of /abc/def/ and the URL requested is http://domain.com/abc/def/ghi, the module with the location of /abc/def/ will be consulted first. If a module returns a 0, meaning that it does not handle that request, Caudium will hand the request to the other MODULE_LOCATION. Confusing, yes, Powerful, definitely. The main routine called in a MODULE_LOCATION is find_file. The definition will pass a string of the location that was called along with the id object. Within id, there are many different variables present. id->variables-> represent any form variables that were passed to the module. id->conf-> represent certain server configuration responses. If you get the urge to see what some of these structures are, sometimes it is easy to put perror(indices(id->variables)*"\n"); in your module, and watch the debug log. The perror statment will print to the /var/log/caudium/debug/default.1 log file, or in the /usr/local/caudium/logs/default.1 file depending on your installation. In the module, we also specify a connection to an SQL server. We do this via the id->conf-> object so that Caudium can maintain a persistent connection with the SQL Server. If there is no existing connection, Caudium will establish one. If there is an existing connection, your module will be handed a thread connection to the SQL server. This will make your module run much faster than doing an individual SQL connection each time you require data. Of course, you may need to limit the connections on your server for a lightly used module. The decision is yours to make based on your requirements. To access form variables, we use the object id->variables. I've had minor problems using the id-> object, so I try to always preface the object with (string) or (float) depending on the data. Sometimes adding a string with an id->variables->name will result in a backtrace where caudium cannot determine the data type. Consequently, if you have a form with multiple fields with the same name, id->variables->name will be an array of results. For a MODULE_LOCATION, you can return control to the web by http_redirects or http_string_answer. Typically you'll want to use http_string_answer to avoid a redirect on a Form Post or when displaying dynamic content. The return format for each would look like:
return http_string_answer("text to display","text/html");
return http_redirect("http://url.to.go.to/url");
If you want to specify partial URLs, you can do:
return http_redirect("/url",id);
and caudium will determine the current location of the query and redirect the person based specifically on the query. This is good to do if you are using cookies or 123sessions as a cookie is often set on the full domain of the server. If someone surfs to www.domain.com and has a cookie set and is redirected to domain.com, their cookie would not be readable. 123sessions.pike has a patch to fix this behavior somewhat, but doing a forced redirect to http://domain.com when the surfer was surfing http://www.domain.com can cause a new Authorization box to pop up because of the differing authentication realm, or can cause cookies that are not set with the domain correctly to not be displayed. // SQL Form Edit // // Written by Chris Davies // http://daviesinc.com/modules/ // v 0.1, 2002-02-20 int thread_safe=1; #include <module.h> inherit "module"; inherit "caudiumlib"; constant module_type = MODULE_LOCATION; constant module_name = "SQL Form Edit: Form Data Saved to SQL"; constant module_doc = #"This MODULE_LOCATION allows you to do easy editing of SQL data via forms.<p> The hidden field <b>unique</b> defines the SQL field name in the table that defines the where clause in the SQL Update.<br> The hidden field <b>tablename</b> defines the SQL table that is being updated. <br> The hidden field <b>successpage</b> defines the page that the person will be sent to after a successful update.<br> The hidden field <b>errorpage</b> defines the page where a surfer will be sent if there is an error during the SQL Update.<p> An example form follows:<p>\n\n <form method=\"post\" action=\"/form/sqlsave\"><br> <input type=\"hidden\" name=\"unique\" value=\"sequence\"><br> <input type=\"hidden\" name=\"tablename\" value=\"links\"><br> <input type=\"hidden\" name=\"successpage\" value=\"/success.rxml\"><br> <input type=\"hidden\" name=\"errorpage\" value=\"/form.rxml\"><br> <sqloutput query=\"select * from links where sequence=3\"><br> <input type=\"hidden\" name=\"sequence\" value=\"#sequence#\"><br> <table><br> <tr><td>title</td><td><input type=\"text\" name=\"title\" value=\"#title#\" size=80></td></tr><br> <tr><td>descr</td><td><input type=\"text\" name=\"descr\" value=\"#descr#\" size=80></td></tr><br> <tr><td>returnlink</td><td><input type=\"text\" name=\"returnlink\" value=\"#returnlink#\" size=80></td></tr><br> </table><br> </sqloutput><br> <input type=\"submit\" value=\"save\"><br> </form><br> "; // If module_unique is set to 1, you would not be able to have multiple // copies of this to process multiple forms. However, this module is // really designed to be data driven and not hardcoded to a particular // setup. constant module_unique = 1; void create() { // // Defining the location tells Caudium what URL to answer for // In this case, the URL that this module would respond to would be // // http://domain.com/form/sqlsave // // This would correspond to <form action="http://domain.com/form/sqlsave"> // defvar("location", "/form/sqlsave", "Mount point", TYPE_LOCATION, "Location"); // This is a standard definition for the SQL Server that you will be // updating. Saving this information in the Config Interface keeps // certain information from being available on the web. defvar ("sqldb", "localhost", "SQL server", TYPE_STRING, "This is the host running the SQL server with the " "authentication information.<br>\n" "Specify an \"SQL-URL\":<ul>\n" "<pre>[<i>sqlserver</i>://][[<i>user</i>][:<i>password</i>]@]" "[<i>host</i>[:<i>port</i>]]/<i>database</i></pre></ul><br>\n" "Valid values for \"sqlserver\" depend on which " "sql-servers your pike has support for, but the following " "might exist: msql, mysql, odbc, oracle, postgres.\n", ); } mapping find_file(string f,object id) { // Create an object to talk with the SQL server. If there is a connection // already created by Caudium, this will create a new communications // thread as Caudium will maintain a persistent connection to the SQL // server object db; if (id->conf->sql_connect) db = id->conf->sql_connect(QUERY(sqldb)); else perror("REFER: Error: no connect<p>\n"); // This defines the field that is the 'auto_increment' or unique field // identifiers to make sure that we are editing and updating the correct // record in the SQL server database string unique = (string)id->variables->unique; // Get the list of field names from the SQL database that is being edited array fields = indices(db->query("select * from "+(string)id->variables->tablename+ " limit 1")[0]); // Walk through the indices to see what field names could be updated in the // edit. Then, remove the tablename.fieldname variables, see which // form values are defined in the form post and update only those fields // that exist in the form that is submitted. string update = ""; foreach (fields,string field) { if (field[0..(sizeof((string)id->variables->tablename))] != (string)id->variables->tablename+".") if (id->variables[field]) update += "," + field + " = '" + db->quote((string)id->variables[field]) + "'"; } // remove the leading , update = update[1..]; // This builds the query that needs to be sent to the SQL server and is // as data driven as possible update = "update " + (string)id->variables->tablename + " set " + update + " where " + (string)id->variables->unique + "='" + (string)id->variables[(string)id->variables->unique] + "'"; // Mysql returns 0 rows updated if there is no change, so the only thing // we can really do here is check to make sure there is no error when // the SQL statement is executed. catch { db->query(update); return http_redirect((string)id->variables->successpage,id); }; return http_redirect((string)id->variables->errorpage,id); } |
|
Copyright © 2000 - 2005
The Caudium Group
All Rights Reserved. Hosting by Kazar.
|
|