-
1 Attachment(s)
VB6 - Yet Another Web Server
The attached project contains a demonstration program using Gossamer, a web server in a UserControl.
Adding the control to your projects is simple. To use it just drop an instance onto a Form, set a few properties, and optionally write two event handlers. Then call StartListening and Gossamer does the rest.
Gossamer handles:
- Static GET requests.
- Dynamic GET requests.
- Static HEAD requests.
- Dynamic POST requests.
Dynamic requests are processed by raising an event back to Gossamer's container Form, where your logic processes the request and returns results.
A tiny sample web site is included that the example program works with to show how to use Gossamer. It has an example of handling dynamic requests as well as static ones.
It would be easy to repackage Gossamer as an OCX if desired.
Gossamer is probably not fully debugged, having been thrown together in two days. But I believe I have gotten most of the glaring bugs out. You can easily add more logic to extend its capabilities as well as tweak things and fix problems.
Testing the sample is easy. Just unzip the project into a folder, open GossDemo1.vbp, run the program and click its Start button. Then point a browser to localhost:8080 to view the example web site.
-
1 Attachment(s)
Re: VB6 - Yet Another Web Server
Small update to the Gossamer Web server control.
Several small improvements for stability and performance, added member descriptions and marked class members intended for internal use as "hidden." See comments at head of modules.
Changes impacting use include:
- VDir property can no longer be changed while Gossamer is listening.
- Relative paths may now be used in hrefs and requests as long as they don't try to escape VDir.
-
1 Attachment(s)
Re: VB6 - Yet Another Web Server
Ok, one last update for now. Remember, you can comment here as well as sending PMs. ;)
Unless there are bugs to fix I don't plan to post further updates. The coding was kept fairly simple so you can make changes on your own. That webcam server was a nice thing one person made, and the mini-forum was interesting but I'd probably just use a stand alone web server product for that myself.
No, I was not planning to add HTTPS support.
This version adds support for browser caching via the If-Modified-Since header. It also exposes a few new Gossamer properties and methods of use in programming dynamic request handlers. I hope it doesn't add any new bugs.
-
1 Attachment(s)
Re: VB6 - Yet Another Web Server
Corrected an absurd, small, non-fatal mistake in handling I/O buffering of static resources (files). Increased buffer size somewhat for improved performance. Now a Const, this buffer size could easily be made a Gossamer property if desired instead.
-
1 Attachment(s)
Re: VB6 - Yet Another Web Server
A few minor changes in this version.
Also added EntityEncode as a Gossamer method. Previously this logic was in the demo project's form itself.
-
Re: VB6 - Yet Another Web Server
Quick question, may sound absurd, but could support for PHP, MySQL etc be added easily?
-
Re: VB6 - Yet Another Web Server
As written, Gossamer raises an event to the parent program upon receiving any POST request or any GET request that has parameters.
This isn't entirely kosher, because the usual approach is to define VDirs for active content (i.e. CGI-BIN folders, etc.) or otherwise mark those incoming URIs requiring dynamic processing. This could be changed if required of course.
In any case, when Gossamer sees a dynamic GET/POST request it raises that event to its container, generally a Form. The event handler has a raft of parameters containing the inputs from the request and accepts outputs to make up a response to go back.
What you do in the event handler and container is up to you. The Form supplied in the archive(s) above is just a trivial example.
However...
Gossamer is not meant to replace general purpose Web servers. It was designed as an embedded server within a VB6 program. As such, the idea was that you would write all of the server-side logic required in VB6.
Yes, it is possible to call PHP either through its CGI interface, or its ISAPI interface (I think it has one), or maybe even the Apache module API. My guess is that this would take some work though. Even the simple CGI interface can get messy, since you'll have to pack up a lot of things into a private Environment Variables block before calling CreateProcess() on the PHP interpreter.
-
Re: VB6 - Yet Another Web Server
How would you change the structure and use html code inside the program like
strHTLM = "<HTML><BODY><H1>" & SomeText & "</H1></BODY></HTML>"
and not use the site with htm files?
-
Re: VB6 - Yet Another Web Server
Look in the client connection handling UserControl (GossClient). When a GET or HEAD request is found, the presence of request query parameters (beginning with a "?") currently is taken as a request for dynamic content instead of a static resource.
This bit of logic could be changed to treat other indications as a request to be processed dynamically. For example you might look at strParts(1) in this part of the code, and if certain values are found that might normally be seen as a request for a file you could call Parent.RaiseDynamicRequest.
So a URL like http://localhost/status.z might be taken as a request for a dynamic "status" report, or http://localhost/snapshot.z might be a request to snap a webcam picture and return it, etc. The key here would be anything requested that ends in a .z extension.
You might use any scheme you want really.
-
Re: VB6 - Yet Another Web Server
Is it possible you can show me how you do it with a code sample I can try?
-
1 Attachment(s)
Re: VB6 - Yet Another Web Server
I have attached a hacked version.
It has changes in GossClient to change the behavior of Gossamer, and in Form1 to take advantage of the changes to GossClient.
This version treats any GET request for a static page as a special dynamic request and creates the page to return from inside the program. It doesn't use any .HTM files.
-
Re: VB6 - Yet Another Web Server
dilettante thank you for taking the time for this hack, it working great with single output but can it still have images and get and post feature and selective output html code like link to other html output? I tried to put <img src=""images.jpg""> inside the html code and image in same folder as the .exe but it does not display image on the website, what would be the trick there? How would you grab the value from post and get from webpage with out having it in the url?
-
Re: VB6 - Yet Another Web Server
When your web server gets a request for a resource it is supposed to respond with the proper response.
You need to look at what is requested and decide what goes back to the browser. If they ask for "images.jpg" and you want that to return an image, you detect it in the request and respond with the right headers and image data.
The hacked version treats ALL requests as dynamic requests. It will never retrieve and send a file.
You might be better off going back to the original version, and then change it so that it treats requests with a certain resource extension as a dynamic request and others as static requests. Just pick something fairly typical (.CGI, .ASP, .DYN) and use that.
The original version treats any GET request with parameters and any POST request as dynamic, i.e.:
GET /anything.xxx?a&b=c
You'll want to change it so:
GET /folder/resource.dyn
GET /folder/other.dyn?a&b=c
... are treated as dynamic requests. Then when a static request comes in like:
GET /folder/image.jpg
... it still gets treated as a static request, and Gossamer will fetch it from the appropriate disk file.
You could also trigger on a folder as well:
GET /dyn/resource.htm
GET /folder/other.dyn?a&b=c
As written the non-hacked version looks for static resources inside a site folder next to the EXE. You can change this by changing your Gossamer control's VDir property to something else, either at design time or run time.
-
Re: VB6 - Yet Another Web Server
I have a little code snippet to enable php processing.
I can post it if you're interested.
-
Re: VB6 - Yet Another Web Server
I am interested in php processing. Can you please post the snippet here or how to add php support in this Web Server?
-
Re: VB6 - Yet Another Web Server
Code:
Public Function ProcessPHP(ByVal FilePath As String, Optional ByVal phpPath As String = "C:\XAMPP\php\php-win.exe", Optional ByVal Args As String = "-f") As String
'Using reference to: Windows Script Host Object Model
' "System32\wshom.ocx"
'/// Declare Shell Objects
Dim Shell As New WshShell
Dim retObj As WshExec
Dim retStr As TextStream
'/// Declare Basic Variables
Dim cCommand As String
Dim q As String
'/// q is for quoting the filepath correctly in the command
q = """"
'/// Put together the command
cCommand = phpPath & " " & Args & " " & q & FilePath & q
'/// Execute command
Set retObj = Shell.Exec(cCommand)
'/// Put return value in textstream and make a string value out of it
Set retStr = retObj.StdOut
ProcessPHP = retStr.ReadAll
End Function
Basically this is it.
But you have to set reference to the "Windows Script Host Object Model" and you have to modify the path to the php executable.
The current is the path to a xampp installation.
All that's left to implement this is constructing a file extension handler (if there isn't one yet in this app) and passing the filepath through this function. The result will be the generated HTML.
-
Re: VB6 - Yet Another Web Server
Very nice code. But I need it in this web server. How can I add it in this codebank project?
dilettante, can you add this to your project?
-
Re: VB6 - Yet Another Web Server
-
1 Attachment(s)
Re: VB6 - Yet Another Web Server
While you can do anything you want with it, Gossamer was not primarily intended to be used to create a general purpose web server. Its real purpose is to write VB6 programs that are servers, and happen to use HTTP as the client connection protocol.
The demo doesn't reflect this since it was meant as a quick and dirty example of use. So as such it was designed for web browsers as clients even though this normally isn't what you'd be doing. As I said, you certainly can use Gossamer to make a web server, but then why would you want it to support PHP, ASP, CGI, etc? The whole reason for Gossamer is for a VB6 program to do the things you might otherwise be forced to do in a scripting language.
Secondarily it can be used to provide a web UI to a VB6 program. That's more in line with what the demo does.
But the sweet spot is to let you write VB6 programs that provide web services.
This has become much easier now that even Microsoft is backing away from SOAP as a bad approach. It adds unneeded complexity, interoperability challenges, and high costs in maintenance over an application's lifetime.
Instead we see more REST-ful protocols used on top of HTTP now. These may use XML, they may use JSON, or they may use another serialization format. But they're all easier to use and bypass many of the woes of the SOAP era.
In any case, after a long while I have a new version to post.
This one has added a new "soft Winsock error" event type and handles these more gracefully. Right now the only error identified as "soft" and ignorable is sckWouldBlock, which could occur on the listening socket under certain circumstances and can be ignored.
The code in the demo Form also has a small change to accomodate this and to fix a silly bug that cause the state of the Start/Stop buttons to change when it shouldn't.
-
1 Attachment(s)
Re: VB6 - Yet Another Web Server
Another update
Corrected one serious, long-standing flaw. Corrected a few small flaws as well as some quite minor or cosmetic things.
The main bug fixed here is the way the HTTP response line was being built. There was no space being emitted between the status code and status text, and this error has been there since the beginning. How the program ever worked is a mystery!
The other bug of consequence that was fixed is to send a Content-Length header of 0 on responses with no body following the response headers. This is another issue that should have resulted in client hangs from time to time.
All of this just confirms that whenever possible you want to use existing well tested software when you can instead of trying to roll your own solution.
Sadly for us there isn't any readily available low cost HTTP server component for use in VB6 programs, and even fewer where the source code is available. As I find and fix things I'll try to keep updating. I do try to rerun regression tests to catch anything that used to work but gets broken by updates.
A Known Limitation
Gossamer 1.x is not designed to handle uploading binary data. It can also fail in some cases if a client is sending UTF-8 encoded requests instead of 7-bit ASCII.
I hope to produce a version 2.0 in the future that addresses this, but for now keep these issues in mind.
-
Re: VB6 - Yet Another Web Server
Hi, this web server is great! Apart from localhost, would you teach me what shall I do to open the server for Internet access?
-
Re: VB6 - Yet Another Web Server
Quote:
Originally Posted by
aukaywah
Hi, this web server is great! Apart from localhost, would you teach me what shall I do to open the server for Internet access?
That is more of an administration issue than a programming issue.
While there are threads here in the CodeBank on ways to automate creation of NAT router port fowarding and firewall rules these are a limited solution at best. There are lots of routers and lots of firewalls in use that cannot be controlled in such a manner.
Gossamer and similar components are best used within LANs as an alternate user interface for long-running programs such as those built as Windows Services. They aren't as useful exposed to the public Internet and are not meant as alternatives to normal web servers for general use.
-
Re: VB6 - Yet Another Web Server
nise project thank you for shareing, i shall learn a lot from this.
-
Re: VB6 - Yet Another Web Server
Interesting Dillettante, I will investigate the way to communicate using your web server, and an Android App using B4A to communicate in both way.
The problem is that it needs to rediret the port on the router to the local computer on the network :(
I am still investigating, but great job.
-
Re: VB6 - Yet Another Web Server
Quote:
Originally Posted by
Thierry69
The problem is that it needs to rediret the port on the router to the local computer on the network :(
This is a "problem" for all webservers. It is normally quite easy to edit the firewall rules so it should only take you a few minutes with google to figure it out.
Edit: Also agree with other that this a great project
-
Re: VB6 - Yet Another Web Server
Quote:
Originally Posted by
Thierry69
The problem is that it needs to rediret the port on the router to the local computer on the network :(
You can then combine this with another of dilletante codebank entries -- [VB6] PortMapper - UPnP NAT Traversal Class -- for seamless user experience.
cheers,
</wqw>
-
Re: VB6 - Yet Another Web Server
Quote:
Originally Posted by
wqweto
upnp has a reputation for being insecure (rightly or wrongly) and many businesses simply turn it off as a best practice. The last few "residential" routers i bought also had it off by default too.
-
Re: VB6 - Yet Another Web Server
found an infinite loop bug
in gossclient.wskClient.DataArrival
Code:
'Strip whitespace from Attribute.
strChar = Right$(strParts(0), 1)
Do While strChar = vbTab Or strChar = " "
strParts(1) = Left$(strParts(0), Len(strParts(0)) - 1)
strChar = Right$(strParts(0), 1)
Loop
If the header key value has a whitespace or tab, it just does the loop over and over again.
I think strParts(1) = should be strParts(0) =
or just do a replace() for tabs and trim for spaces
-
2 Attachment(s)
Re: VB6 - Yet Another Web Server
I converted Gossamer to utilize SimpleServer instead of the Winsock Control. It utilizes a single User Control, as mServer(0) serves as the listening socket and the remainder of the socket arrays service the connections. It provides the ability to use IPV6 instead of just IPV4, although this has not been tested as yet.
I tried to preserve as much of the original code as I could. It is not necessarily the way that I would normally do it. Each programmer has his/her own style. For example, the original code used STATIC_BUFFER_SZ As Long = 8192. SimpleServer uses whatever the operating system provides. In my case the buffer size is 65,536, and for the moment I have left the buffer size at 8,192 until I can figure out how to transfer that value from SimpleServer.
There is no reason that encrypted data cannot be integrated into this service. If I was to do it, I would incorporate TLS 1.3. This can be viewed as a starting point, as there is only a small part of TLS 1.2 that provides the same protection. All previous versions are obsolete/obfuscated.
This download does not include the contents of the \site or \art directories.
J.A. Coutts
Updated: 09/03/2020
-
Re: VB6 - Yet Another Web Server
Quote:
Originally Posted by
couttsj
I converted Gossamer to utilize SimpleServer instead of the Winsock Control. It utilizes a single User Control, as mServer(0) serves as the listening socket and the remainder of the socket arrays service the connections. It provides the ability to use IPV6 instead of just IPV4, although this has not been tested as yet.
I tried to preserve as much of the original code as I could. It is not necessarily the way that I would normally do it. Each programmer has his/her own style. For example, the original code used STATIC_BUFFER_SZ As Long = 8192. SimpleServer uses whatever the operating system provides. In my case the buffer size is 65,536, and for the moment I have left the buffer size at 8,192 until I can figure out how to transfer that value from SimpleServer.
There is no reason that encrypted data cannot be integrated into this service. If I was to do it, I would incorporate TLS 1.3. This can be viewed as a starting point, as there is only a small part of TLS 1.2 that provides the same protection. All previous versions are obsolete/obfuscated.
This download does not include the contents of the \site or \art directories.
J.A. Coutts
Code:
Private Sub SendResponse(ByVal Status As Single, ByVal StatusText As String, ByVal MIME As String, ByVal ExtraHeaders As String)
AppendResp mStrHTTPVersion & " " & CStr(Status) & " " & StatusText & vbCrLf
AppendResp "Date:" & UTCString(Now()) & vbCrLf
If InStr(1, ExtraHeaders, "Last-Modified:", vbTextCompare) = 0 Then
AppendResp "Last-Modified:" & UTCString(Now()) & vbCrLf
End If
If Len(MIME) > 0 Then AppendResp "Content-Type:" & MIME & vbCrLf
mLngResponseLen = 0
On Error Resume Next
mLngResponseLen = UBound(mBytResponse) + 1
On Error GoTo 0
AppendResp "Content-Length:" & CStr(mLngResponseLen) & vbCrLf
AppendResp "Accept-Ranges:none" & vbCrLf
AppendResp ServerHeader & vbCrLf
If Len(ExtraHeaders) > 0 Then
AppendResp ExtraHeaders
If Right$(ExtraHeaders, 2) <> vbCrLf Then AppendResp vbCrLf
End If
AppendResp vbCrLf 'Second CRLF, terminating headers.
' mServer(mIntIndex).uOutBuffer = ExtractResp ‘
mServer(mIntIndex).sOutBuffer = ExtractResp '
mServer(mIntIndex).TCPSend
End Sub
-
Re: VB6 - Yet Another Web Server
I figured out how to get the buffer size from SimpleServer to Gossamer, I had to add another Get to SimpleServer.
Code:
Friend Property Get BufferSize() As Long
BufferSize = m_lSendBufferLen
End Property
The buffer size is now obtained after the connection is established. But this introduced another problem. When I tried to send a file more than 32,767 bytes, the MOD function in ProcessRequest failed. I had to change mIntLastBlockSize to a Long value (mLngLastBlockSize).
xxdoc123;
Thanks for the tip. I obviously didn't test that part.
The download in Post #29 has been updated to reflect these changes.
On another topic, can anyone explain how the dynamic request is supposed to work?
J.A. Coutts
-
Re: VB6 - Yet Another Web Server
-
Re: VB6 - Yet Another Web Server
Quote:
Originally Posted by
couttsj
On another topic, can anyone explain how the dynamic request is supposed to work?
It lets you return data based on a named function instead returning a file.
You will need to modify the Gossamer_DynamicRequest() event to look for a URI and the compose an appropriate response and send it.
Here is a really basic example:
add the bold code to the "GET" case in DynamicRequest()
Code:
Select Case Method
Case "GET"
If LCase(URI) = "\foobar" Then
RespStatus = 200
RespStatusText = "Ok"
RespMIME = "text/html"
RespBody = StrConv(Now, vbFromUnicode)
End If
If LCase$(URI) = "\formget.htm" Then
intFile = Gossamer.GetFreeFile()
Open Gossamer.VDir & URI For Binary Access Read As #intFile Len = 8192
strRespBody = Input$(LOF(intFile), #intFile)
Close #intFile
strParams = Split(Params, "&")
For intParam = 0 To UBound(strParams)
strParts = Split(strParams(intParam), "=")
strDyn = strDyn & Gossamer.URLDecode(strParts(0))
If UBound(strParts) > 0 Then
strDyn = strDyn _
& " = " _
& EntityEncode(Gossamer.URLDecode(strParts(1))) _
& "<BR>"
End If
Next
strRespBody = Replace$(strRespBody, "<!-- INSERT -->", "<P>" & strDyn & "</P>")
RespStatus = 200
RespStatusText = "Ok"
RespMIME = "text/html"
RespBody = StrConv(strRespBody, vbFromUnicode)
End If
Case "POST"
Then point your browser at http://localhost:8080/foobar? and you will see the current time.
You can also accept parameters
http://localhost:8080/getweather?zip=98052
So you have a lot of flexibility with the types of requests you can take and respond to.
you will want to put breakpoints in the function and then hit the server with various requests to see what is possible. A tool like Postman will be very valuable for discovery, testing, and development.
-
Re: VB6 - Yet Another Web Server
Quote:
Originally Posted by
DllHell
It lets you return data based on a named function instead returning a file.
You will need to modify the Gossamer_DynamicRequest() event to look for a URI and the compose an appropriate response and send it.
Thanks for the info DllHell. I will look into it.
J.A. Coutts
-
Re: VB6 - Yet Another Web Server
I need some clarification on the use of "RespExtraHeaders". This is one of several variables (RespStatus, RespStatusText, RespMIME, RespExtraHeaders, and RespBody) that can get a return value in the DynamicRequest event because they are declared ByRef. DllHell has already demonstrated to us the use of RespStatus, RespStatusText, RespMIME, & RespBody, but the use of RespExtraHeaders evades me.
In the SendResponse Subroutine, it appears that if ExtraHeaders contains "Last-Modified:", it would get added twice. How and when are ExtraHeaders utilized?
J.A. Coutts
-
Re: VB6 - Yet Another Web Server
Quote:
Originally Posted by
couttsj
I need some clarification on the use of "RespExtraHeaders". This is one of several variables (RespStatus, RespStatusText, RespMIME, RespExtraHeaders, and RespBody) that can get a return value in the DynamicRequest event because they are declared ByRef. DllHell has already demonstrated to us the use of RespStatus, RespStatusText, RespMIME, & RespBody, but the use of RespExtraHeaders evades me.
you can use it to add cookies. you can also use it to send data that isn't part of the header spec (see the "x-" headers in this list https://developer.mozilla.org/en-US/...esponse_header)
Quote:
Originally Posted by
couttsj
In the SendResponse Subroutine, it appears that if ExtraHeaders contains "Last-Modified:", it would get added twice. How and when are ExtraHeaders utilized?
it would get added twice. you want to use the extraheaders for things that are not already included: things specific to your server impl or your application.
important to note that the extraheaders are only useful if the receiving end knows how to handle them and are typically used for debugging or to provide additional info to developers about the request. For example, you may have a "X-powered-by: yourapp.exe/1.0.2" to know what version of your exe is on the other end.
-
Re: VB6 - Yet Another Web Server
Quote:
Originally Posted by
DllHell
you can use it to add cookies. you can also use it to send data that isn't part of the header spec (see the "x-" headers in this list
https://developer.mozilla.org/en-US/...esponse_header)
important to note that the extraheaders are only useful if the receiving end knows how to handle them and are typically used for debugging or to provide additional info to developers about the request. For example, you may have a "X-powered-by: yourapp.exe/1.0.2" to know what version of your exe is on the other end.
Once again, thank you for the info. You seem to be quite knowledgeable with reference to HTML code. What I hear you saying about this particular item, is that it is not really necessary to support it. I am trying to streamline the code in advance of introducing encryption. I am not trying to implement full web server support. One of the things I had to do was receive and send data as byte values instead of string. Encrypted data uses the full ANSI values rather than ACSII.
J.A. Coutts
-
Re: VB6 - Yet Another Web Server
Hello
I have problems receiving the parameters in the urls that have #
example:
Code:
https://asdasd.pktriot.net/webhook_twitch#access_token=asdasdasdasdasd&scope=user%3Aedit+user%3Aread%3Aemail+channel%3Aread%3Asubscriptions&state=test&token_type=bearer
I only receive: webhook_twitch
but if I change it for ? it works perfectly
Code:
https://asdasd.pktriot.net/webhook_twitch?access_token=asdasdasdasdasd&scope=user%3Aedit+user%3Aread%3Aemail+channel%3Aread%3Asubscriptions&state=test&token_type=bearer
it's a twitch webhook
Greetings
sorry for my english I use a translator
-
Re: VB6 - Yet Another Web Server
The URL and the parameters should be separated with a ? not with a #
https://en.wikipedia.org/wiki/Query_string
-
Re: VB6 - Yet Another Web Server
Everything past # is URL fragment. URL fragments are *not* sent to the server and are handled client-side only.
https://en.wikipedia.org/wiki/URI_fragment
cheers,
</wqw>