CGI Survival Guide

Everything else that doesn't fall into one of the other PB categories.
User avatar
greyhoundcode
Enthusiast
Enthusiast
Posts: 112
Joined: Sun Dec 30, 2007 7:24 pm

Re: CGI Survival Guide

Post 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.
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Re: CGI Survival Guide

Post 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?
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
User avatar
greyhoundcode
Enthusiast
Enthusiast
Posts: 112
Joined: Sun Dec 30, 2007 7:24 pm

Re: CGI Survival Guide

Post 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.
Seymour Clufley
Addict
Addict
Posts: 1264
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Re: CGI Survival Guide

Post 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.
JACK WEBB: "Coding in C is like sculpting a statue using only sandpaper. You can do it, but the result wouldn't be any better. So why bother? Just use the right tools and get the job done."
Fred
Administrator
Administrator
Posts: 18153
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: CGI Survival Guide

Post by Fred »

You should call OpenConsole() only once (and never closeconsole()). Nice framework.
User avatar
greyhoundcode
Enthusiast
Enthusiast
Posts: 112
Joined: Sun Dec 30, 2007 7:24 pm

Re: CGI Survival Guide

Post by greyhoundcode »

Nice piece of work.
Seymour Clufley
Addict
Addict
Posts: 1264
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Re: CGI Survival Guide

Post by Seymour Clufley »

Thank you both for the compliments. I'm always nervous about posting code in case it turns out to be rubbish. :)
JACK WEBB: "Coding in C is like sculpting a statue using only sandpaper. You can do it, but the result wouldn't be any better. So why bother? Just use the right tools and get the job done."
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Re: CGI Survival Guide

Post 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 :-)
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
User avatar
greyhoundcode
Enthusiast
Enthusiast
Posts: 112
Joined: Sun Dec 30, 2007 7:24 pm

ISAPI or CGI

Post 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.
User avatar
DoubleDutch
Addict
Addict
Posts: 3220
Joined: Thu Aug 07, 2003 7:01 pm
Location: United Kingdom
Contact:

Re: CGI Survival Guide

Post by DoubleDutch »

Thats a really good idea.

Does anyone know if Linux have an equivalent system to DLL's as my host uses Linux?
https://deluxepixel.com <- My Business website
https://reportcomplete.com <- School end of term reports system
User avatar
JackWebb
Enthusiast
Enthusiast
Posts: 109
Joined: Wed Dec 16, 2009 1:42 pm
Location: Tampa Florida

Re: ISAPI or CGI

Post 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/
Make everything as simple as possible, but not simpler. ~Albert Einstein
Seymour Clufley
Addict
Addict
Posts: 1264
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Re: CGI Survival Guide

Post 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.
JACK WEBB: "Coding in C is like sculpting a statue using only sandpaper. You can do it, but the result wouldn't be any better. So why bother? Just use the right tools and get the job done."
User avatar
greyhoundcode
Enthusiast
Enthusiast
Posts: 112
Joined: Sun Dec 30, 2007 7:24 pm

Re: CGI Survival Guide

Post 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!
kake26
Enthusiast
Enthusiast
Posts: 157
Joined: Sun Jan 25, 2004 7:21 pm
Contact:

Re: CGI Survival Guide

Post 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.
Last edited by kake26 on Thu Jan 28, 2010 9:21 am, edited 1 time in total.
Seymour Clufley
Addict
Addict
Posts: 1264
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Re: CGI Survival Guide

Post by Seymour Clufley »

Kake26, I for one would be interested in PB code for FastCGI.
JACK WEBB: "Coding in C is like sculpting a statue using only sandpaper. You can do it, but the result wouldn't be any better. So why bother? Just use the right tools and get the job done."
Post Reply