Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.forth > #43868
| Newsgroups | comp.lang.forth |
|---|---|
| Date | 2015-11-27 08:33 -0800 |
| References | <4e8b252d-3dd6-4e1d-b9b6-0457b8dadaf3@googlegroups.com> |
| Message-ID | <3f524700-e761-4dd5-927e-e7d82dd57d8b@googlegroups.com> (permalink) |
| Subject | Re: Writing (web)servers in Forth |
| From | Howerd <howerdo@yahoo.co.uk> |
On Thursday, 26 November 2015 21:14:42 UTC, Julian Fondren wrote:
> Hello, clf,
>
> Yes, it's happened again. Someone has written another webserver in
> Forth. I know, it's literally the first thing anyone does. Can you
> all, possibly, get to work doing something useful instead of putting
> another webserver on GitHub?
>
> I confess, this time it's me who wrote one. You can find it here:
> https://bitbucket.org/demonview/forth-httpd
>
> The core of this repository is an evolution of the pollserver I
> supplied in a previous post to this group. I'll just put the entirety
> of pollserver.fs here:
>
> [defined] client 0= [if] abort
> class client
> : init ( pollfd -- ) remember pollfd and zero out any data structures, etc. ;
> : connected ( -- ) pollfd now has a live socket ;
> : reap ( -- ) clean up, log death, etc. pollfd will be killed afterwards ;
> : update ( -- ) you have input! (or may now write). deal with it ;
> end-class
> [then]
>
> class pollserver
> max-clients 1+ pollfd builds[] pollfds
> max-clients 1+ client builds[] clients
>
> : init-with ( socket -- )
> max-clients 1+ 0 do
> i pollfds init
> i pollfds addr i clients socket !
> loop max-clients pollfds fd ! ;
> : init ( port -- ) server-socket init-with ;
>
> : server ( -- server-socket ) max-clients pollfds fd @ ;
> : accept? ( -- f ) max-clients pollfds ready? ;
> : size ( -- n ) 0 max-clients 0 do i pollfds alive? if 1+ then loop ;
>
> DEFER: too-many-connections ( socket -- ) close-file drop ;
> : add ( socket -- )
> max-clients 0 do
> i pollfds dead? if
> i pollfds fd !
> i clients connected
> unloop exit then loop too-many-connections ;
>
> : /accepting ( -- ) accept? if /accept accepted add then ;
> : /updating ( -- )
> max-clients 0 do
> i pollfds alive? if
> i pollfds dying? if i clients reap pollfds kill
> else i pollfds ready? if i clients update
> then then then loop ;
>
> \ 'poll' in these definitions is the syscall
> : poll-for ( timeout -- ior ) >r 0 pollfds addr max-clients 1+ r> poll ;
> : poll ( -- ior ) 0 pollfds addr max-clients 1+ -1 poll ;
>
> \ 'poll' in this definition is the method
> : /serving ( -- )
> begin poll 0< abort" poll failed"
> /accepting /updating again ;
> end-class
>
> That's it. It literally has nothing at all to do with HTTP, or
> document roots, or request routing, or 404 errors. It's two arrays,
> one fed to poll(), and a mirroring array of client objects. Which
> are defined elsewhere. pollserver doesn't even know if clients are
> waiting for sockets to be ready for input vs. output. Rather than tie
> server logic intimately with with this structure, the direction I was
> going in that earlier post, the logic is handled completely by the
> client objects.
>
> The repo has a bunch of other stuff, including a README that goes into
> other details.
>
> I was impressed, recently, by the Node.js community. You could install
> a standalone webserver that would serve static files from the directory
> you ran it from, and which had a few options. If you wanted to do a
> little more that than, you can do it nearly as rapidly with the Express
> framework. If you wanted to get a server running and keep it running,
> 'forever' was an aptly-named tool for that.
>
> I wasn't as impressed by Node.js itself. For two reasons: my
> background is in system administration, and I'd used Erlang before.
>
> So I wrote a little webserver-inna-script for Erlang, too:
>
> https://bitbucket.org/demonview/http-server
>
> And then I found some cheap micro-VPSes, and figured Forth would be
> more appropriate for these environments.
>
> Here are a few routes in Express:
>
> var counter = 0
> app.get('/counter.fs', function(req, res) {
> res.type('text/plain')
> res.status(200)
> res.send(String(++counter))
> })
>
> app.get('/ip.fs', function(req, res) {
> res.type('text/plain')
> res.status(200)
> res.send(req.ip)
> })
>
> And the same routes in Forth:
>
> variable counter
> simple :: counter-rule
> if: m" /counter.fs" ;
> do: counter ++ counter @ 0 .r ;
>
> simple :: ip-rule
> if: m" /ip.fs" ;
> do: client peer @ .ip ;
>
> That's not so bad, is it
>
> Well I'm not a big fan of the :: syntax, but I had a need to create a
> statically allocated object and also put its address on the stack, and
> SWOOP can do one or the other.
>
> It's a work in progress.
>
> Cheers,
> Julian
Hi Julian,
Nice one :-)
> Rather than tie
> server logic intimately with with this structure, the direction I was
> going in that earlier post, the logic is handled completely by the
> client objects.
I like this. It sounds like you are following the Forth principle of solving a simpler problem... And if if looks like a webserver, walks like a webserver and sounds like a webserver, then it *is* a webserver.
Best regards,
Howerd
Back to comp.lang.forth | Previous | Next — Previous in thread | Find similar | Unroll thread
Writing (web)servers in Forth Julian Fondren <julian.fondren@gmail.com> - 2015-11-26 13:14 -0800 Re: Writing (web)servers in Forth Lars Brinkhoff <lars.spam@nocrew.org> - 2015-11-27 07:47 +0100 Re: Writing (web)servers in Forth Howerd <howerdo@yahoo.co.uk> - 2015-11-27 08:29 -0800 Re: Writing (web)servers in Forth Howerd <howerdo@yahoo.co.uk> - 2015-11-27 08:33 -0800
csiph-web