Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.forth > #43847 > unrolled thread

Writing (web)servers in Forth

Started byJulian Fondren <julian.fondren@gmail.com>
First post2015-11-26 13:14 -0800
Last post2015-11-27 08:33 -0800
Articles 4 — 3 participants

Back to article view | Back to comp.lang.forth


Contents

  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

#43847 — Writing (web)servers in Forth

FromJulian Fondren <julian.fondren@gmail.com>
Date2015-11-26 13:14 -0800
SubjectWriting (web)servers in Forth
Message-ID<4e8b252d-3dd6-4e1d-b9b6-0457b8dadaf3@googlegroups.com>
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

[toc] | [next] | [standalone]


#43857

FromLars Brinkhoff <lars.spam@nocrew.org>
Date2015-11-27 07:47 +0100
Message-ID<86fuzsrl9m.fsf@vps34351.public.cloudvps.com>
In reply to#43847
Julian Fondren <julian.fondren@gmail.com> writes:
>   [defined] client 0= [if] abort

I suggest [undefined].

[toc] | [prev] | [next] | [standalone]


#43867

FromHowerd <howerdo@yahoo.co.uk>
Date2015-11-27 08:29 -0800
Message-ID<ff214e2d-d233-49ea-8e33-a574000ef255@googlegroups.com>
In reply to#43847
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. 

[toc] | [prev] | [next] | [standalone]


#43868

FromHowerd <howerdo@yahoo.co.uk>
Date2015-11-27 08:33 -0800
Message-ID<3f524700-e761-4dd5-927e-e7d82dd57d8b@googlegroups.com>
In reply to#43847
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

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.forth


csiph-web