[Home] [Downloads] [Search] [Help/forum]


Register forum user name Search FAQ

Gammon Forum

[Folder]  Entire forum
-> [Folder]  Programming
. -> [Folder]  General
. . -> [Subject]  Writing dynamic web pages using Lua

Writing dynamic web pages using Lua

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page


Pages: 1  2 

Posted by Nick Gammon   Australia  (23,001 posts)  [Biography] bio   Forum Administrator
Date Reply #15 on Tue 02 May 2006 05:54 AM (UTC)

Amended on Wed 03 May 2006 06:43 AM (UTC) by Nick Gammon

Message

Well, I try *grin*.

I have done my own documentation on Lua (version 5.0.2) base, coroutines, debug, io, math, os, string, and table functions.


- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (23,001 posts)  [Biography] bio   Forum Administrator
Date Reply #16 on Wed 03 May 2006 08:55 PM (UTC)

Amended on Wed 03 May 2006 09:07 PM (UTC) by Nick Gammon

Message
Accessing the MySQL database from the dynamic web page

Now let's look at accessing a database. This is simple enough, using the luasql library available from the LuaForge site. Assuming we have installed the shared library, we use loadlib to load it into our dynamic page. I have made up an example database using these SQL commands:


CREATE TABLE widgets (
  widget_id int NOT NULL auto_increment primary key,
  widget_name varchar(64) NOT NULL,
  weight int,
  price double
  ) 

INSERT INTO widgets VALUES (1,'Gyro spanner',15,102);
INSERT INTO widgets VALUES (2,'Packet of things',22,14);
INSERT INTO widgets VALUES (3,'Roll of paper',42,95);



The page below will display the first 100 records as a table.

This page makes the widget_id a hyperlink, so we can see more details about an individual widget if we click on the link. The page assumes you are calling it using the method described earlier using the cgiutils.lua as the calling module. The URL I used was:


http://10.0.0.2/cgi-bin/cgiutils.lua/testing/test11.lua


Thus, the file below was called test11.lua and placed in the "testing" subdirectory below where cgiutils.lua resides.


-- load the MySQL dll
assert (loadlib ("/usr/local/lib/libmysql.2.0.1.so", "luaopen_luasqlmysql")) ()

-- HTTP and HTML headers
show_html_header ("Database example")
  
-- Page header
print "<h1>Listing of Widgets</h1>"
  
-- create environment object
env = assert (luasql.mysql())

-- connect to data source
con = assert (env:connect ("databasename", "username", "password", "localhost"))

-- retrieve a cursor
cur = assert (con:execute ("SELECT * from widgets LIMIT 100" ))

-- table header row
print [[
<table border=1 cellpadding=3>
<tr>
<th>ID</th><th>Name</th>
</tr>
]]


-- print all rows, the rows will be indexed by field names
row = cur:fetch ({}, "a")

while row do
  
  -- start new row
  print "<tr>"

  print (string.format (
          '<td><a href="test12.lua?id=%i">%i</a></td>', 
          row.widget_id, 
          row.widget_id))
   
  -- Widget name
  print (string.format (
         '<td>%s</td>', 
         fixhtml (row.widget_name)))

  -- end of row
  print "</tr>"
         
  -- reusing the table of results
  row = cur:fetch (row, "a")
  
end

-- end of table
print "</table>"

-- close everything
cur:close()
con:close()
env:close()  
  
-- wrap up page 
show_html_footer ()


Output was (however displayed as an HTML table):


Listing of Widgets

ID	Name
1 	Gyro spanner
2 	Packet of things
3 	Roll of paper


- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (23,001 posts)  [Biography] bio   Forum Administrator
Date Reply #17 on Wed 03 May 2006 09:49 PM (UTC)

Amended on Wed 03 May 2006 09:51 PM (UTC) by Nick Gammon

Message
Accessing the GET and POST data to display an individual record

The next example (which implements test12.lua which is linked to in the previous post) shows how you can access the variable data which the user chooses.

The important line in this file is:


show_widget (post_data.id or get_data.id)


The tables post_data and get_data have been automatically set up by cgiutils.lua before loading this page. If the user clicks on the "Gyro spanner", then this is the hyperlink:


http://10.0.0.2/cgi-bin/cgiutils.lua/testing/test12.lua?id=1


The important part here is "?id=1" which puts the value "1" into the key "id" in the get_data table. Recall that get_data is the data from the URL.

However the displayed page also lets the user enter another widget ID by entering the number into the form box and clicking on the "Show" button. In this case the widget id ends up in the post_data table. The expression "post_data.id or get_data.id" used above means we prefer the post_data to the get_data, if there is a clash.

The function show_widget:


  • Exits if no widget supplied (ie test12.lua called with no id on the URL or form data)

  • Checks the widget id is numeric (to avoid problems with the SQL syntax)

  • Opens the database

  • Locates the desired widget with a SELECT statement

  • Displays an error if that widget is not on file

  • Otherwise displays its data

  • Closes the database


The main script calls show_widget and then displays a form to let the user choose a different widget ID (even if the previous one was not there).


function show_widget (which)
  
  -- exit if nothing
  if not which then
    return
  end -- no widget ID supplied

  -- check a number
  local id = tonumber (which)
  
  if not id then
    show_error (string.format ("Widget ID '%s' is not numeric", tostring (which)))
    return
  end -- not numeric
  
  -- load the MySQL dll
  assert (loadlib ("/usr/local/lib/libmysql.2.0.1.so", "luaopen_luasqlmysql")) ()
  
  -- create environment object
  local env = assert (luasql.mysql())
  
  -- connect to data source
  local con = assert (env:connect ("databasename", "username", "password", "localhost"))
  
  -- retrieve a cursor
  local cur = assert (con:execute ("SELECT * from widgets WHERE widget_id = " .. id))
  
  -- fetch the row
  local row = cur:fetch ({}, "a")
  
  if row then
    show_table (row)
  else
    show_error (string.format ("Widget %i not on file", id))
  end -- if
  
  -- close everything
  cur:close()
  con:close()
  env:close()  

end -- show_widget

-- HTTP and HTML headers
show_html_header ("Form example")
  
-- Page header
print "<h1>Display one widget</h1>"

-- show requested record  
show_widget (post_data.id or get_data.id)
 
-- let them enter a new ID
print [[
<form METHOD="post" ACTION="test12.lua">
<p>Enter Widget ID: <input type=text Name="id" size=6 maxlength=6>
<input Type=submit Name=Submit Value="Show">
</p>
</form>
]]

-- wrap up page 
show_html_footer ()




Example output:


Display one widget

widget_id	1
price	102
widget_name	Gyro spanner
weight	15

Enter Widget ID: ______ [Show]

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (23,001 posts)  [Biography] bio   Forum Administrator
Date Reply #18 on Thu 04 May 2006 01:19 AM (UTC)

Amended on Thu 04 May 2006 01:20 AM (UTC) by Nick Gammon

Message
Example of using cookies

This page (test13.lua) demonstrates getting a cookie value, and setting it from a form. This is a simple demonstration of how you can use a cookie to remember a value from one time the user goes to a page, to another (like in this forum, when you log on).


-- Example of using cookies

-- if they changed it use the new value, otherwise take the cookie value
foo = post_data.foo or cookie_data.foo

-- HTTP header
show_http_header { foo = foo }

-- HTML header
show_html_header ("Cookie example")
  
-- Page header
print "<h1>Testing setting a cookie</h1>"
 
-- let them enter a new cookie value
print [[
<form METHOD="post" ACTION="test13.lua">
<p>Enter cookie value: <input type=text Name="foo" size=20 maxlength=20
]]
if foo then
  print ('value="' .. foo .. '"')
end -- if cookie already set
print [[
> 
<input Type=submit Name=Submit Value="Show">
</p>
</form>
]]



Since cookies are sent as part of the HTTP header, they must be changed (if you are planning to set or change them) as the very first thing you do, before sending any other HTML code. Thus the first thing this script does is set the variable "foo" to be either the post data (in case it has just been changed) or the cookie data (from last time).

Then we call:


show_http_header { foo = foo }


The optional argument to show_http_header lets you specify a table of cookie name/value pairs. You could enhance this by adding expiry dates etc., but for simplicity I haven't here.

Then the page displays a form showing the current value and lets you change it.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (23,001 posts)  [Biography] bio   Forum Administrator
Date Reply #19 on Thu 04 May 2006 11:31 PM (UTC)
Message
Making the URL look nicer

One minor problem with the technique described above is that the URL that the web page user sees looks a bit strange, like this:


http://www.yoursite.com/cgi-bin/cgiutils.lua/testing/test12.lua


The "problem" is the two lots of ".lua" items in the URL. It works, but looks strange. The simple solution is to simply rename cgiutils.lua as something that looks like a directory, eg. "scripts". Then the URL becomes:


http://www.yoursite.com/cgi-bin/scripts/testing/test12.lua


Now you are hiding from the users of your web site that the script is really the file "scripts". It just makes the URL look a bit more natural.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (23,001 posts)  [Biography] bio   Forum Administrator
Date Reply #20 on Fri 05 May 2006 04:55 AM (UTC)

Amended on Fri 05 May 2006 07:51 AM (UTC) by Nick Gammon

Message
Making writing HTML easier

Now that we have got this far, let's make generating the actual HTML easier. One thing page designers do an awful lot of is stuff like this:


<h1> My page heading &amp; other stuff </h1>


It would be nice to simplify this. We need to group together:


  • The opening tag (eg. <h1> )
  • The text in the middle, escaping out things like <, > and &
  • The closing tag (eg. </h1> )


A simple function will do this:


function tag (name, contents)

  print ("<" .. name .. ">" .. 
         fixhtml (contents) .. "</" .. name .. ">")

end -- function tag


We can call it like this:

tag ("h1", "My page heading & other stuff")

However, this function make a couple of assumptions:


  • That the tag doesn't have arguments (like align="left")
  • That the contents are a simple string


For more complex things like tables, this isn't really true.

Let's make a more complex function, that will still handle the simple case, but also handles tag arguments, nested tags, and so on.

This is my next attempt:


function tag (name, ...)

local i = 1  -- 2nd argument may be parameters, or simply the thing to be displayed

  io.write ("<" .. name)

  -- handle parameters (like align="left")
  if type (arg [i]) == "table" then
    for k, v in pairs (arg [i]) do
      -- boolean attributes (eg. selected=true) have to be shown simply as "selected" 
      -- or omitted if false
      if type (v) == "boolean" then
        if v then
          io.write (' ' .. k)
        end -- value is true
      else      
        io.write (' ' .. k .. '="' .. fixhtml (v) .. '"')
      end -- if boolean or not
    end -- for each parameter 
    i = i + 1
  end -- tag parameters supplied
  
  -- if no contents assume closed tag
  if not arg [i] then
    print ("/>")
    return
  end -- no argument - must be tag with no contents (eg. <br/>)

  -- finished the opening tag
  io.write (">")
  
  -- now the contents of the tag
  -- if a function, call it, passing it any further arguments
  
  if type (arg [i]) == "function" then
    local f = arg [i]
    local j
    -- remove elements up to the function arguments
    for j = 1, i do
      table.remove (arg, 1)
    end -- removing argument
    print ()  -- may as well have a newline for neatness
    f (unpack (arg))
  else
    io.write (fixhtml (arg [i]))
  end -- if
  
  -- close the tag (and do the newline)
  print ("</" .. name .. ">")

end -- function tag




The first argument to "tag" is still the name of the tag (eg. hr, table, tr, th).

The next argument can optionally be a table of parameters. If the second argument is a table, it is expanded out, as arguments to the tag. For example:


tag ("hr", {size=10, width=110, align="left"} )


This function call generates this line:


<hr align="left" width="110" size="10"/>


In this case the absence of any further arguments generates an "empty" tag, that is one with no contents.

Now let's try something with contents.



tag ("p", {align="center"}, "This is my paragraph" )


This function call generates this line:


<p align="center">This is my paragraph</p>


Now we have the opening and closing tag.

Finally we can call a function rather than have straight text. This allows us to nest calls, to do something like iterate through a table. Here is an example:


function showrow (k, v) 
  tag ("th", k)
  tag ("td", v)
end -- showrow

function showtable (t)
  for k, v in ipairs (t) do
    tag ("tr", {align="left"}, showrow, k, v)  
  end -- for
end -- showtable

tag ("table",  {border = 1, cellpadding = 5}, 
     showtable, 
     { "every", "good", "boy", "deserves", "fruit" }
    ) 


This example uses the tag function a number of times. The last one (the one that is called first) sets up a table with the "table" tag, and requests that the "showtable" function be called. The arguments to showtable are the inlined table.

The function showtable then iterates through each table item, doing a "tr" tag, and for each one calls "showrow", passing to that two arguments, the key and value for that particular row.

Finally the function showrow calls tag twice, to do the "th" and "td" data elements. The generated HTML from all this is;


<table border="1" cellpadding="5">
<tr align="left">
<th>1</th>

<td>every</td>
</tr>
<tr align="left">
<th>2</th>
<td>good</td>
</tr>
<tr align="left">
<th>3</th>
<td>boy</td>
</tr>
<tr align="left">
<th>4</th>

<td>deserves</td>
</tr>
<tr align="left">
<th>5</th>
<td>fruit</td>
</tr>
</table>





Overall I think that the tag function simplifies the generation of HTML, including complex nested calls, and ensures that each opening tag is balanced by a closing tag.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (23,001 posts)  [Biography] bio   Forum Administrator
Date Reply #21 on Wed 10 May 2006 10:42 PM (UTC)

Amended on Fri 12 May 2006 04:28 AM (UTC) by Nick Gammon

Message
How to write a page that executes user-entered Lua code

This is an interesting project. I often want to experiment with Lua snippets but am not sitting at my PC with Lua installed. This page here demonstrates how you can let someone execute Lua code from their web browser.

To make it fairly robust, it has a number of interesting features:


  • Each time you run it, it remembers what code you entered last time (so you can fix syntax errors)

  • Any output from the executed code (via the 'print' statement) is output to the page, however with HTML properly escaped (that is, "<" turned into "&lt;" and so on)

  • A syntax error during the compile phase is shown as such

  • Runtime errors are detected and shown with a stack traceback

  • To hopefully stop malicious code, the entire code is executed in a specially-constructed "sandbox" which has most of the library Lua functions in it, however omitting things like "os.execute" and "os.remove" which might cause grief.

  • To guard against runaway code (like "repeat until false") hanging the web server, there is code to detect that.

  • To guard against someone writing thousands of lines of output, and making a web page that browsers probably could not load, there is code to stop that (built into the "print" function)

  • To guard against someone writing thousands of bytes, without starting a new line, there is also a test on the number of bytes printed.

  • Added a simulated io.write function to help people who want to build up an output line in pieces.


Here is the code (I called it "execute.lua" which is referred to internally in the <form> data) ...


-- limits
runaway_instruction_limit = 100000  -- how many instructions
linelimit = 500                     -- how many lines can be printed
outputlimit = 10000                 -- how many bytes can be printed

-- this is called if 'runaway_instruction_limit' instructions are done
function hook ()
  error ("Runaway instruction limit reached")
end -- hook

-- heading for errors
function heading (s)
  print '<p><span style="font-family: sans-serif; font-weight: bolder;">'
  print (fixhtml (s))
  print '</span></p>'
end -- heading

linecount = 0    -- how many lines they have printed so far
outputcount = 0  -- how many bytes they have printed so far

-- local print function for use inside executed code, uses fixhtml on the string
-- does a newline at the end, adds a space between arguments
function myprint (...)
  local i
  
  -- check for runaway outputting
  linecount = linecount + 1  
  assert (linecount <= linelimit, "too many lines printed")
  
  -- I am not using ipairs because that stops on a nil argument
  for i = 1, arg.n do
    local s = fixhtml (tostring (arg [i]))
    outputcount = outputcount + string.len (s) + 1
    assert (outputcount <= outputlimit, "too many bytes printed")
    io.write (s .. " ")
  end -- for
  print ""  -- final end of line
  
end -- myprint

-- local io.write function for use inside executed code, uses fixhtml on the string
-- doesn't do a newline, doesn't add a space between arguments
function mywrite (...)
  local i

  -- I am not using ipairs because that stops on a nil argument
  for i = 1, arg.n do
    local s = fixhtml (tostring (arg [i]))
    outputcount = outputcount + string.len (s)
    assert (outputcount <= outputlimit, "too many bytes printed")
    io.write (s)
  end -- for
end -- mywrite

-- makes a sandbox to make execution safer
function MakeSandbox ()
  local box = {}
  local _, name

  -- add in global functions we consider safe
  for _, name in ipairs 
  {
   -- functions 
   "_TRACEBACK", "__pow", -- helper functions
   "assert", "error", -- error management
   "collectgarbage", "gcinfo", -- garbage collection
   "getmetatable", "setmetatable", -- metatables
   "ipairs", "pairs", "next", -- looping through tables
   "loadstring", "pcall", "xpcall", -- calling functions
   "rawequal", "rawget", "rawset", -- raw table management 
   "tonumber", "tostring", "type", -- type management
   "unpack", -- argument management
   
   -- version string
   "_VERSION",
   }
   do
    box [name] = _G [name]
   end -- for each global function
    
   box._G = box  -- _G points to itself
   box.print = myprint -- special print function escapes HTML codes
   box.io = {}
   box.io.write = mywrite -- my special write function
   
  -- now other libraries
  -- we will omit io - don't want file io
  -- also omit debug, that could be dangerous to play with
  for _, name in ipairs 
  { "coroutine", "math", "os", "string", "table", }
   do
     box [name] = {}  -- make library sub-table
     
     -- copy functions into the table
     local k, v
     for k, v in pairs (_G [name]) do
       box [name] [k] = v
     end -- for each entry in the library table 
   end -- for each library
  
  -- omit a couple of the os functions we don't want them to play with
  box.os.execute = nil  -- no executing arbitrary code
  box.os.exit = nil     -- don't terminate us
  box.os.remove = nil   -- don't remove files
  box.os.rename = nil   -- don't rename files
  box.os.getenv = nil   -- don't leak information about our environment
    
  return box

end -- MakeSandbox

-- executes the code they entered, in a "sandbox" environment
function Execute (code)

  local ok
  
  -- this is called for a runtime error
  local function error_handler (err)
    heading ("Runtime error")
    print (fixhtml (err))
    local s = debug.traceback ()
    -- strip traceback from the xpcall upwards
    s = string.gsub (s, "%[C%]%: in function `xpcall'.*", "")
    print (fixhtml (s))
  end -- function error_handler
  
  -- try compiling the code
  local f, err = loadstring (code, "Code")
  
  -- error on compile
  if not f then
    heading ("Syntax error")
    print ("<pre><code>" .. fixhtml (err) .. "</code></pre>")
    return  -- cannot execute it
  end -- syntax error

  -- sandbox them so they don't start deleting files
  setfenv (f, MakeSandbox ())
  
  -- put limit on runaway loops
  debug.sethook (hook, "", runaway_instruction_limit)
  
  -- try running the code
  heading ("Output")
  print ("<pre><code>")
  ok = xpcall (f, error_handler)
  print ("</code></pre>")

  return ok
end -- function Execute

-->>>> CODE STARTS HERE <<<<--

-- HTTP and HTML headers
show_html_header ("Execute Lua code")
  
print [[
<form METHOD="post" ACTION="execute.lua">
<p>Enter Lua code to be executed:</p> 
<p>
<textarea name="code" wrap="virtual" rows="20" cols="80" >]]
io.write (fixhtml (post_data.code or ""))  -- what they had last time
print [[
</textarea>
</p>
<input Type=submit Name=Submit Value="Execute">
</form>
]]

-- now execute it

if post_data.code then
  if Execute (string.gsub (post_data.code, "\r", "")) then
    heading "Completed OK"
  end -- if finished ok
end -- if

show_html_footer ()



This code assumes you are using the method described earlier in this thread of executing using cgiutils.lua. This is the URL I used to test it:


http://10.0.0.2/cgi-bin/cgiutils.lua/testing/execute.lua


If the executed code completes successfully you see the heading "Completed OK", to confirm the script finished.

I have made a couple of edits to the code, taking out getfenv and setfenv, on the grounds that they might be used for escaping from the sandbox environment.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (23,001 posts)  [Biography] bio   Forum Administrator
Date Reply #22 on Thu 11 May 2006 12:04 AM (UTC)
Message
This test script can be used in conjunction with the "Lua execute" page presented above. It is a slight variation on the "tprint" utility which is included with MUSHclient. It is a recursive table-printer. In this case it prints the _G table, showing all of the functions and variables made available in the sandbox to the script writer.


function tprint (t, indent, done)
  -- show strings differently to distinguish them from numbers
  local function show (val)
    if type (val) == "string" then
      return '"' .. val .. '"'
    else
      return tostring (val)
    end -- if
  end -- show
  -- entry point here
  done = done or {}
  indent = indent or 0
  for key, value in pairs (t) do
    io.write (string.rep (" ", indent)) -- indent it
    if type (value) == "table" and not done [value] then
      done [value] = true
      print(show (key), ":");
      tprint (value, indent + 2, done)
    else
      io.write (show (key), "=")
      print (show (value))
    end
  end
end


tprint (_G)



Output for me was:


Output


"string" : 
  "sub"=function: 0x8066758 
  "gfind"=function: 0x80668e8 
  "rep"=function: 0x80667c0 
  "find"=function: 0x80668b0 
  "char"=function: 0x8066790 
  "dump"=function: 0x8066878 
  "byte"=function: 0x8066960 
  "upper"=function: 0x8066840 
  "gsub"=function: 0x8066920 
  "format"=function: 0x8066998 
  "len"=function: 0x8066728 
  "lower"=function: 0x8066808 
"xpcall"=function: 0x8063d18 
"tprint"=function: 0x8072f80 
"table" : 
  "setn"=function: 0x8065308 
  "insert"=function: 0x80654d8 
  "getn"=function: 0x80653b8 
  "foreachi"=function: 0x8065380 
  "remove"=function: 0x8065510 
  "sort"=function: 0x8065340 
  "concat"=function: 0x80650b8 
  "foreach"=function: 0x80652d0 
"rawset"=function: 0x8063ca8 
"gcinfo"=function: 0x8063d90 
"tonumber"=function: 0x8064838 
"print"=function: 0x806b298 
"coroutine" : 
  "resume"=function: 0x80650f0 
  "yield"=function: 0x8065128 
  "status"=function: 0x8065078 
  "wrap"=function: 0x8065040 
  "create"=function: 0x8064fc8 
"os" : 
  "setlocale"=function: 0x8064aa8 
  "getenv"=function: 0x80649d8 
  "difftime"=function: 0x8064a38 
  "time"=function: 0x8064ae0 
  "clock"=function: 0x8065000 
  "date"=function: 0x8065470 
  "tmpname"=function: 0x8064b18 
"__pow"=function: 0x80671e0 
"math" : 
  "log"=function: 0x8066058 
  "atan"=function: 0x8067010 
  "ldexp"=function: 0x8067248 
  "deg"=function: 0x80670e0 
  "tan"=function: 0x8064e78 
  "cos"=function: 0x8064e48 
  "abs"=function: 0x8066bd8 
  "random"=function: 0x8067178 
  "rad"=function: 0x8067140 
  "frexp"=function: 0x8064f50 
  "pi"=3.1415926535898 
  "floor"=function: 0x8064ee8 
  "acos"=function: 0x8064df0 
  "max"=function: 0x80672e0 
  "sqrt"=function: 0x8067280 
  "atan2"=function: 0x8067048 
  "asin"=function: 0x8064db8 
  "min"=function: 0x80672b0 
  "mod"=function: 0x8064f18 
  "log10"=function: 0x8067080 
  "exp"=function: 0x80670b0 
  "pow"=function: 0x8067110 
  "randomseed"=function: 0x80671b0 
  "sin"=function: 0x8064da0 
  "ceil"=function: 0x8064eb0 
"_G" : 
  "string"=table: 0x8072f30 
  "xpcall"=function: 0x8063d18 
  "tprint"=function: 0x8072f80 
  "table"=table: 0x8072f58 
  "rawset"=function: 0x8063ca8 
  "gcinfo"=function: 0x8063d90 
  "tonumber"=function: 0x8064838 
  "print"=function: 0x806b298 
  "coroutine"=table: 0x8063b78 
  "os"=table: 0x8063c08 
  "__pow"=function: 0x80671e0 
  "math"=table: 0x8063be0 
  "_G"=table: 0x80686f0 
  "pcall"=function: 0x8063ce0 
  "_VERSION"="Lua 5.0.2" 
  "unpack"=function: 0x8064918 
  "type"=function: 0x80648a8 
  "pairs"=function: 0x80647c8 
  "next"=function: 0x8064758 
  "_TRACEBACK"=function: 0x8067898 
  "assert"=function: 0x80648e0 
  "tostring"=function: 0x8064870 
  "ipairs"=function: 0x8064790 
  "loadstring"=function: 0x8063e38 
  "io" : 
    "write"=function: 0x806b2b8 
  "rawequal"=function: 0x8064950 
  "rawget"=function: 0x8064988 
  "collectgarbage"=function: 0x8063d58 
  "setmetatable"=function: 0x80646b0 
  "getmetatable"=function: 0x8064670 
  "error"=function: 0x8064630 
"pcall"=function: 0x8063ce0 
"_VERSION"="Lua 5.0.2" 
"unpack"=function: 0x8064918 
"type"=function: 0x80648a8 
"pairs"=function: 0x80647c8 
"next"=function: 0x8064758 
"_TRACEBACK"=function: 0x8067898 
"assert"=function: 0x80648e0 
"tostring"=function: 0x8064870 
"ipairs"=function: 0x8064790 
"loadstring"=function: 0x8063e38 
"io"=table: 0x8065d58 
"rawequal"=function: 0x8064950 
"rawget"=function: 0x8064988 
"collectgarbage"=function: 0x8063d58 
"setmetatable"=function: 0x80646b0 
"getmetatable"=function: 0x8064670 
"error"=function: 0x8064630 

Completed OK 

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (23,001 posts)  [Biography] bio   Forum Administrator
Date Reply #23 on Fri 08 Sep 2006 04:30 AM (UTC)
Message
I have made a minor change to the cgiutils.lua file presented further back.

When testing it with the thttpd server it hung on the line which retrieved the post data. It seems that I should only be reading from stdin if the method is actually POST, otherwise it can just hang. This fixes it:



  if os.getenv ("REQUEST_METHOD") == "POST" then
    for _, v in ipairs (split (io.read ("*a"), "&")) do
      assemble_value (v, post_data)
    end -- for
  end -- if post


- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (23,001 posts)  [Biography] bio   Forum Administrator
Date Reply #24 on Sat 09 Sep 2006 10:03 PM (UTC)
Message
I also found, using thttpd, that with some clients they hung with large amounts being returned, if you didn't send another header (Connection: close), plus thttpd didn't identify itself or send the date. A couple of extra lines fixes that:

In cgiutils.lua, after the line:


 print ("Content-Type: text/html; charset=iso-8859-1")


... add these lines:


  print ("Date: " .. os.date ("%a, %d %b %Y %H:%M:%S GMT"))
  print ("Server: " .. "thttpd/2.25b")  --> or whatever it is
  print ("Connection: close")


- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


153,798 views.

This is page 2, subject is 2 pages long:  [Previous page]  1  2 

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.

[Home]


Written by Nick Gammon - 5K   profile for Nick Gammon on Stack Exchange, a network of free, community-driven Q&A sites   Marriage equality

Comments to: Gammon Software support
[RH click to get RSS URL] Forum RSS feed ( https://gammon.com.au/rss/forum.xml )

[Best viewed with any browser - 2K]    [Hosted at HostDash]