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
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.