Page 2 of 3

Re: CGI Survival Guide

Posted: Sun Dec 20, 2009 10:02 am
by greyhoundcode
Seymour Clufley wrote:I wrote a CGI include that handles all the communication coming in and out of a PB exe. I've been using it for ages and it also handles returning images (although I haven't tested that for a while). If it would help, I'll post it.
I would certainly be interested to see this, and any configuration info on testing and developing PB exes within XAMPP, should anyone have already done this.

Re: CGI Survival Guide

Posted: Sun Dec 20, 2009 1:29 pm
by blueznl
As I have never done any web development, any info is wecome.

About those PHP frameworks, I assume those provide a number of API's, but for what kind of functions?

Re: CGI Survival Guide

Posted: Sun Dec 20, 2009 3:34 pm
by greyhoundcode
The frameworks typically do two things - they provide structure, often in the form of the MVC pattern, and they offer a number of classes solving common problems, ranging from pagination of database queries (you see that in action every time you perform a search on a forum such as this) to setting and retrieving cookies. The list is extensive and you could get a good insight by visiting the Kohana documentation - which is a personal favourite of mine.

The MVC (model - view - controller) pattern referred to is a way of separating presentation and logic, so that you avoid a spaghetti soup of code involving functionality and output. MVC is not restricted to web apps, nor am I a particular expert on the ins and outs of it. I certainly don't want to teach better programmers that I to suck eggs, but basically say the framework gets a request such as website.net/boats/view/504.

The request is then routed to the Boats controller, which in turn uses its View method to query the Boats model (models handle interaction with the database). 504 might be a table-id within the Boats table of the database.

On retrieving the necessary information, the controller will then call a View, which is primarily HTML mixed with purely presentational code elements. The view is then outputted to the browser.

That might or might not sound overly complicated - but I'd be happy to clarify any points. It's a system that can take a day or two to wrap your head around but makes it really fast to code.

Re: CGI Survival Guide

Posted: Mon Dec 21, 2009 6:59 pm
by Seymour Clufley
Okay, as requested here's my CGI framework. It was based on code from around the forums, which I think was mainly by Rings.

The goal with this code was to make as small a CGI system as possible, so that I could forget about it and concentrate on receiving request strings and replying to them.

For POST requests, any information will be in the string cgi\PostData. I think GET parameters come in through cgi\QueryString but I never use that method so can't remember how it works.

Anyway you can reply to the request using one of 5 procedures:
  • ReplyText(text.s)
  • ReplyHTML(html.s)
  • ReplyLocation(URLorFilename.s)
  • ReplyImage(imagenumber.i,compressionformat.i)
  • ReplyImageFile(filename.s,file's compression format.i)
You need to compile the following code to a console exe (it demonstrates returning an image):

Code: Select all

Structure CommonGatewayInterface
  AuthType.s
  ContentLength.i
  ContentType.s
  DocumentRoot.s
  GatewayInterface.s
  PathInfo.s
  PathTranslated.s
  QueryString.s
  RemoteAddr.s
  RemoteHost.s
  RemoteIdent.s
  RemotePort.s
  RemoteUser.s
  RequestURI.s
  RequestMethod.s
  ScriptName.s
  ScriptFilename.s
  ServerAdmin.s
  ServerName.s
  ServerPort.s
  ServerProtocol.s
  ServerSignature.s
  ServerSoftware.s
  HTTPAccept.s
  HTTPAcceptEncoding.s
  HTTPAcceptLanguage.s
  HTTPCookie.s
  HTTPForwarded.s
  HTTPHost.s
  HTTPPragma.s
  HTTPReferer.s
  HTTPUserAgent.s
  
  PostData.s
EndStructure
Global cgi.CommonGatewayInterface


Procedure GetCGIVariables() 
  
  cgi\AuthType = GetEnvironmentVariable("AUTH_TYPE")
  cgi\ContentLength = Val(GetEnvironmentVariable("CONTENT_LENGTH"))
  cgi\ContentType = GetEnvironmentVariable("CONTENT_TYPE")
  cgi\DocumentRoot = GetEnvironmentVariable("DOCUMENT_ROOT")
  cgi\GatewayInterface = GetEnvironmentVariable("GATEWAY_INTERFACE")
  cgi\PathInfo = GetEnvironmentVariable("PATH_INFO")
  cgi\PathTranslated = GetEnvironmentVariable("PATH_TRANSLATED")
  cgi\QueryString = GetEnvironmentVariable("QUERY_STRING")
  cgi\RemoteAddr = GetEnvironmentVariable("REMOTE_ADDR")
  cgi\RemoteHost = GetEnvironmentVariable("REMOTE_HOST")
  cgi\RemoteIdent = GetEnvironmentVariable("REMOTE_IDENT")
  cgi\RemotePort = GetEnvironmentVariable("REMOTE_PORT")
  cgi\RemoteUser = GetEnvironmentVariable("REMOTE_USER")
  cgi\RequestURI = GetEnvironmentVariable("REQUEST_URI")
  cgi\RequestMethod = GetEnvironmentVariable("REQUEST_METHOD")
  cgi\ScriptName = GetEnvironmentVariable("SCRIPT_NAME")
  cgi\ScriptFilename = GetEnvironmentVariable("SCRIPT_FILENAME")
  cgi\ServerAdmin = GetEnvironmentVariable("SERVER_ADMIN")
  cgi\ServerName = GetEnvironmentVariable("SERVER_NAME")
  cgi\ServerPort = GetEnvironmentVariable("SERVER_PORT")
  cgi\ServerProtocol = GetEnvironmentVariable("SERVER_PROTOCOL")
  cgi\ServerSignature = GetEnvironmentVariable("SERVER_SIGNATURE")
  cgi\ServerSoftware = GetEnvironmentVariable("SERVER_SOFTWARE")
  cgi\HTTPAccept = GetEnvironmentVariable("HTTP_ACCEPT")
  cgi\HTTPAcceptEncoding = GetEnvironmentVariable("HTTP_ACCEPT_ENCODING")
  cgi\HTTPAcceptLanguage = GetEnvironmentVariable("HTTP_ACCEPT_LANGUAGE")
  cgi\HTTPCookie = GetEnvironmentVariable("HTTP_COOKIE")
  cgi\HTTPForwarded = GetEnvironmentVariable("HTTP_FORWARDED")
  cgi\HTTPHost = GetEnvironmentVariable("HTTP_HOST")
  cgi\HTTPPragma = GetEnvironmentVariable("HTTP_PRAGMA")
  cgi\HTTPReferer = GetEnvironmentVariable("HTTP_REFERER")
  cgi\HTTPUserAgent = GetEnvironmentVariable("HTTP_USER_AGENT")
  
  
  ; -------------------------------------------------------
  ; now to get cgi POST data, if any
  If cgi\ContentLength>0
      *Buffer = AllocateMemory(cgi\ContentLength)
      hInput = GetStdHandle_(#STD_INPUT_HANDLE)
      ;SetConsoleMode_(hInput, #ENABLE_LINE_INPUT|#ENABLE_ECHO_INPUT|#ENABLE_PROCESSED_INPUT)
      ReadFile_(hInput, *Buffer, cgi\ContentLength, @bRead, 0)
      cgi\PostData = PeekS(*Buffer)
      FreeMemory(*Buffer)
      CloseHandle_(hInput)
  EndIf
  
EndProcedure 




; RESPONSE PROCEDURES


Macro CGI_Reply(response)
  OpenConsole()
  Written = WriteConsoleData(response,MemoryStringLength(response))
  CloseConsole()
EndMacro


Procedure ReplyLocation(URLorFilename.s)
  ; this is for redirecting, or sending a file back as response
  
  header.s = "Location: "+URLorFilename + #CRLF$ + #CRLF$
  CGI_Reply(@header)
  
EndProcedure


Procedure ReplyText(string.s)
  
  HttpAnswer.s = "Content-type: text/plain;charset=UTF-8"  + #CRLF$ + #CRLF$ + string
  CGI_Reply(@HttpAnswer)
  
EndProcedure


Procedure ReplyHTML(html.s)
  
  HttpAnswer.s = "Content-type: text/html;charset=UTF-8"  + #CRLF$ + #CRLF$ + html
  CGI_Reply(@HttpAnswer)
  
EndProcedure


Procedure.b ReplyImage(image.i,format.i)
  
  ; this is for sending a native PB image to fill an IMG element
  ; the respective image encoder will be needed depending on the format used
  ; eg. if you're sending the image as a PNG, make sure you have called UsePNGImageEncoder() somewhere
  
  If Not IsImage(image)
      ProcedureReturn #False
  EndIf
  
  contenttype.s
  Select format
      Case #PB_ImagePlugin_BMP
          contenttype = "x-ms-bmp"
      Case #PB_ImagePlugin_JPEG ; remember to call UseJPEGImageEncoder()
          contenttype = "jpeg"
      Case #PB_ImagePlugin_JPEG2000 ; remember to call UseJPEG2000ImageEncoder()
          contenttype = "jp2"
      Case #PB_ImagePlugin_PNG ; remember to call UsePNGImageEncoder()
          contenttype = "x-png"
  EndSelect
  slaveimgfile.s = "C:\slaveimgfile.tmp"
  SaveImage(image,slaveimgfile,format)
  
  ; read data back in
  file = ReadFile(#PB_Any,slaveimgfile)
  If Not file
      ProcedureReturn #False
  EndIf
  idlength = Lof(file)
  *image = AllocateMemory(idlength)
  bytes = ReadData(file,*image,idlength)
  CloseFile(file)
  DeleteFile(slaveimgfile)
  
  
  ; form response header
  header.s = "Content-type: image/"+contenttype  + #CRLF$ + #CRLF$
  
  OpenConsole()
  Written1=WriteConsoleData(@header,Len(header))
  Written2=WriteConsoleData(*image,idlength)
  CloseConsole()
  
  FreeMemory(*image)
  
  ProcedureReturn #True
  
EndProcedure


Procedure.b ReplyImageFile(filename.s,format.i)
  
  ; this is for sending the contents of an image file (.jpg etc) back to fill an IMG element
  ; no encoders or decoders are necessary since we are simply sending the file's raw data
  ; however, denote the format using #PB_ImagePlugin_[FORMAT] so that the procedure knows which content-type the data should be sent with
  
  If FileSize(filename)<0
      ProcedureReturn #False
  EndIf
  
  contenttype.s
  Select format
      Case #PB_ImagePlugin_BMP
          contenttype = "x-ms-bmp"
      Case #PB_ImagePlugin_JPEG
          contenttype = "jpeg"
      Case #PB_ImagePlugin_JPEG2000
          contenttype = "jp2"
      Case #PB_ImagePlugin_PNG
          contenttype = "x-png"
  EndSelect
  
  ; read data back in
  file = ReadFile(#PB_Any,filename)
  If Not file
      ProcedureReturn #False
  EndIf
  idlength = Lof(file)
  *image = AllocateMemory(idlength)
  bytes = ReadData(file,*image,idlength)
  CloseFile(file)
  
  
  ; form response header
  header.s = "Content-type: image/"+contenttype  + #CRLF$ + #CRLF$
  
  OpenConsole()
  Written1=WriteConsoleData(@header,Len(header))
  Written2=WriteConsoleData(*image,idlength)
  CloseConsole()
  
  FreeMemory(*image)
  
  ProcedureReturn #True
  
EndProcedure




GetCGIVariables()

;-
;- PROGRAM START


; start here
; any request data will probably be in the cgi\PostData string
; but this demo ignores the request and simply sends back an image


; do it from file...
;ReplyImageFile("C:\Image018.jpg",#PB_ImagePlugin_JPEG) : End



; do it with a native PB image...
iw = 300
ih = 225
img = CreateImage(#PB_Any,iw,ih)
StartDrawing(ImageOutput(img))
  Box(0,0,iw,ih,RGB(0,0,64))
  margin=1
  For a = 1 To 33
      level = Random(255)
      LineXY(margin+Random(iw-margin-margin),margin+Random(ih-margin-margin),margin+Random(iw-margin-margin),margin+Random(ih-margin-margin),RGB(level,level,level))
  Next a
  DrawText(10,10,"  Hello from PB  ",#Black,#Yellow)
StopDrawing()


UseJPEGImageEncoder()
ReplyImage(img,#PB_ImagePlugin_JPEG)
FreeImage(img)
Then save it in your local server folder as "pb.exe".

Then save this as an HTML file in your local server folder:

Code: Select all

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<TITLE>IMG test</TITLE>
</head>

<body>

<IMG src="pb.exe">

</body>
</html>
That should be it. Open the HTML file with your localserver prefix. Hopefully an image will be displayed!

I think other content types could be handled in a similar way, but as yet I've had no need of them so haven't accommodated them yet.

As for XAMPP, I've been using it for years to build websites offline. I can't remember the settings. I'll examine my conf files and post back if I can work out what is necessary to make it work. From what I remember, my difficulty with XAMPP was getting it to work in the first place (folders etc.) - after that, getting it to work with PB executables was easy.

As to the PB code, everything up till the line

Code: Select all

;- PROGRAM START
can be placed in an include file so you can forget all about the CGI system and concentrate on the requests and replies.

I am currently using this code as the basis for a fairly complex Ajax framework. It's done very simply: the cgi\PostData is stringfielded to get all the "aspects" of a completely custom request. A reply is then formulated and sent back with similar dressing to a JavaScript system at the client end.

Obviously it can also handle Sjax requests.

Or you can use it as simply as in the demo. But to be honest I hate CGI pages; Ajax is a far more sensible way to do things.

Re: CGI Survival Guide

Posted: Tue Dec 22, 2009 10:18 am
by Fred
You should call OpenConsole() only once (and never closeconsole()). Nice framework.

Re: CGI Survival Guide

Posted: Wed Dec 23, 2009 8:41 am
by greyhoundcode
Nice piece of work.

Re: CGI Survival Guide

Posted: Wed Dec 23, 2009 1:17 pm
by Seymour Clufley
Thank you both for the compliments. I'm always nervous about posting code in case it turns out to be rubbish. :)

Re: CGI Survival Guide

Posted: Wed Dec 23, 2009 11:19 pm
by blueznl
Seymour Clufley wrote: Thank you both for the compliments. I'm always nervous about posting code in case it turns out to be rubbish. :)
Oh, don't worry about it, it has never stopped me before :-)

ISAPI or CGI

Posted: Mon Dec 28, 2009 9:28 am
by greyhoundcode
What about compiling to a DLL, to run our web apps ISAPI style, with a view to further speed improvements over a CGI executable? So that it remains loaded in the server memory rather than a new process being spawned each time a visitor makes an HTTP request, I mean.

Re: CGI Survival Guide

Posted: Mon Dec 28, 2009 11:15 am
by DoubleDutch
Thats a really good idea.

Does anyone know if Linux have an equivalent system to DLL's as my host uses Linux?

Re: ISAPI or CGI

Posted: Sat Jan 02, 2010 5:09 am
by JackWebb
greyhoundcode wrote:What about compiling to a DLL, to run our web apps ISAPI style, with a view to further speed improvements over a CGI executable? So that it remains loaded in the server memory rather than a new process being spawned each time a visitor makes an HTTP request, I mean.
What you describing sounds almost exactly like FastCGI. It's built into the server but not all servers have this feature. Also, your cgi's have to be written as a FastCGI program. Someone on here did one but I don't remember who. I would like to learn how to do this as well. This site has some good explanation.

http://cryp.to/publications/fastcgi/

Re: CGI Survival Guide

Posted: Tue Jan 05, 2010 7:36 am
by Seymour Clufley
So that it remains loaded in the server memory rather than a new process being spawned each time a visitor makes an HTTP request
I've also been looking for a solution to this problem. And it really is a problem if a lot of data needs to be known by the CGI program. (The data for my current website takes almost a minute to load into a program!)

What I've been doing is have a separate program which is always running. This communicates with "the CGI program" by means of temporary files. The CGI program is just a go-between, which writes the request info to a file with a unique code (4983986.request). The "permanent" program scans the folder for new files, and responds to each one by creating a "response" file with the same code (4983986.response). The CGI program has been waiting for such a file to appear and when it does, the program reads it in and sends the contents back to the client, then terminates itself.

This does solve the problem and response time is actually fast. But there must be better ways for two programs to communicate than using temporary files.

Re: CGI Survival Guide

Posted: Wed Jan 06, 2010 5:08 pm
by greyhoundcode
I wonder if environmental variables could help? Though probably FastCGI or ISAPI would be the perfect answer. I suppose the problem is that this is all quite specialised stuff ... surely someone will have an answer!

Re: CGI Survival Guide

Posted: Thu Jan 28, 2010 7:57 am
by kake26
Hi all,

This thread happens to fall in line with what I am doing myself. Granted I'm just starting write a CGI handler but thats where I have to start from. I find though if you read a few RFCs this is not a complicated task. I've built a handler for CGI with relatively little effort. FastCGI, is something I want to try next. I'm more than happy to share any code I might create. My other aim is to make things cross platform since I require code that works in such a fashion. Its interesting so far to see how much smaller what I've written is then what has been posted. In any case I intend to write this up and post everything. I'll include everything I can and link it. My code is also completely purebasic. Post more when I'm done.

Re: CGI Survival Guide

Posted: Thu Jan 28, 2010 8:24 am
by Seymour Clufley
Kake26, I for one would be interested in PB code for FastCGI.