# VBForums CodeBank > CodeBank - Visual Basic 6 and earlier >  VB6 FastCGI Server

## jpbro

*NOW ON GITHUB - SOURCE CODE IN THIS THREAD WON'T BE UPDATED ANYMORE - GO TO GITHUB INSTEAD!! https://github.com/jpbro/VbFcgi*

This is a FastCGI web application server & framework written in VB6 for use with Nginx (and possibly other web server that support FastCGI).

It handles FCGI_BEGIN_REQUEST, FCGI_END_REQUEST, FCGI_PARAMS, FCGI_STDIN, FCGI_STDOUT, and FCGI_STDERR records/communications quite nicely (currently being tested against the Nginx webserver). It can also launch itself as multiple processes to work against Nginx load-balancing features.


*Usage*
First you need to compile the latest source code (to vbFcgiHost.exe), then copy the compiled file and the vbRichClient5 DLLs (vbRichClient5.dll, DirectCOM.dll, and vb_cairo_sqlite.dll) to the same deployment folder.

*LATEST SOURCE CODE NOW ON GITHUB - SOURCE CODE IN THIS THREAD WON'T BE UPDATED ANYMORE - GO TO GITHUB INSTEAD!! https://github.com/jpbro/VbFcgi*

Next, you need a properly configured Nginx web server running, optionally with upstream load-balancing servers defined. For example, in nginx.conf, you might have something like this defined:



```
http {
   upstream backend {
         # Load-balancing across multiple FCGI listeners defined below
	 server 127.0.0.1:9000;
	 server 127.0.0.1:9001;
	 server 127.0.0.1:9002;
	 server 127.0.0.1:9003;
   }

   location ~ \.fcgi$ {
			root html;
			fastcgi_keep_conn on;
			fastcgi_pass backend;  # This is where we've configured FCGI to be shipped out to our multiple listener process for load-balancing
			fastcgi_index Default.aspx;
			fastcgi_split_path_info ^(.*cgi)(/.*)$;
			fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
			fastcgi_param PATH_INFO $fastcgi_path_info;
			fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
			include fastcgi_params;
	}

   # REST OF YOUR NGINX CONFIG
}
```

Next, start your vbFastCgi.exe process(es) from the command line (to match the load balancing configuration above you would use the following command line:
_vbfcgihost.exe /spawn 4 /host 127.0.0.1 /port 9000_)

You should see a browser page similar to the screenshot at the bottom of this page.

That's it (other than coding your own responses)! 



*Useful Resources*
_Nginx Site:_ http://nginx.org/

_vbRichClient Site:_ http://www.vbrichclient.com

_FastCGI Spec:_ http://www.fastcgi.com/devkit/doc/fcgi-spec.html



The following list of Gaps in Understanding and Known Issues will be updated as I go.

*Questions/Gaps in Understanding*
The FastCGI protocol mentions that the web server can send SIGTERM to the FCGI server to ask it to close cleanly. Not sure how/if this is done in the Windows Nginx implementation since it handles it's FCGI communications over a TCP pipe and I've never received any message that I can identify as being related to SIGTERM.Just bumped into SCGI as an alternative to FastCGI. Would it be better to use this protocol?How should we handle the mixed "" "/" use in CGI parameters like DOCUMENT_ROOT on Windows? For example: DOCUMENT_ROOT = C:*\*Users*\*Jason*\*Downloads*\*nginx-1.7.9*/*html. Should I just convert all forward slashes to back slashes?*Answered by Olaf in Post #9* What does the CTcpServer.DataArrival  FirstBufferAfterOverflow parameter mean? How should it be handled when TRUE?


*Known Issues*
Not responding to all FCGI Roles. This is most likely a WONTFIX as I only need the RESPONDER role. Not processing all FCGI record types (as of 0.0.5 only FCGI_DATA not supported, and I've never encountered it in the wild. The spec only says "FCGI_DATA is a second stream record type used to send additional data to the application." so I'm not sure when it applies, or what to do with the data in the stream. *FIXED IN 0.0.2 RELEASE* Occasionally getting a "The connection was reset" error. Ngnix reports error: _#5512: *263 upstream sent invalid FastCGI record type: 2 while reading upstream_?



*NOW ON GITHUB - SOURCE CODE IN THIS THREAD WON'T BE UPDATED ANYMORE - GO TO GITHUB INSTEAD!! https://github.com/jpbro/VbFcgi*

*Legacy Source Code* vbFcgiHost.zip

_Version 0.0.8_
Added support for parsing out HTTP query strings to key/value(s) pairs. Keys can have multiple values.Added support for parsing out Cookies into Key/Value pairs.Added htmlEscape function to ensure we're not sending special characters to the browserSome tidy up, added missing variable type declarations on some functions.

_Version 0.0.7_
Significant performance improvement by caching vbRichClient5.cCrypt object for re-use in DataArrival event (and elsewhere).

_Version 0.0.6_
Changed the command line parameters (one fewer required, shorter switches)When in SPAWNER mode, the process will monitor the LISTENER mode process for crashes, and restart crashed processes if necessary.Mutex names are now instance specific based on the EXE path, allowing you to run multiple SPAWNER instances of vbFcgiHost.exe.Additional comments and code cleanup

_Version 0.0.5_
Lots of changes - special thanks to Olaf for directing me to information about nginx load balancing across multiple FCGI listeners. This means we can avoid using threads, and just run multiple vbFcgiHost processes.
Renamed to vbFcgiHost (with a single vbFcgiHost.exe).Now UI-less.
To *start* listeners, use commandline _vbfcgihost.exe /spawncount X /spawnstartport Y_ where X is the number of listener processes you want to create, and Y is the starting port number. For example, _vbfcgihost.exe /spawncount 4 /spawnstartport 9000_ will create 4 listener processes, one for each port from 9000 -9003.
To *stop* listeners, use commandline: _vbfcgihost.exe /stop_Now processing STDIN recordsBetter STDERR handlingAdded new _gc_MaxStdinInMemorySize_ configuration constant (bytes). STDIN streams at or below this size will reside in memory, STDIN streams above this size will be shuffled off to the filesystem.Lots of code cleanup, new comments, and general improvements all around.

_Version 0.0.3_
Added the _gc_MaxResponseLoopSeconds_ configuration variable to allow you to tweak performance for multiple active requests (vs. relying on RC5 Timer granularity for short but many requests, or getting stuck on many but long requests).General cleanup, and some error condition improvementsMore comments!

_Version 0.0.2_
Fixed bad value for FCGI_END_REQUEST constant (should have been 3, was 2)

_Version 0.0.1_
So far we can process BEGIN, PARAMS, and STDIN requests from the web server, and respond with a basic web page listing all the received CGI parameters.We can also handle Unicode transfer to the serve rin UTF-8 encoding.


*Browser Results Screenshot*


*Process Diagram*
_You're only responsible for writing the stuff in BLUE. The startup/monitoring script/service is optional of course - you can just spawn the necessary process from a command line if you prefer Everything else is handled by the VBFCGI framework or third-party libraries/processes._

----------


## jpbro

First bug fix released (bad constant value for FCGI_END_REQUEST).

----------


## dilettante

Nginx web server _already is_ a FastCGI server.  Maybe you can explain where this code of yours fits in.

Is it a second server sitting between a FastCGI program and Nginx web server or something?  Or is this a FastCGI _client_ class used to write VB6 FastCGI programs?

----------


## jpbro

It's entirely possible I've got terminology wrong - I've only just started mucking about with Nginx and FastCGI this weekend. 

Based on my reading of a few different resources (and what I'm getting from Nginx over TCP) I thought I was creating a FastCGI "server", which would parse FCGI records, and pass this on to an FCGI "application", which would pass a response back to my "server", which would parse it into an FCGI response and send that back to the Nginx web server. Looking again, maybe my entire end of things is just an FCGI "application" according to the spec.

In any case, I'll clean up the terminology as it becomes clearer to me, but here's what I'm doing:

Nginx has a _fastcgi_pass_ configuration variable, which can be set to something like 127.0.0.1:9000. I can then bind to my program to 127.0.0.1:9000 and start receiving what appear to be raw FastCGI records like Params, STDIN, over TCP from Nginx.

From there I'm parsing out the raw FastCGI records and building objects based on those records in VB6 (this is what I _thought_ was the FCGI "server" part of my code, but I'm willing to admit it looks like this is the wrong term). From there I can build HTTP responses (this is what I _thought_ was the FCGI _application_ portion, but my terminology may again be wrong.) to send back to my FCGI server, which would then respond to web server in the form of FCGI StdOut, StdErr records. Nginx takes the STDOUT response and delivers that to the browser.

Essentially, my current and possibly inaccurate mental model (or at least bad terminology for this mental model) is this:

Browser > Web server > FCGI Server > FCGI Application > FCGI Server > Web Server > Browser

Which perhaps instead should perhaps be this:

Browser > Web/FCGI Server > FCGI Application > Web/FCGI Server > Browser

Colour Key: Nginx and My Demo Code

----------


## jpbro

So my confusion is based on the following sentences from the FastCGI spec. Interesting terms in *bold*:

_This specification has narrow goal: to specify, from an application perspective, the interface between a FastCGI application and a Web server that supports FastCGI._

AND 

_FastCGI is designed to support long-lived application processes, i.e. application servers. That's a major difference compared with conventional Unix implementations of CGI/1.1, which construct an application process, use it respond to one request, and have it exit._

My initial reading of the above lead me to believe that what I was creating an _application server_ which I guess I shortened to just server or FCGI server, perhaps wrongly in terms of clarity.

Later parts of the FCGI spec seem to drop the word _server_ entirely (unless referencing the web server), and reference the _FastCGI application_, or even just the _application_.

Some searching of other FCGI code out there that is performing the similar task has shown me that they are sometimes even called *FCGI Interfaces* - as a go-between the web server's raw FCGI requests, and something like Perl CGI scripts.

My current thinking is that what I'm working on would properly be called an *FCGI Application Server*, and that this can just be shortened to *FCGI Application*, but my confidence is not terribly high in this regard.

----------


## jpbro

Update Released.

*0.0.3*
Added the gc_MaxResponseLoopSeconds configuration variable to allow you to tweak performance for multiple active requests (vs. relying on RC5 Timer granularity for short but many requests, or getting stuck on many but long requests).General cleanup, and some error condition improvementsMore comments!


*Questions/Gaps in Understanding*
For Olaf, if you are reading: What does the RC5 CTcpServer.DataArrival FirstBufferAfterOverflow parameter mean? How should it be handled when TRUE?

----------


## dilettante

> _FastCGI is designed to support long-lived application processes, i.e. application servers. That's a major difference compared with conventional Unix implementations of CGI/1.1, which construct an application process, use it respond to one request, and have it exit._
> 
> My initial reading of the above lead me to believe that what I was creating an _application server_ which I guess I shortened to just server or FCGI server, perhaps wrongly in terms of clarity.


At a certain point words like server and client can get confusing because they describe roles, and any given bit of running code can have more than one role at the same time.

But it sounds like what you are making is an interface API: code to help a VB6 "application server" communicate with a FastCGI web server.


All of that is less interesting to me than the reasons or possible use cases for ever doing any of this.  Why would anyone want to?  Is this just a matter of showing how it can be done?  Because I don't see any practical use for it today.

If a VB6 application needs to be able to respond to HTTP requests you can just write the code inline or use a 3rd party component.  Such programs can't really use FastCGI, because in that relationship they need to be under the thumb of the FastCGI web server and shouldn't have a visible user interface at all.  Normally such a server starts and stops such programs as needed.

I can live with "just because" as a reason, but as I said I can't imagine anyone using it as part of a practical solution to anything.

----------


## pekko

> I can live with "just because" as a reason, but as I said I can't imagine anyone using it as part of a practical solution to anything.


I can see many reasons other than "just because".

One example. You have www-server on Amazon and some legacy VB6 apps around world. Those legacy apps talks to external devices, HVAC, Pumps, Vending Machines, etc.
With VB6/FastCGI you can easily build one www-interface to you devices, everything in one url http://MyLegacyDevices

Sometimes rs232/rs485 is only interface for older devices. Of course there is other solutions to connect older devices to internet but usually they are not cheap.

----------


## Schmidt

> For Olaf, if you are reading: What does the RC5 CTcpServer.DataArrival FirstBufferAfterOverflow parameter mean? How should it be handled when TRUE?


The cTcpServer (Listener-) Class can be started with a "BufSize-Parameter", which one can
adjust (with a good reserve, depending on the expected "worst-case-timing-behaviour").

E.g. when you design your own simple client-protocol as:
[4Bytes which represent a Long-Value, describing the byte-length of the following Blob-Packet]
[Blob-Bytes the Client sends, according to the preceding, simple 4Byte-Header]

And the Client then sends (very fast):
[4byte-header][Blob][4byte-header][Blob][4byte-header][Blob]...

...to the Server (which does automatic buffering) - then you can only rely on that simplified
protocol to work "properly aligned" (inside your Servers DataArrival-Event), when you stay
"in sync" with the 4Byte-Headers, never missing a single Byte in the Stream, from the time
the connection was accepted at the Server-end.

The automatic Buffering will help with the "never missing a single Byte"-part - 
but in the (potential) case it runs out of space (because you didn't dequeue fast enough
within DataArrival), then there *will* be Bytes missing, since in that case (incoming bytes 
exceed the yet available queue-space) the connection in question will automatically clear
the Buffer-queue completely (now having enough space again, to buffer the next
incoming bytes, and so the buffering continues ... - but you will need an information 
about that "hickup" -> with FirstBufferAfterOverflow = True...

This way you will know you're out of sync (within the Servers DataArrival-Event) and can
undertake "special efforts" (which you normally avoid for performance-reasons), to find
the right "syncing-point" again (the beginning of a new 4Byte-Header "by other means", 
which performance-wise can be costly - and is normally not necessary)...

So, that's it in a nutshell.
Easiest way to ensure "resyncing" in such a case (using such a simple, but fast performing protocol),
is to simply disconnect that client (which will get noticed at the clientside in the appropriate Disconnect-
Event - and thus the Client can react to that relative fast, by simply performing a re-connect - 
resulting in a new assigned "accepting-socket" on the end of the cTCPServer-Instance - including
a fresh Buffer-Queue - and a properly "aligned" new protocol-handling on this new connection...


Olaf
.

----------


## pekko

Quick question to Olaf, will there be websocket support in your cWebServer.

----------


## Schmidt

> Quick question to Olaf, will there be websocket support in your cWebServer.


Not planned anytime soon currently...

Other than the cRPCListener-Class, the cWebServer is not working multithreaded so far,
mainly thought as a "simple to fire-up" Process-internal WebServer, to ensure App-
internal Browser-Interaction - or to allow for simple "App-Remote-Scenarios" (e.g. when
you want to press a few App-Buttons on certain Forms remotely over your Phone or Tablet - 
stuff like that).

Feel free to submit a CodeBank-entry in case you want to implement it in the appropriate
Request-Handler-Event of cWebServer - and in case you did some nice "Ground-Work", 
I'd be willing to take part in the efforts to "make it more stable" or so - but I don't
have the time currently to start something like that on my own, "for the fun of it"...

Olaf

----------


## jpbro

Update Released.

*Version 0.0.5*
Lots of changes - special thanks to Olaf for directing me to information about nginx load balancing across multiple FCGI listeners. This means we can avoid using threads, and just run multiple vbFcgiHost processes.

Renamed to vbFcgiHost (with a single vbFcgiHost.exe).Now UI-less.

    To start listeners, use commandline _vbfcgihost.exe /spawncount X /spawnstartport Y_ where X is the number of listener processes you want to create, and Y is the starting port number. For example, _vbfcgihost.exe /spawncount 4 /spawnstartport 9000_ will create 4 listener processes, one for each port from 9000 -9003.

    To stop listeners, use commandline: _vbfcgihost.exe /stop_Now processing STDIN recordsBetter STDERR handlingAdded new gc_MaxStdinInMemorySize configuration constant (bytes). STDIN streams at or below this size will reside in memory, STDIN streams above this size will be shuffled off to the filesystem.Lots of code cleanup, new comments, and general improvements all around.

----------


## jpbro

Update Released.

*Version 0.0.6*
Changed the command line parameters (one fewer, shorter switch names)When in SPAWNER mode, the process will monitor the processes in LISTENER mode for crashes, and restart crashed processes as necessary.Mutex names are now instance specific based on the EXE path, allowing you to run multiple SPAWNER instances of vbFcgiHost.exe

Regarding the command line changes, starting in SPAWNER mode now uses the following switches:

*/spawn X*
Spawn X processes in LISTENER mode.

*/host X*
Bind to adapter on IP address X

*/port X*
Start binding on port X, +1 for each additionally spawned listener process

*/stop*
Stops all running vbFcgiHost processes started from the same path.

----------


## jpbro

Update Released.

*Version 0.0.7*
Significant performance improvement by caching vbRichClient5.cCrypt object for re-use in DataArrival event (and elsewhere).

----------


## jpbro

Update Released.

*Version 0.0.8*

I recommend you get the latest vbRichClient5 for use with this version, though I don't think it is mandatory.

_Changes:_

Added support for parsing out HTTP query strings to key/value(s) pairs. Keys can have multiple values.Added support for parsing out Cookies into Key/Value pairs.Added htmlEscape function to ensure we're not sending special characters to the browserSome tidy up, added missing variable type declarations on some functions.

----------


## jpbro

Hate to dig up an old thread of mine that generated little interest, but I've moved along quite a bit with this project over the past 8 months or so (including some exciting feedback from users), and I'm curious if anyone is interested in a WEB<>VB6 code bridge over Nginx/FCGI or not. It's a nice way to bridge your legacy VB6 code with the modern device world such as tablets/smartphones (of course, you have to do all the HTML, JS, CSS yourself, but Nginx does all of the hard work, and my FCGI classes do all the bridging).

While I started with a vanilla VB6/vbRichClient5 solution (provided above), I now ask because I've since moved to use more of my own private + third party libraries more often. I'm afraid I'll pass the point of no return soon (if I haven't already) where it will take too much of my free time (i.e. more than I have available) to decouple the freely distributable vs. non-freely distributable code so as to still be of use to the remaining VB6 community. 

So I guess this is a "speak now or forever hold your peace" kind of moment  :Smilie:

----------


## jg.sa

G'Day JPBro

It is really interesting what you have done with this code.

I recently returned to a VB6 app. & I'm looking at now creating my 1st CGI VB6 'app.'  :Thumb: 




> So I guess this is a "speak now or forever hold your peace" kind of moment



So I'm hoping I'm not tooooooooo late !!!!

----------


## jpbro

Hi jg.sa! Not necessarily too late - my personal version of the FCGI application server has evolved quite a bit since my last post here on the public version, and like I said I'm now using some closed source pay-to-license libraries for parts, so I will have to spend some time re-implemented stuff using freely available libraries and code. I'm thinking it might be worthwhile to put it up on GitHub, so I'll try to find the time to do this soon (I'll post back here when it is available).

----------


## jpbro

> All of that is less interesting to me than the reasons or possible use cases for ever doing any of this.  Why would anyone want to?  Is this just a matter of showing how it can be done?  Because I don't see any practical use for it today.


Almost 2 years for a reply from me, how's that for responsiveness  :Wink: 

For me there are 2 primary reasons for the existence of this project:
 To use as much of my existing VB6 code (developed over years) for an network enabled application based on vbRichClient5 RPC classes, and be able to get data from that application to the browser with a minimum amount of effort, duplication of effort programming-wise. To take advantage of Nginx's HTTPS, load balancing, and proxying features. 

For the first point, I already have a client-server system programmed and working well in VB6, but the client portion is naturally Windows only. Users are demanding more access via tablets & smart phones which I want to provide. Being able to use all of my existing backend code, and just add some interfacing code to generate HTML was very appealing. The beta web app took less than a week to develop, because all of the heavy lifting was already done on the backend for the full featured native client application.

The second point should be self-evident - there's nearly infinitely less effort in using Nginx then trying to roll my own HTTPS server with load-balancing and proxying features. My users' data can be sensitive, so HTTPS was required - even the experts sometimes muck up when dealing with HTTPS implementations, so that was a can of worms I did not want to open myself.

Why Nginx specifically? It can be distributed easily under 2-cluase BSD license without requiring a second installer/purchasing additional licenses. That "no second installer" part is also one of the same reasons I chose SQLite via vbRichClient5.  My users appreciate the simple and straightforward installation that my application provides as not all of them have IT departments at their behest.




> I can live with "just because" as a reason, but as I said I can't imagine anyone using it as part of a practical solution to anything.


So definitely not "just because" - I'm using FCGI application server code as part of a practical solution today (and for almost 2 years now). It has performed well in real-world use, and saved me a tonne of time getting my existing Windows-only application onto the web (albeit in a pared-down form right now - not all of the features of the native app are available on the web version).

This configuration ensures that my users can access their data from any device, anywhere - laptop, desktop, smartphone, tablet - on the LAN or out in the field, and that they can do so with minimal configuration or fuss on their end. 

For those who prefer images, here's a basic look at it (just whipped this up now, so there may be errors, but it should get the message across):

----------


## jg.sa

G'Day JPBro

In one word W O W  :Smilie: 

What you have done is amazing  :EEK!: 

I have approx. 10 desktop VB6 apps. I have been using personally as well as having sold/rented them in the past, but it was just a hobby not even paying for the costs of servers ( I host my own ) & the static IPs ( I have 9 ) and the software licenses etc., all of them are what I call Web 0 ( Client / Server ), this was mainly due to the need for them not to go Jurassic park on me & I wanted really good security around what emails were being sent, how many were being sent and to who they were being sent !!!

One app. allows children to send email, but not receive, this same app can also be used to allow staff to send email as the boss, but not receive & this app could have been easily used to send spam. Think of a retail situation & the staff can order from suppliers of bread with the bosses email, but the staff cannot read the incoming emails - like bank statements in Outlook.

In the past I was going to make an effort to setup a dealer network to manage the apps. as you need advanced knowledge of SMTP to setup and manage these apps. Basically I'm using a loop hole in the way ISPs setup their SMTP servers in OZ & NZ, dont know if this is the same in the rest of the world, but I have always suspected it is.

As an old IBM programmer of Assemble, Fortran, Cobol, C, Ada ... I have setup a web sites using IBM Domino, but I always wanted to be 'free' of big blue, I have got NGinx front ending Domino HTTP & I have built a 'Proof of Concept' web site for dealers to register and it allows them to manage (Add, Delete, Modify) the To email addresses/contacts as the app. has no way of permitting the To list to be managed locally, this means I always know exactly who a user can send To & hopefully control the possible spammer - notice I didn't say 'stop'.

All of my customers are using PCs, but I always wanted a design that would allow for mobile, because I have a customers who has a locksmith business and they wanted to be able to send from tablets in the field & I got them to buy cheap Win X tablets instead.

So it is really good timing for me to have found this post.

Can you flick me a link so I can 'Sign up' ???

As I said I have built a VB6 CGI app. & I'm not certain it is 'appropriate' code as I live in rural Oz in a town of approx. 2000ppl I dont know anyone else who can code let alone Web 0 I have always wondered if I should have found a framework to see how the 'professionals' are doing VB6 CGI, before I start down the path of creating my own.

I used info from pages like

http://www.aivosto.com/visdev/cgi.html
http://www.angelfire.com/mi4/bvo/vb/vbgb.htm

So my CGI is a 'mashup' of what I have read & scrounged.

Can you point me to a page that provides a really good VB6 CGI framework ?

TIA

----------


## jpbro

Thanks for the kind words jg.sa  :Blush:  and thanks for your interest in the project!

I've just migrated everything to GitHub here: https://github.com/jpbro/VbFcgi

I've also made some improvements to the code to make it easier to develop your own FCGI applications as separate DLLs (almost acting as a plugin to the VbFcgiHost.exe that is included and will hopefully remain fairly static/unchanged except for bug fixes).

The biggest barrier getting existing VB6 stuff onto the web will be if you haven't coded the "server" side of your current client-server applications to be UI-less, as any UI will just get in the way here. If you have a server-side DLLs with public methods that you can call that return things like strings, recordsets, etc... then you can reference those DLLs in the VbFcgiApp.dll and code against them in the IFcgiApp_BuildResponse method. That's where you'll build your HTML responses (or JSON or whatever you want to respond to the browser with) and then send it back over using the methods of the CFcgiUpstream object that gets sent with every IFcgiApp_BuildResponse call.

I'm sure that sounds really confusing, so if you're like me you'll want to take a look at some demo code. I've included a small FCGI demo application in the Git repository for study, so I recommend giving that a look. Feel free to get back to me here (or on GitHub) if you have any questions.

Enjoy!

Re: A good VB6 CGI framework - Sorry I don't know of any. I've firmly planted myself in my VB6 FCGI framework at this point!

----------


## jg.sa

G'Day JPBro




> Thanks for the kind words jg.sa  and thanks for your interest in the project!


Stop blushing  :Smilie: 




> I've just migrated everything to GitHub here: https://github.com/jpbro/VbFcgi


Thanks for doing that, I will DL ASAP




> I've also made some improvements to the code to make it easier to develop your own FCGI applications as separate DLLs (almost acting as a plugin to the VbFcgiHost.exe that is included and will hopefully remain fairly static/unchanged except for bug fixes).


Ok, that is good for me, I cant believe that you would have any 'Bugs'  :Smilie: 




> The biggest barrier getting existing VB6 stuff onto the web will be if you haven't coded the "server" side of your current client-server applications to be UI-less, as any UI will just get in the way here. If you have a server-side DLLs with public methods that you can call that return things like strings, recordsets, etc... then you can reference those DLLs in the VbFcgiApp.dll and code against them in the IFcgiApp_BuildResponse method. That's where you'll build your HTML responses (or JSON or whatever you want to respond to the browser with) and then send it back over using the methods of the CFcgiUpstream object that gets sent with every IFcgiApp_BuildResponse call.


I have built a few server 'addins' for IBM systems so I am very familiar with the concepts, the first thing I add to any of the CGI code I DL is a debug text file.

But, FCGI is all new to me !!!




> I'm sure that sounds really confusing, so if you're like me you'll want to take a look at some demo code. I've included a small FCGI demo application in the Git repository for study, so I recommend giving that a look. Feel free to get back to me here (or on GitHub) if you have any questions.


No you are not




> Enjoy!


I will, I'm really looking forward to using VB6 text manipulation facilities to generate HTML




> Re: A good VB6 CGI framework - Sorry I don't know of any. I've firmly planted myself in my VB6 FCGI framework at this point!


I didnt think any were lurking that I hadnt found, I have been searching for a few months off and on now !!!

I guess my biggest question is, why the code I have DLed does not have any JS for client side validation or using Angular or jQuery or ... FWs 

The code I'm thinking about will use a FW & do lots of good stuff like validation etc.

I really wanted to understand how to use what I would call server side 'includes', I have many sites that use code like

< script type="text/javascript" src="js-inc/head.js"> < /script>
.
.
.
< script type="text/javascript" src="js-inc/iframeinclude.js"> < /script>

I started using this in the late '90s & I know it is not considered the best, but if you dont have a server like PHP or ASP etc. then this is the easiest

BTW - I checked out your web site - statslog which is so good I might 'borrow' a copy  :Smilie: 

I really like the site & the app concept, but I couldnt find any CGI except some code to serialise (Oz spelling, not US) the form.

'/cgi-bin/trial.pl', 
	serializedformdata,

Can you flick me a link to a web site that has your CGI in it ?

TIA

Grasshopper

----------


## jpbro

One thing I didn't mention previously in this thread is that the FCGI code (and the vbRichClient5 library that it uses for parts) works well on Linux under Wine (Windows compatibility layer https://www.winehq.org/).

This has a few  benefits:

 There are a lot of reasonably priced VPS providers out there where you can install a Linux image and then install your FCGI app to make it available on the web. They are typically cheaper than the Windows alternatives. Because Wine runs as separately per-user, it's almost like having multiple versions of Windows on a single computer. This allows you to offer different tiers of service with different pricing. For example, a single user might jump on a shared VPS for a lower price, while a small to mid-size company might want their own unshared VPS for a higher price. Larger enterprises might want their own co-located server, or run it all in-house  (they may still insist on Windows but at least all options are available). Wine is not like Windows 10 (always on automatic updates). You can more or less freeze yourself at a point in time/particular feature-set on the server-side without having to worry about one of Microsoft's half-baked updates ruining your day. As long as you keep your hosting Linux OS up to date for security patches, and lock it down in terms of unnecessary open ports, install software like fail2ban, logwatch, etc.. you should be reasonably safe.

Just wanted to make it known that Linux is a viable possibility here, and that you're not 100% locked into MS if you use this.

----------


## jg.sa

Hello Admins.

I have tried to post here 3 x times today & I'm having issues with

- Auto save saying it cant
- Post that just dont
- Having to login over & over again

Whats happened to my posts ???

----------


## jpbro

> G'Day JPBro
> Stop blushing


Done  :Smilie: 





> Thanks for doing that, I will DL ASAP


Very welcome! I hope this project gets some traction this time around.






> Ok, that is good for me, I cant believe that you would have any 'Bugs'


Well prepare yourself to be disappointed  :Wink:   Joking aside, there are almost definitely some bugs that will pop up, but I'll do my best to take care of them in a timely manner.






> I have built a few server 'addins' for IBM systems so I am very familiar with the concepts, the first thing I add to any of the CGI code I DL is a debug text file.
> 
> But, FCGI is all new to me !!!


Good to know you're coming in to it with a solid technical base. FCGI is relatively new to me too, so we'll do some learning together.


I didnt think any were lurking that I hadnt found, I have been searching for a few months off and on now !!!




> I guess my biggest question is, why the code I have DLed does not have any JS for client side validation or using Angular or jQuery or ... FWs 
> 
> The code I'm thinking about will use a FW & do lots of good stuff like validation etc.


JS, Jquery, Angular, or any other browser-side frameworks/libraries can be added when you build your HTML on the VB6/server-side. Alternately, you can have all of the JS files and web-pages as static resources on your web-server, and your VB6 FCGI app could just process AJAX queries and return JSON strings, text strings, etc... for dynamic parts. Part of the job upfront will be sitting down and figuring out what parts of the larger system (web server, FCGI application, browser) will be best served doing what.

I really wanted to understand how to use what I would call server side 'includes', I have many sites that use code like






> I really like the site & the app concept, but I couldnt find any CGI except some code to serialise (Oz spelling, not US) the form...Can you flick me a link to a web site that has your CGI in it ?


The demo FCGI app on GitHub is very basic and lean, and doesn't do any query parameter parsing or responding yet. It just builds a simple HTML page with the FCGI parameters it received and spits it back upstream. I will be adding soon the query parameter parsing stuff soon - it is already in my private version of the FCGI application server, but I used some paid third-party libraries to make my life a bit easier, so I'll have to work on some open-source replacements which might take a bit of time.

----------


## jpbro

Big update over at GitHub.

It's important to note that binary compatibility has been broken. I don't think anyone is using this yet, but if you are then you should unregister the existing VbFcgiLib.ll and VbFcgiApp.dll before installing the latest versions an re-registering. Note that the IFcgiApp interface has changed, so if you've done any custom code you will have to modify it to support the new ProcessRequest() method (replaces the old BuildResponse method)

I've added a number of new classes:

CHttp, CHttpCookies, and CHttpQueryParams
   The CHttp object will be passed as part of the CFcgiRequest object to downstream FCGI apps. It has properties for CHttpCookies (for getting and setting cookies), and CHttpQueryParams (for getting query string parameters as sent from the upstream web browser).

Also, the FCGI host can now handle multiple FCGI apps with different names. Here's how it works:

When a URL is passed from the browser such as http://localhost/myapp.fcgi, the FCGI host will take the "myapp.fcgi" and look for that file in the same folder. It will then attempt to create an CFcgiApp class from that file. Note that .fcgi files are just regular VB6 Active DLL files with the extension changed. 

When you want to make your own FCGI app, simply start a new ActiveX DLL project, reference the VbFcgiLib.dll, and change the default Class1 class name to CFcgiApp. 

In your CFcgiApp class, add "Implements VbFcgiLib.IFcgiApp" to the General section, and then add "Private Sub IFcgiApp_ProcessRequest(po_Request As VbFcgiLib.CFcgiRequest, po_Response As VbFcgiLib.CFcgiResponse)". The IFcgiApp_ProcessRequest sub is where you do all of your response building.

After compiling your DLL, make a copy in the same folder as VbFcgiLib.dll and VbFcgiHost.exe, and then rename the extension from .dll to .fcgi. That should be enough to make it accessible from the browser (assuming a properly configured web server and running VbFcgiHost.exe).

I still have to add a bunch of comments,  documentation, and update the demo to show how things like cookies and query parameter processing work, but I hope that will come soon.

----------


## dreammanor

Very nice. It's a great thread.

----------


## jpbro

Thanks @dreammanor!

Another update at GitHub - I've updated the demo application to show how Cookies and Query Parameters can be used to generate dynamic page content.

----------


## jpbro

Some more updates at GitHub including some important bug fixes (told you there were bugs jg.sa!  :Wink:  ). 

Biggest issue I have right now is that Cookies only seem to work with Firefox (Chrome, Edge, IE all fail). I've looked at the Network inspector in Chrome and it seems to be receiving cookies and sending them back on subsequent requests, but for some reason they aren't getting from Nginx to my FCGI app server. I'd appreciate any insights any of you might have on this!

----------


## dreammanor

I have to wait until my current project is completed before I can begin to learn and use your excellent code. I found a message that I don't know whether it's useful to you:

https://serverfault.com/questions/61...-reverse-proxy

----------


## jpbro

Hi dreammanor, thanks for that link it was useful but unfortunately it didn't help. I tried editing my hosts file to get a domain to point to 127.0.0.1 and then I used that domain for my cookie, but I'm still not getting back from the webserver.

I've done a bit more testing and I think there might be a bug in my FCGI parameter parsing (an off by one error or perhaps a miscalculation of the size of the payload from the webserver). What I've noticed by looking at the inspector is that Chrome is sending the Cookie header as the last entry of the request, but Firefox has it somewhere in the middle. When I copy and paste the request header from Chrome into the Firefox the developer tools window and resend, then Firefox exhibits the same problem as Chrome. 

I'll do some more testing and figure out where I'm going wrong, but thanks again for doing some research!

----------


## jpbro

Found the source of the problem - I was miscalculating when I should exit the loop that deserializes FCGI parameters by 1 record (8 bytes). I'll have an update posted to GitHub soon.

----------


## jpbro

Update has been posted to GitHub to fix the bad loop-end calculation for FCGI_PARAMS deserialization.

----------


## jpbro

New update over at GitHub. Biggest new feature is an HTML builder/helper class that makes it easier to build HTML responses. For example:



```
Public Sub TestBuilderHtml()
   Dim x As New CBuilderHtml
   Dim l_TagIndex As Long
   
   x.AppendDocType htmldoctype_Html5
   x.OpenTags "html"
   l_TagIndex = x.OpenTags("head")
   x.AppendWithTag "Test page", "title"
   x.CloseOpenedTagsToIndex l_TagIndex
   x.Append vbNewLine
   
   x.OpenTags "body"
   l_TagIndex = x.OpenTags("table", "tr")
   x.AppendWithTag "This is a test & stuff.", "td"
   x.CloseLastOpenedTag
   x.OpenTags "tr"
   x.AppendWithTag "This is a test2.", "td"
   x.CloseOpenedTagsToIndex l_TagIndex
   
   x.OpenHyperlinkTag "http://www.github.com"
   x.Append "Visit GitHub"
   x.CloseAllOpenedTags ' Optional, calling Finished will also take care of this.
   
   x.Finished contentencoding_UTF16_LE
   
   Debug.Print x.Content
End Sub
```

Produces the following HTML:



```
<!DOCTYPE html>
<html><head><title>Test page</title></head>
<body><table><tr><td>This is a test &amp; stuff.</td></tr><tr><td>This is a test2.</td></tr></table><a href='https://www.github.com' >Visit GitHub</a></body></html>
```

Enjoy!

----------


## jpbro

PS: @dreammanor - your link re: cookies with IP addresses ended up being good knowledge for MS Edge. It doesn't accept cookies with an IP domain. Looks like Chrome does accept them now (as does IE).

----------


## jpbro

Just a test...keep getting a SOMETHING WENT WRONG page on my larger post  :Frown:

----------


## jpbro

New updates over at GitHub here: https://github.com/jpbro/VbFcgi

NOTE: This build breaks binary compatibility. I hope that is the last time (remember though, you don't need to register anything on your target machines, just your development machine. Please unregister anything you previously registered on your dev machine before registering the latest binaries).

New features: It is now easier to build HTML using the updated HTML builder/helper (you no longer have to write bytes upstream yourself, just call the Finish method when you are done building your HTML and it will be passed upstream for you).

Also added a new HTTP header builder/helper class. It will automatically include things like Content-Type, Content-Encoding, Content-Length (though you can over ride) based on what is in the active IBuilder object. You can also add custom header fields like Cookies, etc... This is all demonstrated in the vbFcgiApp demo, but I'm happy to answer any questions here about how to use it.

----------


## jpbro

Here's some sample code:

[code]
Private Sub IFcgiApp_ProcessRequest(po_Request As VbFcgiLib.CFcgiRequest, po_Response As VbFcgiLib.CFcgiResponse)
   Dim l_StartedBuildAt As Double
   Dim ii As Long

   Dim lo_Header As vbRichClient5.cStringBuilder
   Dim lo_IBuilder As VbFcgiLib.IBuilder
   Dim lo_Html As VbFcgiLib.CBuilderHtml
   Dim l_Title As String
   Dim l_SubTitle As String
   Dim l_SubTitleExample As String
   Dim l_SubTitleExampleUrl As String
   Dim l_VisitCount As Long
   Dim l_HttpHost As String
   Dim l_TagIndex As Long

   On Error GoTo ErrorHandler

   ' Make sure that FCGI parameters are complete built and the Upstream FCGI object has been set
   ' otherwise raise fcgierr_NotReadyForResponse
   ' Just a sanity check - this should never happen
   If po_Request.Fcgi.Params.State <> paramstate_Built Then Err.Raise fcgierr_NotReadyForResponse, , "FCGI Parameters incomplete."

   l_StartedBuildAt = libRc5Factory.C.HPTimer

   Set mo_FcgiParams = po_Request.Fcgi.Params
   Set mo_FcgiStdin = po_Request.Fcgi.Stdin

   l_HttpHost = po_Request.Fcgi.Params.ValueByEnum(stdparam_HttpHost)

   ' Build the HTML portion of the HTTP response

   ' Initialize the HTML builder/helper
   Set lo_Html = po_Response.Builders.Builder(builder_Html)

   With lo_Html
      .AppendDocType htmldoctype_Html5

      .OpenTags "html"

      l_TagIndex = .OpenTags("head")

      ' *** START DEMONSTRATION OF QUERY PARAMETER HANDLING
      If po_Request.Http.QueryParameters.Exists("title") Then
         l_Title = lo_Html.EncodeEntities(po_Request.Http.QueryParameters("title"))
      End If
      ' *** END DEMONSTRATION OF QUERY PARAMETER HANDLING
      If stringIsEmptyOrWhitespaceOnly(l_Title) Then
         l_Title = "vbFcgi Demo App"
         l_SubTitle = "Pass a ""title"" query to change the title of this page."
         l_SubTitleExampleUrl = "http://" & l_HttpHost & po_Request.Fcgi.Params.ValueByEnum(stdparam_ScriptName) & "?title=Greetings from planet earth!"
      End If

      .AppendWithTag l_Title, "title"

      .CloseOpenedTagsToIndex l_TagIndex  ' Close <head> tag

      ' Build BODY
      .OpenTags "body"

      .AppendWithTag l_Title, "h1"

      If Not stringIsEmptyOrWhitespaceOnly(l_SubTitle) Then
         .AppendWithTag l_SubTitle, "h2"

         l_TagIndex = .OpenTags("p")
         .Append "Example: "
         .OpenHyperlinkTag l_SubTitleExampleUrl
         .Append .EncodeEntities(l_SubTitleExampleUrl)
         .CloseOpenedTagsToIndex l_TagIndex ' Close up to H3 tag
      End If

      l_TagIndex = .OpenTags("p", "b")
      .Append "<a href='https://www.github.com/jpbro/VbFcgi'>Learn more about VbFcgi on GitHub.</a>"
      .CloseOpenedTagsToIndex l_TagIndex  ' Close B and P tags

      ' *** START DEMONSTRATION OF COOKIES
      l_TagIndex = .OpenTagWithAttributes("p", , , "color: orange; font-weight: bold;")
      If po_Request.Http.Cookies.Exists("visits") Then
         On Error Resume Next
         l_VisitCount = po_Request.Http.Cookies.CookieByKey("visits").Value
         On Error GoTo ErrorHandler

         If l_VisitCount = 0 Then
            ' Bad cookie value!
            .Append "Hey! Have you been mucking about with your cookies?"
         Else
            ' Display number of visits
            .Append4 "You have previously visited this page ", l_VisitCount, " time", IIf(l_VisitCount <> 1, "s.", ".")
         End If
      Else
         ' First visit
         .Append "This is your first visit, pleased to meet you!"
      End If
      .CloseOpenedTagsToIndex l_TagIndex  ' Close P tag

      ' Increment "Visits" cookie
      po_Request.Http.Cookies.AddOrReplaceCookie "visits", l_VisitCount + 1
      ' *** END DEMONSTRATION OF COOKIES

      .Append4 "<p>", "The current date & time on the server is: ", Now, "</p>"
      .AppendWithTag "VbFcgi is " & ChrW$(&HAAA&) & ChrW$(&HE01&) & ChrW$(&H671&) & ChrW$(&H188&) & ChrW$(&H47B&) & ChrW$(&H257&) & ChrW$(&HFEC9&) & " capable via UTF-8!", "p"

      ' Build FCGI Parameters table
      .AppendWithTag "FCGI Parameters received from Upstream Webserver:", "h2"
      .OpenTags "table"
      For ii = 1 To mo_FcgiParams.Count - 1
         .OpenTags "tr"
         .AppendWithTag mo_FcgiParams.KeyByIndex(ii), "td"
         .AppendWithTag mo_FcgiParams.ValueByIndex(ii), "td"
         .CloseLastOpenedTag  ' Close tr tag
      Next ii
      .CloseLastOpenedTag  ' Close table tag

      ' Build response time

----------


## dreammanor

Thank you, jpbro. The attachment is invalid, it may be deleted by the moderator.

----------


## jpbro

Hi dreammanor, thanks for pointing that out...I keep getting errors from the forum when I try to attach an image. I can see the image now, but maybe it will disappear again (hope not!).

----------


## jpbro

Ugg, not sure what's wrong with the forum but it keeps taking me to a "Something went wrong page" and now it is chopping off the bottom of my code sample, removing the [/code] tag.

Tried in Firefox and Chrome, so must be something server-side.

----------


## dreammanor

> Hi dreammanor, thanks for pointing that out...I keep getting errors from the forum when I try to attach an image. I can see the image now, but maybe it will disappear again (hope not!).


Hi jpbro, now I can see the image, thank you.

----------


## jpbro

Just added a new feature at the GitHub repository, a CBuilderFile class that helps you easily send file data downstream to the browser from your FCGI applications.

It automatically builds HTTP header fields for Content-Length, Content-Type (based on a partial list of MIME type to File Extension mappings) etc...
It can also detect if the browser's cached copy matches the server-side copy (based on ETag/If-None-Match values) and send back HTTP 304 Not Modified instead of the entire file data.

Please see the included VbFcgiApp demo for an example of its usage.

Right now the Etag is being calculated by combining the file path, size, and last modified date and then hashing that data. This isn't perfect, but it should work OK until I can hash actual file data.

CAUTION BINARY COMPATIBILITY BROKEN AGAIN.

----------


## jpbro

CAUTION - BINARY COMPATIBILITY HAS BEEN BROKEN.

Big new update to VbFcgiLib here: https://github.com/jpbro/VbFcgi

The biggest new addition is support for .VBML files. 

*What are VBML files?* They are text files (typically HTML documents) that site in your web server's document root folder and include special tags. When VbFcgiLib encounters these tags, it raises a FoundTag event in your FCGI application class. You can then perform any sort of dynamic processing that you'd like and pass a replacement value back. VbFcgiLib will then replace the tag with your dynamic content. The best part is that you can easily edit VBML files in your favourite text editor and changes will be reflected live without needing to rebuild your FCGI application or restart your web server.

For example, this VBML file:



```
<!DOCTYPE html>
	<head>
		<title>[[TITLE]]</title>
		<style>
			td 
			{ 
				padding: 10px;
				border: 1px solid #d0d0d0;
			}
			.right 
			{ 
				text-align: right; 
			}
		</style>
		
	</head>

	<body>
		<h1>This is a table of random numbers generated dynamically in a VB6 FCGI application:</h1>
		
		[[TABLE_001]]
		
		
		<p>[[SAMPLE UNKNOWN TAG]]</p>
	</body>
</html>
```

This document has 3 tags: [[TITLE]], [[TABLE_001]], and [[SAMPLE_UNKNOWN_TAG]].

Your FCGI application will receive 3 FoundTag events that pass TITLE, TABLE_001, and SAMPLE_UNKNOWN_TAG as tag values. You can use the passed CWebStringTemplate class to build plain text or HTML text. Once built, your dynamic text will replace the tag text. Once all tags have been processed, all the content will be sent to the browser.

So some VB code like this:



```
Private Sub mo_Vbml_FoundTag(ByVal p_Tag As String, po_Replacement As VbFcgiLib.CWebStringTemplate, ByRef p_DoNotReplace As Boolean)
   Dim ii As Long
   Dim l_OpenTagIndex As Long
            
   Select Case UCase$(p_Tag)
   Case "TITLE"
      ' Generate a dynamic title
      po_Replacement = "Welcome on " & Format$(Now, "MMMM D, YYYY at H:NN ampm")
   
   Case "TABLE_001"
      ' Generate a random table of data, and tell the VBML template parser not to encode HTML entities on the assumption we've done it ourselves
      
      With po_Replacement
         .SkipEncodeEntities = True ' Turn off automatic entity encoding since we are building HTML
         
         .OpenTags "table"
         For ii = 0 To 5
            l_OpenTagIndex = .OpenTags("tr")
            
            .AppendWithTag Chr$(65 + ii), "td"
            .AppendWithTagAndAttributes Format$(Rnd * 100, "0.000"), "td", "right"
            .AppendWithTagAndAttributes Format$(Rnd * 100, "0.000"), "td", "right"
            .AppendWithTagAndAttributes Format$(Rnd * 100, "0.000"), "td", "right"
            
            .CloseOpenedTagsToIndex l_OpenTagIndex  ' Close the <tr> tag
            
            .Append vbNewLine
         Next ii
         
         .CloseAllOpenedTags
      End With
     
      
   Case Else
      ' Unknown tag
      apiOutputDebugString "Encountered unknown tag: " & p_Tag
      
      po_Replacement.SkipEncodeEntities = True ' Turn off automatic entity encoding since we are building HTML
      po_Replacement = "<b style='color: red'>UNKNOWN TAG WARNING:</b> <i>&laquo;" & p_Tag & "&raquo;</i>"
      
   End Select
End Sub
```

Will be turned into this:



Enjoy  :Smilie:

----------


## jpbro

I've been adding a lot of PURPOSE and USAGE comments to the various VbFcgiLib classes that I hope will help introduce you to the library, so please give them a read over at GitHub. 

Of course, I'm more than happy to answer questions here should there be any.

----------


## gibra

Thank you very much, Jason. 
I'm curious to try it, so during the holidays I hope to find the time to do some tests.

----------


## jpbro

Hi gibra, it will be nice to get some feedback. I look forward to your comments and questions once you've found a chance to it it a try! Hope you enjoy it  :Smilie:

----------


## jpbro

I've added a JSON builder/helper class and updated the demo app to include some new demonstrations (JSON and VBML templates).

Binary compatibility was broken again.

----------


## jpbro

I've updated the demo application to better illustrate how you can build modern looking & performing websites using VBFCGI/VB6 backend, with VBML templates along with technologies like Jquery, Bootstrap, JSON & AJAX.

Result looks like this:

----------


## jpbro

Thought I pose this as a question in case anyone out there had an insights/preferences.

Consider the following URL:

http://localhost/myapp.fcgi?test

Where test is the query string.

In the current VbFcgi implementation, test is treated as a default parameter value. Calling CHttpQueryParams.DefaultValue returns test, and calling CHttpQueryParams.ValuesByKey("") returns a CHttpQueryParamValues object with a single value of test.

I'm thinking it might be better to treat test as a "flag" and use it as a key instead of a value. So calling CHttpQueryParams.Exists("test") would return True and calling CHttpQueryParams.ValuesByKey("test") would return a CHttpQueryParamValues object with zero values.

Perhaps the best of both worlds would be an option property to let users decide which approach to take based on the needs of their FCGI applications?

Thoughts anyone?

----------


## dreammanor

My audio-subtitle editing tool will be completed soon, I'm going to start learning and testing your framework next week.

----------


## jpbro

Sorry for spamming everyone subscribed to this thread - there's nothing new in the framework, but I tried to send a private message to jg.sa regarding our conversations here since they spawned the idea to open-source the VBFCGI code. I haven't heard from them since, and unfortunately their private mailbox was full.

If jg.sa is still reading: Have you had a chance to try out the FCGI framework?  If so, how are things working out? I'm interested in your feedback. Cheers!  :Smilie:

----------


## dreammanor

Hi jpbro, I checked your source code yesterday, but I failed to install nginx and I'm looking for the reason for the failure. After I install nginx successfully, I'll begin to write some test procedures. I'd like to use your framework to develop a Web-Login module.

----------


## jpbro

Hi dreammanor,

There could be few reasons for nginx not working...

 If you are running any other web server (like IIS for example), then that server has likely already bound to port 80 and this would cause the second attempt by Nginx to fail (unless you change the Nginx port to something else). It could be that your firewall or antivirus/security software is blocking Nginx?

For testing purpose you might want to try using the version I have included in my GitHub repository in the /bin/nginx folder. It doesn't need installation, you can just run it from the Command Line. e.g.:



```
> C:\VbFcgi\bin\nginx\nginx.exe
```

Once it is running you can go to the following page in your browser:

http://127.0.0.1

And you should see a *Welcome to nginx!* message page. If you get that far, then you have a working local dev instance of Nginx running that you can use. The configuration file I've included is set up for a single FCGI web app listener on port 9100, so the command line to start your web app will be:



```
> C:\VbFcgi\bin\VbFcgiHost.exe /port 9100 /spawn 1
```

Once that is spawned you can test if the demo FCGI app is working by going here:

http://127.0.0.1/vbfcgiapp.fcgi

At which point you should the the VBFCGI demo web page.

Hope that helps, but let me know if you still have any issues.

----------


## dreammanor

Hi jpbro, the error message is as follows:

----------


## dreammanor

Directory problem has been resolved, I'm working on permissions issues:

Edit:
I'm now testing some of the solutions found on the Internet.

----------


## jpbro

Sounds like a firewall or antivirus might be blocking Nginx...Some info on error 10013 :

https://answers.microsoft.com/en-us/...7e4eddc?auth=1

----------


## dreammanor

> Hi dreammanor,
> 
> There could be few reasons for nginx not working...
> 
>  If you are running any other web server (like IIS for example), then that server has likely already bound to port 80 and this would cause the second attempt by Nginx to fail (unless you change the Nginx port to something else). It could be that your firewall or antivirus/security software is blocking Nginx?


It is true that system occupies port 80, I modified the listening port in the nginx config file, now the problem has been solved, nginx can be started normally. Thank you, jpbro.

----------


## jpbro

Glad you got the Nginx portion working  :Smilie: 

Let me know if you have any issues going forward with your web app and I will do what I can to help.

----------


## dreammanor

> Glad you got the Nginx portion working 
> 
> Let me know if you have any issues going forward with your web app and I will do what I can to help.


OK, I'm learning and testing your framework, thank you, jpbro.

----------


## jpbro

Coming soon: I've done a bit of work on a demo to show how you can get data from a VB6 form downstream to a web browser using the VbFcgi framework based on comments in the following thread: http://www.vbforums.com/showthread.p...B6-app-FastCGI

Note that I don't recommend this approach for new development, or for long-term deployment. Think of it as a stop-gap measure so you can more quickly get your existing applications on the web for testing, but for the long-term you should be looking to move the functionality out of forms and into classes and/or standard modules. 

Anyway, I have a bit of cleanup to do, but I will try to get this published to the VBFcgi GitHub repository soon.

In the meantime here's a screenshot to whet your appetite!



ALSO AN IMPORTANT NOTE: The VB6 form shown above is NOT being displayed in the browser (it is a separate EXE running). Also, the VB6 form will NOT be showing when used with your FCGI application (it will be loaded but not shown, and interacted with while invisible). In fact you should *never* show any form from your FCGI application as it will be running unattended.

----------


## dreammanor

Hi jpbro, I also need your demo, thanks very much.

Also, when I send the URL http://127.0.0.1/vbfcgiapp.fcgi?json_getdata to the browser, the page can be displayed normally. But when I clicked the button "Request Table Data from Server" at the bottom of the page, the page did not show Json data.

Another question, in addition to using OutputDebugString, I wonder if there are any better ways to debug vbFcgiLib.dll and vbFcgiApp.dll?

----------


## jpbro

@dreammanor - I've just published the Browser<>VB6 Form interfacing demo to GitHub.

re: the "Request Table Data" problem - is it possible that your browser is blocking javascript for 127.0.0.1? If you can confirm that Javascript is allowed, what browser and version are you using? I'll run some tests.

Regarding the OutputDebugString debugging - There might be a better way, but I'm not sure (don't know if there would be an effective way to have a "stepping" debugger when working against the browser - the browser would probably timeout while you are stepping through even if it were possible). 

One thing we could do though is write short test subs for things are want to try out...then you can run the test method and step through the code in the IDE. I think adding some a debug flag to the po_Response object so you can see exactly what data is being written to the pipe might be helpful with this too.

I'm open to any bright ideas about this from anyone though.

Thanks for the feedback  :Smilie:

----------


## dreammanor

> @dreammanor - I've just published the Browser<>VB6 Form interfacing demo to GitHub.
> 
> re: the "Request Table Data" problem - is it possible that your browser is blocking javascript for 127.0.0.1? If you can confirm that Javascript is allowed, what browser and version are you using? I'll run some tests.


I tested the new Demo, the pictures on the VB6 Form still can not be displayed on the page.

I checked my  browser settings, JavaScript is not blocked. But I don't know how to check if JavaScript is blocked only for 127.0.0.1. My browser is Chrome, version: 64.0.3282.140 (64 bit)




> Regarding the OutputDebugString debugging - There might be a better way, but I'm not sure (don't know if there would be an effective way to have a "stepping" debugger when working against the browser - the browser would probably timeout while you are stepping through even if it were possible). 
> 
> One thing we could do though is write short test subs for things are want to try out...then you can run the test method and step through the code in the IDE. I think adding some a debug flag to the po_Response object so you can see exactly what data is being written to the pipe might be helpful with this too.
> 
> I'm open to any bright ideas about this from anyone though.
> 
> Thanks for the feedback


It seems Olaf said that he has better debugging methods.

----------


## jpbro

> I tested the new Demo, the pictures on the VB6 Form still can not be displayed on the page.



Interesting...I just tried in Chrome and the Browser<>VB6 form interfacing is only partially working (getting the ListBox content is OK, but the PictureBox image is not). That part of the demo works fine in Firefox and Edge, so I'll have to do some more testing to see if I can figure out what's going wrong in Chrome. Strangely, I don't have a problem with the "Request Data" method in Chrome though? Always fun working with the various browsers!

----------


## jpbro

Looks like I was wrong in the last post - I had some stale/corrupt cache files in Chrome from an earlier test so that's why the image demo wasn't working. After clearing the cache, everything works fine in Chrome, Firefox, & Edge.

Are things working for you in browsers other than Chrome?

----------


## dreammanor

I cleared the cached files in Chrome & Edge and re-tested, the Demo still can't display the picture. I'll install a Firefox browser to test it.

Also, I would like to download the Json data returned by FastCGI via WinHttp and display it in a VB6 TextBox, but I didn't get the Json data. Here is my test code:



```
Private Sub Form_Click()
    Dim jsonData As String
    
    jsonData = GetJsonDataFromFastCGI()
    
    MsgBox jsonData
    
End Sub

Private Function GetJsonDataFromFastCGI() As String
    Dim Req As Object:      Dim B() As Byte
    
    Set Req = CreateObject("Winhttp.WinHttpRequest.5.1")
    
    With Req
        .Open "POST", "http://127.0.0.1:8080/vbfcgiapp.fcgi?json_getdata", False
        .SetRequestHeader "Content-Type", "application/json"
        .Send vbNullString
        B = .ResponseBody
    End With
    
    If B(0) = 123 Then
        Err.Raise vbObjectError, , StrConv(B, vbUnicode)
    Else
        GetJsonDataFromFastCGI = StrConv(B, vbUnicode)
    End If
        
End Function
```

----------


## jpbro

Hi dreammanor - try this instead (it works for me):



```
Option Explicit

Private Sub Form_Click()
    Dim jsonData As String
    
    jsonData = GetJsonDataFromFastCGI()
    
    MsgBox jsonData
    
End Sub

Private Function GetJsonDataFromFastCGI() As String
    Dim Req As Object:      Dim B() As Byte
    
    Set Req = CreateObject("Winhttp.WinHttpRequest.5.1")
    
    With Req
        .Open "POST", "http://127.0.0.1:8080/vbfcgiapp.fcgi?json_getdata=json_getdata", False
        .SetRequestHeader "Content-Type", "application/json"
        .Send vbNullString
        B = .ResponseBody
    End With
    
    If B(0) <> 123 Then
        Err.Raise vbObjectError, , StrConv(B, vbUnicode)
    Else
        GetJsonDataFromFastCGI = StrConv(B, vbUnicode)
    End If
        
End Function
```

The 2 changes I made are:

 Pass a key and value for the "json_getdata" query. This is because keys and values are currently required by the framework. This doesn't appear to be neccessary by any RFI or anything, so looks like a good candidate for an enhancement to the framework. Test B(0) <> 123 instead of testing B(0) = 123 since we want JSON the error condition should only exist if our first character isn't "["

Hope that helps!

----------


## jpbro

Looks like I've already encountered the issue of "valueless" query keys as an open question at GitHub:

https://github.com/jpbro/VbFcgi/issues/13

----------


## dreammanor

> Hi dreammanor - try this instead (it works for me):
> 
> 
> 
> ```
> Option Explicit
> 
> Private Sub Form_Click()
>     Dim jsonData As String
> ...


Great, it works fine. My first (and most important) goal has been fully achieved. Next, I'll begin to learn how to generate dynamic web pages, I want all the web pages are dynamically generated. Thank you so much, jpbro.

In addition, could FastCGI return binary-data? So I can return the database recordset to client side directly.

----------


## jpbro

You can send raw Byte data downstream from the IFcgiApp_ProcessRequest method as follows:



```
         po_Response.WriteBytes MyByteArray  ' Where my byte array is any byte data
         po_Response.Finished
```

For RC5 recordsets specifically, you can also write JSON downstream as follows:



```
      ' Build JSON response where lo_Rs is a vbRichClient5.cRecordset obje
      Dim lo_Json As VbFcgiLib.CBuilderJson

      Set lo_Json = po_Response.Builders.Builder(builder_Json)
               
      lo_Json.Initialize lo_Rs.ToJSONUTF8
             
      lo_Json.Finish
```

----------


## dreammanor

> You can send raw Byte data downstream from the IFcgiApp_ProcessRequest method as follows:
> 
> 
> 
> ```
>          po_Response.WriteBytes MyByteArray  ' Where my byte array is any byte data
>          po_Response.Finished
> ```
> 
> ...


Very nice, the obstacles to the database operations are cleared.

----------


## dreammanor

> Looks like I was wrong in the last post - I had some stale/corrupt cache files in Chrome from an earlier test so that's why the image demo wasn't working. After clearing the cache, everything works fine in Chrome, Firefox, & Edge.
> 
> Are things working for you in browsers other than Chrome?


I've tested the demo in Firefox, it still can't display the picture. But it doesn't matter, maybe there's something wrong with my computer configuration.

In addition, the test.png file seems to be damaged, it can't be displayed on my computer.





> Looks like I've already encountered the issue of "valueless" query keys as an open question at GitHub:
> 
> https://github.com/jpbro/VbFcgi/issues/13


About the query parameters, I have an idea:

Could we turn the URLs(URIs) of FastCGI into a standard Web-API form? E.g:

127.0.0.1/api/products
127.0.0.1/api/products/id
127.0.0.1/api/products/?category=category

----------


## jpbro

> In addition, the test.png file seems to be damaged, it can't be displayed on my computer.


Thanks for that info - the test.png file does seem broken on GitHub - maybe it didn't get transferred as a binary file? I'll see if I can fix it.

What if you replace the test.png file with a known working PNG file? Does it work then?




> About the query parameters, I have an idea:
> 
> Could we turn the URLs(URIs) of FastCGI into a standard Web-API form? E.g:
> 
> 127.0.0.1/api/products
> 127.0.0.1/api/products/id
> 127.0.0.1/api/products/?category=category


You can configure the server section of your nginx.conf to rewrite addresses like the above into HTTP query strings that VBFCGI will understand. For example, something like this (untested, but I think it will work):



```
rewrite ^(/api/products/id/)([0-9]+) "nomad.fcgi?action=showproducts;productid=$2" last;
```

Would redirect URLs like http://127.0.0.1/api/products/id/1234567 to 127.0.0.1/nomad.fcgi?action=showproducts;productid=1234567

The [0-9]+ part is a regex that matches any sequence of integers 1 or more characters long.

----------


## dreammanor

> What if you replace the test.png file with a known working PNG file? Does it work then?


Yes, if I replace the test.png file with a known working PNG file, it works fine.




> You can configure the server section of your nginx.conf to rewrite addresses like the above into HTTP query strings that VBFCGI will understand. For example, something like this (untested, but I think it will work):
> 
> 
> 
> ```
> rewrite ^(/api/products/id/)([0-9]+) "nomad.fcgi?action=showproducts;productid=$2" last;
> ```
> 
> Would redirect URLs like http://127.0.0.1/api/products/id/1234567 to 127.0.0.1/nomad.fcgi?action=showproducts;productid=1234567
> ...


I haven't tested it yet because the query string I entered in the URL is always unresponsive. I wrote a test procedure, but it still didn't work.



```
Private Sub IFcgiApp_ProcessRequest(po_Request As VbFcgiLib.CFcgiRequest, po_Response As VbFcgiLib.CFcgiResponse)
    ShowHttpQueryParameters po_Request, po_Response
End Sub

Private Sub ShowHttpQueryParameters(po_Request As VbFcgiLib.CFcgiRequest, po_Response As VbFcgiLib.CFcgiResponse)
    Dim ii As Long
    Dim lo_Json As VbFcgiLib.CBuilderJson
         
     Set lo_Json = po_Response.Builders.Builder(builder_Json)
               
    lo_Json.Initialize Nothing ' Initialize to empty collection
    
    lo_Json.IJsonObject.AddJsonObjectByKeyValuePairs "sub", "ShowHttpQueryParameters"
    
    With po_Request.Http.QueryParameters
        For ii = 0 To .KeyCount - 1
            lo_Json.IJsonObject.AddJsonObjectByKeyValuePairs .KeyByIndex(ii), .ValuesByIndex(ii)
        Next ii
    End With
              
    lo_Json.Finish
    
End Sub
```

----------


## jpbro

It looks like I can't add a JSONObject RC5 collection to another JSON Object RC5 collection and have it maintain its overall JSONObject state. This causes the result of SerializeToJSON* to return an empty JSON Object string ("{}"). I'll come up with a workaround ASAP - thanks for posting the code that demonstrates the problem.

----------


## jpbro

Alright, so I think the issue is that JSON objects & arrays that are children of a JSON object must be named/keyed, and the current AddJsonObjectByKeyValuePairs method doesn't take a "name/key" parameter.

There are a couple of options - add a Name/Key parameter (which will break binary compatibility), add a new method that has a Name/Key parameters and will maintain binary compatibility (maybe AddJsonObjectByNameAndKeyValuePairs), or perhaps try to dynamically generate a random name for the  the JSON object/array being added when they are being added to an existing JSONObject. I'll have to give this a bit of thought.

----------


## dreammanor

Thank you jpbro, you did a lot for us.

----------


## jpbro

Hi dreammanor, happy to help  :Smilie: 

I've pushed some changes to the JSON builder/helper class that I hope will do the trick. Now when you want to add array or object data to an existing JSON object collection, the first passed value to _AddJsonObjectByKeyValuePairs_  or _IJson_AddJsonArrayByValues_ should be a string that will act as the object/array name within the parent JSON object.

Here's how I've rewritten your code to accommodate the new approach: 



```
Private Sub IFcgiApp_ProcessRequest(po_Request As VbFcgiLib.CFcgiRequest, po_Response As VbFcgiLib.CFcgiResponse)
    ShowHttpQueryParameters po_Request, po_Response
End Sub

Private Sub ShowHttpQueryParameters(po_Request As VbFcgiLib.CFcgiRequest, po_Response As VbFcgiLib.CFcgiResponse)
    Dim ii As Long
    Dim lo_Json As VbFcgiLib.CBuilderJson
         
    Set lo_Json = po_Response.Builders.Builder(builder_Json)
               
    lo_Json.IJsonObject.AddJsonObjectByKeyValuePairs "sub", "ShowHttpQueryParameters"
    
    With po_Request.Http.QueryParameters
        For ii = 0 To .KeyCount - 1
            lo_Json.IJsonObject.AddJsonObjectByKeyValuePairs "Query" & ii+1, .KeyByIndex(ii), .ValuesByIndex(ii)
        Next ii
    End With
              
    lo_Json.Finish
End Sub
```

Hope that helps, but let me know if you encounter any further issues. Thanks for reporting the problem!

----------


## dreammanor

Thank you so much, jpbro. I'll test it and feed back the test results.

----------


## Schmidt

> ```
>     With po_Request.Http.QueryParameters
>         For ii = 0 To .KeyCount - 1
>             lo_Json.IJsonObject.AddJsonObjectByKeyValuePairs "Query" & ii+1, .KeyByIndex(ii), .ValuesByIndex(ii)
>         Next ii
>     End With
> End Sub
> ```


I've just dived into your code for the first time - and am not really sure (yet), what the purpose of these special Add-Functions is,
but the JSON-standard requires (with regards to adding "new stuff"):

1) in case a JSON-Object is the target you want to add-to: a Key and a Value
2) in case a JSON-Array is the target you want to add-to: only a Value (and no Key)

Values can be "plain, Values" (like Strings, Doubles, Integers, Boolean and the null-value) - 
but *also* "Node-Values" (either another JSON-Array, or another JSON-Object - both encapsulated by the cCollection-Type).

So in that special case above, an add-method would only have to do (instead of the looping):


```
  lo_Json.IJsonObject.AddToObject "QueryParams", _
                             po_Request.Http.QueryParameters.Internal_cCollection_Clone
```

To put the whole Param-List in one single call into the "MotherObject" (target-Object) under the Key "QueryParams".

E.g. in case the target-object already contained two "plain-values" with the keys "FirstName" and "LastName" like that:


```
{
  "FirstName": "Fred",
  "LastName": "Flintstone"  
}
```

Then after the "single-line-add-call" it would e.g. contain:


```
{
  "FirstName": "Fred",
  "LastName": "Flintstone", 
  "QueryParams":{
                    "URL":"Some/Url",
                    "HTTPS":true,
                    "etc":"pp"
                }
}
```

Olaf

----------


## jpbro

> I've just dived into your code for the first time


Thanks for taking a look  :Smilie: 




> - and am not really sure (yet), what the purpose of these special Add-Functions is


I guess it's time to work on some better documentation.

The purpose of the Add* functions is to make it easy to add a JSON Object or JSON Array to an existing JSON (object or array) collection. If there is no pre-existing JSON collection, then the Add* methods will create a top level Object or Array instead (as if you called the Initialize method).

The comments in the Initialize method describe the various ways you can initialize the JSON collection:



```
   ' - A JSON string (VB6 String/UTF-16LE)
   ' - A JSON Byte Array (UTF-8)
   ' - A vbRichClient5 cArrayList object
   ' - A VbRichClient5 cCollection object that has been created as a JSONObject or JSONArray
   ' - A non-byte Array of any type (it will be added to the collection as a JSONArray)
   ' - Nothing object - an empty JSONObject collection will be created
   ' - Empty array (e.g. .Initialize Array()) - an empty JSONArray collection will be created.
```

So after initializing the JSON collection as an JSON Array or Object via the Initialize method, the Add* methods will allow you to to sub-arrays or sub-objects to the initialized collection.

The bug was that I wasn't collection a Name for the appended sub-arrays/objects when they were being appended to a JSON Object collection, but this has been fixed now (the first passed parameter of the ParamArray will be used as the Name for the appended data).




> but the JSON-standard requires (with regards to adding "new stuff"):
> 
> 1) in case a JSON-Object is the target you want to add-to: a Key and a Value
> 2) in case a JSON-Array is the target you want to add-to: only a Value (and no Key)
> 
> Values can be "plain, Values" (like Strings, Doubles, Integers, Boolean and the null-value) - 
> but *also* "Node-Values" (either another JSON-Array, or another JSON-Object - both encapsulated by the cCollection-Type).


Thanks for the clarifications - the code I pushed yesterday understands this now.




> So in that special case above, an add-method would only have to do (instead of the looping):
> 
> 
> ```
>   lo_Json.IJsonObject.AddToObject "QueryParams", _
>                              po_Request.Http.QueryParameters.Internal_cCollection_Clone
> ```


That looks good as an extension too - being able to pass a collection to an Add method would be handy in some case too, but I still think there is value to have the ParamArray versions as well for cases where you just want to quickly add data using a single call without and pre-built collection around. For example:



```
   ' Responding to a call from the browser
   lo_Json.IJsonObject.AddJsonObjectByKeyValuePairs "status", 200, "message", "Everything was successful!"
```

produces:



```
{"status":200,"message":"Everything was successful!"}
```

or:



```
   ' Responding to a call from the browser
   lo_Json.IJsonObject.AddJsonObjectByKeyValuePairs "status", 200
   lo_Json.IJsonObject.AddJsonObjectByKeyValuePairs  "results", "message", "Everything was successful!", "someotherkey", "someothervalue"
```

produces:



```
{"status":200,"results":{"message":"Everything was successful!","someotherkey":"someothervalue"}}
```

----------


## dreammanor

> Hi dreammanor, happy to help 
> 
> I've pushed some changes to the JSON builder/helper class that I hope will do the trick. Now when you want to add array or object data to an existing JSON object collection, the first passed value to _AddJsonObjectByKeyValuePairs_  or _IJson_AddJsonArrayByValues_ should be a string that will act as the object/array name within the parent JSON object.
> 
> Here's how I've rewritten your code to accommodate the new approach: 
> 
> 
> 
> ```
> ...


After clearing the cached files in Chrome, I tested the above code, and the new code does not seem to work.

When I input http://127.0.0.1/vbfcgiapp.fcgi, the browser shows "{" sub ":" ShowHttpQueryParameters "}"

When I input http://127.0.0.1/vbfcgiapp.fcgi?action=getcustomers, the browser shows "502 Bad Gateway"

----------


## dreammanor

Hi jpbro, I'm trying to build a test environment with RC5.cWebServer so that I can debug your FastCGI code in VB IDE.

My idea is as follows:

Use RC5.cWebServer to replace the mo_TcpServer(RC5.cTCPServer) in CFcgiServer, and generate CFcgiDownstream data in *WebServer_ProcessRequest* event. I don't know if this is feasible?

I'm not familiar with web development, so it's important for me to build a test environment that can debug source code in the VB6 IDE.

----------


## jpbro

Hi dreammanor,

Sorry my mistake - the code should actually be as follows:



```
Private Sub IFcgiApp_ProcessRequest(po_Request As VbFcgiLib.CFcgiRequest, po_Response As VbFcgiLib.CFcgiResponse)
    ShowHttpQueryParameters po_Request, po_Response
End Sub

Private Sub ShowHttpQueryParameters(po_Request As VbFcgiLib.CFcgiRequest, po_Response As VbFcgiLib.CFcgiResponse)
    Dim ii As Long
    Dim lo_Json As VbFcgiLib.CBuilderJson
         
    Set lo_Json = po_Response.Builders.Builder(builder_Json)
               
    lo_Json.IJsonObject.AddJsonObjectByKeyValuePairs "sub", "ShowHttpQueryParameters"
    
    With po_Request.Http.QueryParameters
        For ii = 0 To .KeyCount - 1
            lo_Json.IJsonObject.AddJsonObjectByKeyValuePairs "Query" & ii+1, .KeyByIndex(ii), .ValuesByIndex(ii).ValueByIndex(0)
        Next ii
    End With
              
    lo_Json.Finish
End Sub
```

Note that I changed the call to _.ValuesByIndex(ii)_ to _.ValuesByIndex(ii).ValueByIndex(0)_. This is because in theory you can have multiple values with the same key name (it's rare and probably not a good idea, but it isn't against any specification as far as I know, so it had to be accommodated).

----------


## jpbro

Regarding getting RC5 cWebServer linked up to a VBFCGI app - I don't think it is possible because I don't think it supports the FastCGI protocol (nor is there any way to tell it to forward particular queries to the FCGI backend).

Easier debugging would be nice, but I'll have to think of how to accomplish this. What I do now though is write code in MTests in the VbFcgiAppDemo project and call those tests from the Debug window. This does give you the ability to step though most of the code (it doesn't replicate actual calls from a browser, nor does it display data in a browser, but it does let you test your logic and see what will be sent downstream once you start doing actual browser testing).

----------


## dreammanor

> Hi dreammanor,
> 
> Sorry my mistake - the code should actually be as follows:
> 
> 
> 
> ```
> Private Sub IFcgiApp_ProcessRequest(po_Request As VbFcgiLib.CFcgiRequest, po_Response As VbFcgiLib.CFcgiResponse)
>     ShowHttpQueryParameters po_Request, po_Response
> ...


Great, the test code works fine.

----------


## dreammanor

> Regarding getting RC5 cWebServer linked up to a VBFCGI app - I don't think it is possible because I don't think it supports the FastCGI protocol (nor is there any way to tell it to forward particular queries to the FCGI backend).
> 
> Easier debugging would be nice, but I'll have to think of how to accomplish this. What I do now though is write code in MTests in the VbFcgiAppDemo project and call those tests from the Debug window. This does give you the ability to step though most of the code (it doesn't replicate actual calls from a browser, nor does it display data in a browser, but it does let you test your logic and see what will be sent downstream once you start doing actual browser testing).


Now, I can't simulate the *IFcgiApp_ProcessRequest* event in the Debug window or *MTests.bas*. When I input query parameters in the browser and the Web-Server doesn't return the result I want, I can't know where the problem is.

----------


## jpbro

> Now, I can't simulate the *IFcgiApp_ProcessRequest* event in the Debug window or *MTests.bas*. When I input query parameters in the browser and the Web-Server doesn't return the result I want, I can't know where the problem is.


I've got an idea for a CSimulator class that will allow you to simulate a browser request and have it loop back to your CFcgiApp_ProcessRequest method in the IDE. I'm working on this now, but it may take a bit of time to finish. If it works out, you should be able to write tests in a module and execute them from the Immediate window. From there you can step through your code to debug it.

----------


## jpbro

I recommend updating to the version just published at GitHub - it includes improved resiliency to user's who are refreshing the browser rapidly (by holding down the F5 key for example).

Also, you can show a custom 404 HTML page (in the Nginx document root folder) when your FCGI app returns a status of 404.

----------


## dreammanor

> I've got an idea for a CSimulator class that will allow you to simulate a browser request and have it loop back to your CFcgiApp_ProcessRequest method in the IDE. I'm working on this now, but it may take a bit of time to finish. If it works out, you should be able to write tests in a module and execute them from the Immediate window. From there you can step through your code to debug it.


I'm looking forward to the new CSimulator. Extremely grateful.

----------


## jpbro

I've just updated the VbFcgi GitHub repository with the new _VbFcgiLib.CSimulator_ class. It is in an early state right now, but there is a _SimulateRequest_ method that takes a URL and a reference to your _IFcgiApp_ implementing class. It then builds FCGI request and response objects for passing to your _IFcgiApp_ProcessRequest_ method, meaning you can now step through your app in the IDE.

To do this:

In a standard module in your FCGI application project, add some code like this:



```
Public Sub TestApp()
   Dim lo_MyApp As New CFcgiApp   ' Create an instance of your FCGI application
   Dim lo_Simulator As New VbFcgiLib.CSimulator   ' Create an instance of the simulator class

   ' Change the URL below to whatever URL you want to test
   lo_Simulator.SimulateRequest "http://localhost/myapp.fcgi?query1=value1;query2=value2", lo_MyApp
End Sub
```

Make sure to put a breakpoint (or Debug.Assert False) in your _IFcgiApp_ProcessRequest_ method so that you can step through the code after calling TestApp in the Immediate window. 

*NOTE #1* - The simulator has only been lightly tested, so there are probably bugs. Please let me know if you run into any trouble!

*NOTE #2* - The simulator does not currently support POST requests with HTTP body content.

----------


## dreammanor

Thank you so much, jpbro, it is really useful. I'm in Chinese Spring Festival and I'll start testing CSimulator a few days later. Wish every happiness will always be with you and your family!

----------


## jpbro

Thanks for the kind words dreammanor, best wishes to you and your family too!  :wave:

----------


## dreammanor

> I've just updated the VbFcgi GitHub repository with the new _VbFcgiLib.CSimulator_ class. It is in an early state right now, but there is a _SimulateRequest_ method that takes a URL and a reference to your _IFcgiApp_ implementing class. It then builds FCGI request and response objects for passing to your _IFcgiApp_ProcessRequest_ method, meaning you can now step through your app in the IDE.
> 
> To do this:
> 
> In a standard module in your FCGI application project, add some code like this:
> 
> 
> 
> ```
> ...


Hi jpbro, I tested the CSimulator carefully and it worked very well. It is very convenient to use the CSimulator to debug the vbFcgiLib and vbFcgiApp code in the VB6 IDE. Now all the query parameters (including "imagebyindex" and "showparams") are working properly. I like FastCGI more and more, it's really a great product. Thank you so much.

----------


## jpbro

Great, glad you found it helpful! I can imagine some further enhancements to allow you to simulate cookies & body content, but this will have to wait until I have a bit more time. Thanks again for testing it out, and let me know if you encounter any issues.

----------


## dreammanor

Very much looking forward to the new features. I'm testing how to transfer the binary data of a ADODB recordset.

----------


## dreammanor

> You can send raw Byte data downstream from the IFcgiApp_ProcessRequest method as follows:
> 
> 
> 
> ```
>          po_Response.WriteBytes MyByteArray  ' Where my byte array is any byte data
>          po_Response.Finished
> ```


Hi, jpbro, I'm testing how to transfer the binary data of a SqliteDB or ADODB recordset using FastCGI. I rewrote your code based on what you and Olaf suggested:



```
Private Function Action_GetCustomersFromSqliteDB(po_Request As VbFcgiLib.CFcgiRequest, po_Response As VbFcgiLib.CFcgiResponse) As Boolean
    'On Error Resume Next
    
    Dim ii As Long
    Dim lo_Cnn As vbRichClient5.cConnection
    Dim lo_Cmd As vbRichClient5.cCommand
    Dim lo_Rs As vbRichClient5.cRecordset
         
    ' Create an in-memory database
    Set lo_Cnn = libRc5Factory.C.Connection(, DBCreateInMemory)
    lo_Cnn.Execute "CREATE TABLE mytable (code TEXT, value1 INTEGER, value2 INTEGER, value3 REAL)"

    ' Build random table data
    Set lo_Cmd = lo_Cnn.CreateCommand("INSERT INTO mytable (code, value1, value2, value3) VALUES (?,?,?,?)")
    For ii = 0 To 25
        With lo_Cmd
            .SetAllParamsNull

            .SetText 1, Chr$(65 + ii)
            .SetInt32 2, Int(Rnd * 100)
            .SetInt32 3, Int(Rnd * 100)
            .SetDouble 4, Rnd

            .Execute
        End With
    Next ii

    ' Get data into recordset
    Set lo_Rs = lo_Cnn.OpenRecordset("SELECT * FROM mytable")

    po_Response.WriteBytes lo_Rs.Content
    
    po_Response.Finished
    
    If envRunningInIde Then
        ' For debugging purposes, print the content that will be sent downstream
        Debug.Print "Getting data from SqliteDB finished!"
    End If
    
    Set lo_Rs = Nothing
    Set lo_Cmd = Nothing
    Set lo_Cnn = Nothing
End Function
```

Use CSimulator to debug it in the VB6 IDE and it works fine. However, when running in the Chrome browser, I got the following error:



```
An error occurred.
Sorry, the page you are looking for is currently unavailable.
Please try again later.

If you are the system administrator of this resource then you should check the error log for details.

Faithfully yours, nginx.
```

I checked the Nginx error log, the error message is as follows:



```
2018/02/23 18:55:07 [error] 13628#4532: *33 upstream sent invalid header while reading response header from upstream, client: 127.0.0.1, server: localhost, request: "GET /vbfcgiapp.fcgi?getcustomers=1 HTTP/1.1", upstream: "fastcgi://127.0.0.1:9100", host: "127.0.0.1"
```

----------


## jpbro

When you use the "raw" WriteBytes method (instead of one of the IBuilder helpers) then you are responsible for writing the HTTP headers as well. There is no byte-array IBuilder helper right now (though I've been considering writing one), so you will have to do something like this (I haven't had a chance to test, so let me know if there are any errors you can't get around):



```
  Dim la_Content() As Byte
               
  Set lo_Rs = libRc5Factory.C.Recordset
               
  la_Content = lo_Rs.Content
               
  po_Response.WriteBytes stringVbToUtf8("Content-Length: " & arraySize(la_Content) & vbNewLine)
  po_Response.WriteBytes stringVbToUtf8("Content-Type: application/octet-stream" & vbNewLine)
  po_Response.WriteBytes stringVbToUtf8("Content-Type: application/octet-stream" & vbNewLine)
  po_Response.WriteBytes stringVbToUtf8(vbNewLine)
  po_Response.WriteBytes la_Content
```

----------


## dreammanor

Hi jpbro, the above code works perfectly, and I've loaded the binary data returned by FastCGI into the FlexGrid. Thank you so much, happy weekend.

----------


## dreammanor

jpbro, now I can get FastCGI Json data with the following code:



```
    Dim Req As Object:      Dim B() As Byte
    
    Set Req = CreateObject("Winhttp.WinHttpRequest.5.1")

    With Req
        .Open "POST", "http://127.0.0.1/vbfcgiapp.fcgi?json_getdata=1", False
        .SetRequestHeader "Content-Type", "application/json"
        .Send vbNullString
        B = .ResponseBody
    End With
    ...
```

However, sometimes I want to pass the query parameters in another way, such as:



```
    With Req
        .Open "POST", "http://127.0.0.1/vbfcgiapp.fcgi", False
        .SetRequestHeader "Content-Type", "application/json"
        .Send "{""json_getdata"": ""1"""}"
        B = .ResponseBody
    End With
    ...
```

This method can pass more query parameters, but FastCGI doesn't recognize the query parameters emitted in *Send* method.

----------


## jpbro

Hi dreammanor. When you are sending data upstream via body content, the content will be in the FCGI STDIN buffer.

If you are going to be passing all of your parameters upstream as JSON payloads, you could check the Content-Type value in FCGI Parameters object in your VBFCGI app. When you see an "application/json" content type, you can assume it is your list of parameters. For example:



```
   Dim lo_Json As VbFcgiLib.CBuilderJson
   Dim l_ContentType As String

   ' Check if the Content-Type header starts with "application/json"
   ' If so, we'll just reflect the JSON back downstream, but in your app you can parse it out to a vbRichClient5.cCollection
   ' object and then act on the passed parameters as required.
         
   l_ContentType = po_Request.Fcgi.Params.ValueByEnum(stdparam_ContentType)
   
   If Not stringIsEmptyOrWhitespaceOnly(l_ContentType) Then
      ' We split on ";" because the Content-Type header can include extra info like charset. e.g. Content-Type: application/json; Charset=UTF-8
      If LCase$(Split(l_ContentType, ";")(0)) = "application/json" Then
         ' JSON body content received
         Set lo_Json = po_Response.Builders.Builder(builder_Json)
      
         ' Get the JSON body content from the FCGI.Stdin object, and initialize our JSON builder helper with that same data
         ' to send back downstream to prove we received it OK        
         lo_Json.Initialize po_Request.Fcgi.Stdin.Content
               
         lo_Json.Finish
      End If
   End If
```

Hope that helps!

----------


## dreammanor

It's exactly what I need, thank you so much, jpbro.

----------


## dreammanor

Hi jpbro, in order to facilitate the future development, I will make a complete web debugging platform (debugging tools). Your CSimulator is great, and it has made it possible to debug most VbFcgi code in the VB6 IDE. There is only one minor problem, CSimulator can't simulate a real browser scene (that is, the process between sending URLs from the browser and VbFcgi start receiving the data). So I have an idea, using RC5.cWebServer to simulate this process, the process is as follows:

(1) winHttp sends a request to RC5.WebServer

(2) RC5.WebServer forwards this request to VbFcgi

(3) VbFcgi accepts the request from RC5.WebServer

(4) VbFcgi returns the processed result to RC5.WebServer (*Note: As long as the above three steps can achieve the purpose of testing, the fourth step is not necessary.*)


In other words, let RC5.WebServer to simulate a small part of the nginx function in the VB6 IDE. If this feature can be achieved, then all the VbFcgi code can be debugged in the VB6 IDE. I don't know whether this idea is feasible, I would like to hear the professional opinions from you and Olaf.

----------


## dreammanor

Hi jpbro, I have a few minor questions:

The vbFastCGI query parameters are case sensitive, for example, "*Action*=1" is different from "*action*=1". I checked the source code and found out why:

*CHttpQueryParams
*

```
Private Sub Class_Initialize()
   Set mo_QueryParams = libRc5Factory.C.Collection(False, BinaryCompare)
End Sub
```

I'd like to know if it is necessary to change *BinaryCompare* to *TextCompare*?

In addition, HttpCookies are also case-sensitive:

*CHttpCookies*


```
Private Sub Class_Initialize()
   Set mo_Cookies = libRc5Factory.C.Collection(False, BinaryCompare)
End Sub
```

----------


## dreammanor

The second small question:

*CFcgiStdIn*


```
Public Function Content() As Byte()
   Dim la_Bytes() As Byte
   Dim l_CurPos As Long
   
   ' On Error Resume Next
   
   ' Get all available content in STDIN
   
   If Me.HasContent Then
      l_CurPos = mo_StdIn.GetPosition  ' Record current stream position
      
      mo_StdIn.SetPosition 0  ' Go to beginning of stream
      mo_StdIn.ReadToByteArr la_Bytes  ' Get all bytes
      
      mo_StdIn.SetPosition l_CurPos ' Reset stream position
      
   Else
      la_Bytes = Array()   ' Return empty array. LBound = 0, UBound = -1
   End If

   Content = la_Bytes
End Function
```


The TypeName of *Array*() is Variant, and the TypeName of *la_Bytes* is Byte, so "*la_Bytes = Array()*" will always have "Type mismatch" error. How can we get an empty byte-array with *LBound = 0* and *UBound = -1*?

----------


## dreammanor

The third small question:

*CFcgiApp*


```
Private Sub IFcgiApp_ProcessRequest(po_Request As VbFcgiLib.CFcgiRequest, po_Response As VbFcgiLib.CFcgiResponse)
 ...
 ...
 ...

Final:
 If po_Response.IsFinished Then
    po_Response.Finished
 end If

end Sub
```

Sometimes we may forget to call po_Response.Finished, and if a *IsFinished* property (similar to IBuilder.*IsFinished*) is provided for CFcgiResponse, then we can execute the following code at the end of the sub:



```
 If po_Response.IsFinished Then
    po_Response.Finished
 end If
```

----------


## wqweto

> The TypeName of *Array*() is Variant, and the TypeName of *la_Bytes* is Byte, so "*la_Bytes = Array()*" will always have "Type mismatch" error. How can we get an empty byte-array with *LBound = 0* and *UBound = -1*?


Try assigning empty string like la_Bytes = vbNullString

cheers,
</wqw>

----------


## jpbro

> The vbFastCGI query parameters are case sensitive, for example, "*Action*=1" is different from "*action*=1".


My interpretation of RFC 3986 URI specifications leads me to believe that query parameter keys must be considered case-sensitive in order to be conformant:




> 6.2.2.1.  Case Normalization
> 
>    For all URIs, the hexadecimal digits within a percent-encoding
>    triplet (e.g., "%3a" versus "%3A") are case-insensitive and therefore
>    should be normalized to use uppercase letters for the digits A-F.
> 
>    When a URI uses components of the generic syntax, the component
>    syntax equivalence rules always apply; namely, that the scheme and
>    host are case-insensitive and therefore should be normalized to
> ...


So the scheme and host are case-insensitive, but the rest of the URI is case-sensitive unless specifically defined otherwise by the scheme (which unless I missed something, there is not such exception defined for HTTP/HTTPS). For that reason, I'd like to keep it as BinaryCompare - though perhaps I could make an option, I'm not sure that it's a great idea to be purposefully allowing non-conformant behaviour.

As for cookies, I found this: https://stackoverflow.com/questions/...case-sensitive

It states that while there is nothing in the RFC that defines cookies as having to be case-sensitive, they are de-facto case-sensitive due to the fact that cases-insensitivity is usually explicitly prescribed for various elements elsewhere in the RFC, and various browser implementations consider cookie keys to be case-senstitive. For those reasons, I think it's important to use binary comparison for cookie keys.

----------


## jpbro

> The second small question:
> 
> The TypeName of *Array*() is Variant, and the TypeName of *la_Bytes* is Byte, so "*la_Bytes = Array()*" will always have "Type mismatch" error. How can we get an empty byte-array with *LBound = 0* and *UBound = -1*?


Good catch, thanks for reporting this bug. And thanks @wqweto for a succinct solution  :Smilie: 

I'll have this fixed and push to the GitHub repository soon.

----------


## jpbro

@dreammanor, I've added an IsFinished property to the CFcgiResponse class. Please note though there there's a small bug in your logic for the end of sub code. It shuold be:



```
If Not po_Response.IsFinished Then
   po_Response.Finished
End If
```

(notice the "Not").

All changes/fixes are now published at the VbFcgi GitHub repository.

----------


## dreammanor

> Try assigning empty string like la_Bytes = vbNullString
> 
> cheers,
> </wqw>


Hi wqweto, your solution is great, thank you.

----------


## dreammanor

> My interpretation of RFC 3986 URI specifications leads me to believe that query parameter keys must be considered case-sensitive in order to be conformant:
> 
> 
> 
> So the scheme and host are case-insensitive, but the rest of the URI is case-sensitive unless specifically defined otherwise by the scheme (which unless I missed something, there is not such exception defined for HTTP/HTTPS). For that reason, I'd like to keep it as BinaryCompare - though perhaps I could make an option, I'm not sure that it's a great idea to be purposefully allowing non-conformant behaviour.
> 
> As for cookies, I found this: https://stackoverflow.com/questions/...case-sensitive
> 
> It states that while there is nothing in the RFC that defines cookies as having to be case-sensitive, they are de-facto case-sensitive due to the fact that cases-insensitivity is usually explicitly prescribed for various elements elsewhere in the RFC, and various browser implementations consider cookie keys to be case-senstitive. For those reasons, I think it's important to use binary comparison for cookie keys.


What you say is very reasonable, and it's better to keep case sensitivity.

----------


## dreammanor

> @dreammanor, I've added an IsFinished property to the CFcgiResponse class. Please note though there there's a small bug in your logic for the end of sub code. It shuold be:
> 
> 
> 
> ```
> If Not po_Response.IsFinished Then
>    po_Response.Finished
> End If
> ```
> ...


Thank you, jpbro. I've downloaded your new code.

----------


## dreammanor

Hi jpbro, I want to develop a Website-Builder similar to Wordprocess or Joomla, which is both a user self-building website platform and a content management system. I know most Website-Builders are developed using PHP + MySQL, and some are developed using PHP + GoLang + MySQL. You are a web expert and I would like to know if the VBFcgi can be used to develop a Website-Builder like Wordprocess or Jommla? Besides VBFcgi, what other tools or technologies do I need?  Thanks.

*Edit:*
Olaf just gave me some very helpful suggestions.

----------


## jpbro

I recently updated the demo source & binaries over at GitHub to show how you can use a thread shared in memory SQLite DB connection to share non-persistent data between instances of your VBFCGI application DLLs running under the same VBFCGI host process. This is based on an approach that Olaf demonstrated here: http://www.vbforums.com/showthread.p...-ThreadHandler

----------


## jpbro

Hi dreammanor,

Sorry I missed your question back in June. I was busy with an illness in the family for a good chunk of the summer and things are just getting back to normal now.

The short answer is that you can develop just about anything you want - on the browser side of course you will be using HTML5, CSS, JavaScript (and any JS frameworks you like) to handle the front-end/UI. You can dynamically build this stuff in your VBFCGI app and send it downstream, or you can have static stuff built and served directly by Nginx. The static HTML5/JS stuff will then call back up to your VBFCGI app to get the dynamic data (usually in the form of JSON strings) and make changes to the UI. 

In the middle/back-end you have your VBFCGI app, and it is where you will do all of your database IO. There are lots of options for your database, and the choice will depend on your expected workloads, how much concurrency you require, etc...

One thing you might want to look at is my basic "Template" demo approach. Essentially, you can write HTML5 pages that have special tags inserted where you want dynamic information to appear. The VBFCGI library will notice these tags and raise an event in your VBFCGI app. In your app you can then perform whatever processing you want (coded in VB6) then server back a response that the framework will use to replace the special tag with. Once all tags are processed, the framework will ship the completed HTML downstream for eventual consumption by a web browser.

It's a really broad topic to be honest, and it's difficult to give any specific recommendations on how to create a self-building website platform (it's not really my area of expertise). I'd be curious to see what Olaf suggested to you too  :Smilie:

----------


## dreammanor

> I was busy with an illness in the family for a good chunk of the summer and things are just getting back to normal now.


Very sorry to hear that. I hope you and your family will always be healthy and safe.




> Hi dreammanor,
> 
> The short answer is that you can develop just about anything you want - on the browser side of course you will be using HTML5, CSS, JavaScript (and any JS frameworks you like) to handle the front-end/UI. You can dynamically build this stuff in your VBFCGI app and send it downstream, or you can have static stuff built and served directly by Nginx. The static HTML5/JS stuff will then call back up to your VBFCGI app to get the dynamic data (usually in the form of JSON strings) and make changes to the UI. 
> 
> In the middle/back-end you have your VBFCGI app, and it is where you will do all of your database IO. There are lots of options for your database, and the choice will depend on your expected workloads, how much concurrency you require, etc...
> 
> One thing you might want to look at is my basic "Template" demo approach. Essentially, you can write HTML5 pages that have special tags inserted where you want dynamic information to appear. The VBFCGI library will notice these tags and raise an event in your VBFCGI app. In your app you can then perform whatever processing you want (coded in VB6) then server back a response that the framework will use to replace the special tag with. Once all tags are processed, the framework will ship the completed HTML downstream for eventual consumption by a web browser.
> 
> It's a really broad topic to be honest, and it's difficult to give any specific recommendations on how to create a self-building website platform (it's not really my area of expertise). I'd be curious to see what Olaf suggested to you too


Thank you for your detailed reply. The development of WebBuilder has not yet begun, mainly because I have not found a suitable solution. 

Olaf's suggestion is in the following post:
http://www.vbforums.com/showthread.p...=1#post5298619

----------


## dreammanor

> I recently updated the demo source & binaries over at GitHub to show how you can use a thread shared in memory SQLite DB connection to share non-persistent data between instances of your VBFCGI application DLLs running under the same VBFCGI host process. This is based on an approach that Olaf demonstrated here: http://www.vbforums.com/showthread.p...-ThreadHandler


Hi jpbro, thank you very much. I'm currently developing a small project with Golang. After the project is completed, I'll start testing the latest vbFastCGI.

----------


## jpbro

Are you writing the back end stuff in Golang? Or or you transpiling Golang to JS for the front end?

If you are doing the back end in Goland then maybe VBFCGI isn't relevant. The main purpose of VBFCGI is to write your backend in VB6 and there's only 2 reasons to do that:

 You have existing back end code from a mature Client/Server or N-tier application that you want to use on the web. There is no "re-writing" in this case, so you get to spend all/most of your efforts on the front end (using regular browser technologies like HTML5, JS, CSS, etc...) You don't want to learn another language for the back end stuff because you are most skilled at writing in VB6. This would be the case for new projects where the goal is to get the back end together as quickly as you are able (since you are fastest at coding in VB6), then focus on the front-end in regular web technologies like HTML5, JS, CSS, etc...

If you are starting a new project in a new (to you) language like Golang, it would probably be best to forego VBFCGI entirely and choose a more standard web stack.

----------


## dreammanor

> Are you writing the back end stuff in Golang? Or or you transpiling Golang to JS for the front end?
> 
> If you are doing the back end in Goland then maybe VBFCGI isn't relevant. The main purpose of VBFCGI is to write your backend in VB6 and there's only 2 reasons to do that:
> 
>  You have existing back end code from a mature Client/Server or N-tier application that you want to use on the web. There is no "re-writing" in this case, so you get to spend all/most of your efforts on the front end (using regular browser technologies like HTML5, JS, CSS, etc...) You don't want to learn another language for the back end stuff because you are most skilled at writing in VB6. This would be the case for new projects where the goal is to get the back end together as quickly as you are able (since you are fastest at coding in VB6), then focus on the front-end in regular web technologies like HTML5, JS, CSS, etc...
> 
> If you are starting a new project in a new (to you) language like Golang, it would probably be best to forego VBFCGI entirely and choose a more standard web stack.


Hi jpbro,

In my last project, I've implemented cloud account management using vbFastCGI on Linux. Now what I'm going to do is a new project, which is mainly about big data storage, so I need to develop this project with Golang.

Edit:
In addition, I've sent a private message to you, I don't know if you have received it. Every night, vbForums is extremely crowded and often unable to send posts properly.

----------


## jpbro

Hi dreammanor,

I received your private message and have replied. I repost some of it here because I think it could be useful information for others who come across this thread:

I understand the desktop market is drying up quickly in China - it is happening here too, though I think a bit less dramatically as I can still make a living of the desktop stuff with business customers. There's no doubt that things are moving more and more to the cloud though, even with business applications which is why I developed VBFCGI as it gives me a quick & clear path to the web. Though not all of my VB6 code will be useful there (primarily all of the user interface stuff that must be completely re-written in web languages like HTML5, JS, and CSS), all of my backend stuff (database, user management, business logic, etc...) can be re-used. Since I am the one running the backend services for customers though, my users don't care what the backend is written in since they don't ever see/touch the backend as it is all in the cloud.

That said, if you are writing new stuff, it makes sense to make use of newer technologies on the backend like Go. Since I have somewhere in the neighbourhood of 700,000 lines of existing backend VB6 code, I wanted to keep as much of it as possible. If you are starting a fresh project then you won't have the same incentive of course!

----------

