Proxy learning exercise

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Proxy learning exercise

Stephen-71
Hi All,

As a learning exercise I thought I would extend the Wiki Chatserver
(http://sblinn.jottit.com/GNU_Smalltalk_SimpleEcho_TCP_Server) to create
a simple forwarding proxy server.

I'm hoping someone can give me a hand...

Simply extending the example of the chat server could result in
something like the code below...

run
   | source |
   [
     ss waitForConnection.
     source := (ss accept).
     [self handleSocket: source] fork
   ] repeat
!!

"Instance method to handle each connection"
!SimpleProxyServer methodsFor: 'handling'!

handleSocket: source
   | msg dest |
   dest := (TCP.Socket remote: amailserver port: 25).
   [
     "handle source data"
     msg := (source nextHunk).
     msg displayOn: dest.
     dest flush.
     "handle dest data"
     msg := (dest nextHunk).
     msg displayOn: source.
     source flush.
   ] repeat
!!

-----------------------

But the code above wouldn't work since it blocks on input from source
and there may be data in the dest Stream. So I modified handleSocket to
look like this...

run
   | source |
   [
     ss waitForConnection.
     source := (ss accept).
     [self handleSocket: source] fork
   ] repeat
!!

"Instance method to handle each connection"
!SimpleProxyServer methodsFor: 'handling'!

handleSocket: source
   | msg dest |
   dest := (TCP.Socket remote: amailserver port: 25).
   [
     "handle source data"
     (source available) ifTrue: [
             msg := (source nextHunk).
             msg displayOn: dest.
             dest flush ].
     "handle dest data"
     (dest available) ifTrue: [
                 msg := (dest nextHunk).
                 msg displayOn: source.
                 source flush ].
   ] repeat
!!
  -----------------------

And this is where I'd like a hand. The code above works as a relay but
isn't suitable because:
  1. It doesn't close the client (source) socket when the server (dest)
socket closes.
  2. The loop is CPU intensive

I would like to know how to block until _either_ the "source" socket,
or, the "dest" socket has some data.
Also, if either of the source or dest sockets is closed then the thread
should terminate and close any sockets owned by the thread.

Thanks
Stephen


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: Proxy learning exercise

Paolo Bonzini-2

>  1. It doesn't close the client (source) socket when the server (dest)
> socket closes.
>  2. The loop is CPU intensive

You can use two processes.

p1 := [
     source ensureReadable.
     source isPeerAlive ifFalse: [ p1 terminate. p2 terminate ].
     dest nextPutAllFlush: source nextHunk ] newProcess.
p2 := [
     dest ensureReadable.
     dest isPeerAlive ifFalse: [ p1 terminate. p2 terminate ].
     source nextPutAllFlush: dest nextHunk ] newProcess.

p1 resume.
p2 resume


In 3.0c there is no #nextHunk, so you can use StreamSocket to remove the
write buffering and do

p1 := [
     source ensureReadable.
     source isPeerAlive ifFalse: [ p1 terminate. p2 terminate ].
     source nextAvailablePutAllOn: dest ] newProcess.
p2 := [
     dest ensureReadable.
     dest isPeerAlive ifFalse: [ p1 terminate. p2 terminate ].
     dest nextAvailablePutAllOn: source ] newProcess.

p1 resume.
p2 resume


This has better performance because it does not do unnecessary copies
and creation of objects.

Paolo


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: Proxy learning exercise

Stephen-71
In reply to this post by Stephen-71
Paolo Bonzini wrote:

 > You can use two processes.
 >
 > p1 := [
 >     source ensureReadable.
 >     source isPeerAlive ifFalse: [ p1 terminate. p2 terminate ].
 >     dest nextPutAllFlush: source nextHunk ] newProcess.
 > p2 := [
 >     dest ensureReadable.
 >     dest isPeerAlive ifFalse: [ p1 terminate. p2 terminate ].
 >     source nextPutAllFlush: dest nextHunk ] newProcess.
 >
 > p1 resume.
 > p2 resume
 >
 >
 > In 3.0c there is no #nextHunk, so you can use StreamSocket to remove the
 > write buffering and do
 >
 > p1 := [
 >     source ensureReadable.
 >     source isPeerAlive ifFalse: [ p1 terminate. p2 terminate ].
 >     source nextAvailablePutAllOn: dest ] newProcess.
 > p2 := [
 >     dest ensureReadable.
 >     dest isPeerAlive ifFalse: [ p1 terminate. p2 terminate ].
 >     dest nextAvailablePutAllOn: source ] newProcess.
 >
 > p1 resume.
 > p2 resume
 >
 >
 > This has better performance because it does not do unnecessary copies
 > and creation of objects.
 >
 > Paolo
 >

Thank you Paolo.

I've found this solution extremely helpful. Thanks for putting the "new"
solution in as well - that was going to be the next part of the exercise!

Stephen


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk