Skip to content

Commit

Permalink
Merge pull request #10 from juliendelplanque/tcp-server
Browse files Browse the repository at this point in the history
tcp-server
  • Loading branch information
juliendelplanque authored Oct 25, 2019
2 parents 190dc62 + 1b48bc8 commit 18ca38f
Show file tree
Hide file tree
Showing 7 changed files with 266 additions and 87 deletions.
13 changes: 13 additions & 0 deletions src/JRPC-Client/JRPCClient.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@ JRPCClient class >> http: aString [
^ JRPCHTTPClient url: aString
]

{ #category : #'instance creation' }
JRPCClient class >> tcp [
^ JRPCTCPClient new
]

{ #category : #'instance creation' }
JRPCClient class >> tcpForAddress: aSocketAddress port: anInteger [
^ JRPCTCPClient new
address: aSocketAddress;
port: anInteger;
yourself
]

{ #category : #api }
JRPCClient >> callMethod: aString arguments: anObject withId: anInteger [
"Call a method which takes arguments."
Expand Down
58 changes: 58 additions & 0 deletions src/JRPC-Client/JRPCTCPClient.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"
I am a client for JSON-RPC 2.0 over TCP.
Internally, I use a Socket.
"
Class {
#name : #JRPCTCPClient,
#superclass : #JRPCClient,
#instVars : [
'address',
'port'
],
#category : #'JRPC-Client'
}

{ #category : #defaults }
JRPCTCPClient class >> defaultPort [
^ 4000
]

{ #category : #accessing }
JRPCTCPClient >> address [
^ address ifNil: [ address := NetNameResolver localHostAddress ]
]

{ #category : #accessing }
JRPCTCPClient >> address: anObject [
address := anObject
]

{ #category : #accessing }
JRPCTCPClient >> defaultPort [
^ self class defaultPort
]

{ #category : #accessing }
JRPCTCPClient >> port [
^ port ifNil: [ port := self defaultPort ]
]

{ #category : #accessing }
JRPCTCPClient >> port: anObject [
port := anObject
]

{ #category : #'private - sending' }
JRPCTCPClient >> sendRequest: aJRPCRequestObject [
| socket result |
socket := Socket newTCP.
socket
connectTo: self address
port: self port.
socket
sendData: (self convertJRPCJsonableObjectToJSON: aJRPCRequestObject asJRPCJSON).
result := socket receiveData.
socket closeAndDestroy.
^ self parseSupposedJRPCMessageObjectFromString: result
]
5 changes: 5 additions & 0 deletions src/JRPC-Server/JRPCServer.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ JRPCServer class >> http [
^ JRPCHTTPServer new
]

{ #category : #'instance creation' }
JRPCServer class >> tcp [
^ JRPCTCPServer new
]

{ #category : #'handlers management' }
JRPCServer >> addHandlerNamed: aString block: aBlock [
namesToHandlersDict
Expand Down
66 changes: 66 additions & 0 deletions src/JRPC-Server/JRPCTCPServer.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"
I am a JSON-RPC 2.0 server running over the TCP protocol.
Internally, I use a Socket.
"
Class {
#name : #JRPCTCPServer,
#superclass : #JRPCServer,
#instVars : [
'port',
'tcpServer',
'serverLoop',
'process'
],
#category : #'JRPC-Server'
}

{ #category : #defaults }
JRPCTCPServer class >> defaultPort [
^ 4000
]

{ #category : #defaults }
JRPCTCPServer >> defaultPort [
^ self class defaultPort
]

{ #category : #accessing }
JRPCTCPServer >> port [
^ port ifNil: [ port := self defaultPort ]
]

{ #category : #accessing }
JRPCTCPServer >> port: anObject [
port := anObject
]

{ #category : #starting }
JRPCTCPServer >> start [
tcpServer := Socket newTCP.
tcpServer listenOn: self port backlogSize: 10.
serverLoop := true.
process := [ [ serverLoop ]
whileTrue: [ (tcpServer waitForAcceptFor: 60)
ifNotNil: [ :clientSocket |
[ [
| data |
data := clientSocket receiveData.
clientSocket sendData: (self handleJSON: data contents) ]
ensure: [ clientSocket closeAndDestroy ] ]
forkAt: Processor lowIOPriority
named: 'JRPC TCP connection' ] ] ]
forkAt: Processor highIOPriority
named: 'JRPC TCP server'
]

{ #category : #stopping }
JRPCTCPServer >> stop [
serverLoop := false.
tcpServer closeAndDestroy.

"Ensure process finishes."
process isTerminated
ifTrue: [ ^ self ].
process terminate
]
101 changes: 101 additions & 0 deletions src/JRPC-Tests/JRPCAbstractServerTest.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
Class {
#name : #JRPCAbstractServerTest,
#superclass : #TestCase,
#instVars : [
'server'
],
#category : #'JRPC-Tests'
}

{ #category : #testing }
JRPCAbstractServerTest class >> isAbstract [
^ self = JRPCAbstractServerTest
]

{ #category : #private }
JRPCAbstractServerTest >> checkPortAvailability [

[ ( ZnNetworkingUtils serverSocketOn: self port ) close ]
on: Error
do: [ :error | self fail: ( 'Port <1p> is not available' expandMacrosWith: self port ) ]
]

{ #category : #'instance creation' }
JRPCAbstractServerTest >> newJRPCClient [
^ self subclassResponsibility
]

{ #category : #private }
JRPCAbstractServerTest >> port [

^ 7777
]

{ #category : #running }
JRPCAbstractServerTest >> tearDown [

server ifNotNil: [ server stop ].
server := nil.
super tearDown
]

{ #category : #accessing }
JRPCAbstractServerTest >> testNotification [

| notificationCount |

notificationCount := 0.
server addHandlerNamed: 'mail_sent' block: [ notificationCount := notificationCount + 1 ].

self newJRPCClient notifyMethod: 'mail_sent'.

self assert: notificationCount equals: 1
]

{ #category : #accessing }
JRPCAbstractServerTest >> testNotificationWithInvalidMethod [

| notificationCount |

notificationCount := 0.
server addHandlerNamed: 'mail_sent' block: [ notificationCount := notificationCount + 1 ].

self newJRPCClient notifyMethod: 'invalid'.

self assert: notificationCount equals: 0
]

{ #category : #accessing }
JRPCAbstractServerTest >> testRequestOnInvalidEndpoint [

| httpClient failed |

server addHandlerNamed: 'sum' block: [ :a :b | a + b ].
failed := false.

httpClient := JRPCClient http: ( 'http://localhost' asUrl port: self port ) / 'bad'.
httpClient
ifFail: [ :error |
failed := true.
'{}'
].
self
should: [ httpClient callMethod: 'sum' arguments: #(1 3) withId: 1 ] raise: JRPCIncorrectJSON;
assert: failed
]

{ #category : #accessing }
JRPCAbstractServerTest >> testRequestWithoutParameters [

server addHandlerNamed: 'zero' block: [ 0 ].

self assert: ( self newJRPCClient callMethod: 'zero' withId: 3 ) result equals: 0
]

{ #category : #accessing }
JRPCAbstractServerTest >> testValidRequest [

server addHandlerNamed: 'sum' block: [ :a :b | a + b ].

self assert: ( self newJRPCClient callMethod: 'sum' arguments: #(1 3) withId: 1 ) result equals: 4
]
88 changes: 1 addition & 87 deletions src/JRPC-Tests/JRPCHttpServerTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,10 @@ I'm a test for JSON RPC over HTTP transport
"
Class {
#name : #JRPCHttpServerTest,
#superclass : #TestCase,
#instVars : [
'server'
],
#superclass : #JRPCAbstractServerTest,
#category : #'JRPC-Tests'
}

{ #category : #private }
JRPCHttpServerTest >> checkPortAvailability [

[ ( ZnNetworkingUtils serverSocketOn: self port ) close ]
on: Error
do: [ :error | self fail: ( 'Port <1p> is not available' expandMacrosWith: self port ) ]
]

{ #category : #private }
JRPCHttpServerTest >> newJRPCClient [

Expand All @@ -26,12 +15,6 @@ JRPCHttpServerTest >> newJRPCClient [
yourself
]

{ #category : #private }
JRPCHttpServerTest >> port [

^ 7777
]

{ #category : #running }
JRPCHttpServerTest >> setUp [

Expand All @@ -46,72 +29,3 @@ JRPCHttpServerTest >> setUp [
assert: server debugMode.
server start
]

{ #category : #running }
JRPCHttpServerTest >> tearDown [

server ifNotNil: [ server stop ].
server := nil.
super tearDown
]

{ #category : #accessing }
JRPCHttpServerTest >> testNotification [

| notificationCount |

notificationCount := 0.
server addHandlerNamed: 'mail_sent' block: [ notificationCount := notificationCount + 1 ].

self newJRPCClient notifyMethod: 'mail_sent'.

self assert: notificationCount equals: 1
]

{ #category : #accessing }
JRPCHttpServerTest >> testNotificationWithInvalidMethod [

| notificationCount |

notificationCount := 0.
server addHandlerNamed: 'mail_sent' block: [ notificationCount := notificationCount + 1 ].

self newJRPCClient notifyMethod: 'invalid'.

self assert: notificationCount equals: 0
]

{ #category : #accessing }
JRPCHttpServerTest >> testRequestOnInvalidEndpoint [

| httpClient failed |

server addHandlerNamed: 'sum' block: [ :a :b | a + b ].
failed := false.

httpClient := JRPCClient http: ( 'http://localhost' asUrl port: self port ) / 'bad'.
httpClient
ifFail: [ :error |
failed := true.
'{}'
].
self
should: [ httpClient callMethod: 'sum' arguments: #(1 3) withId: 1 ] raise: JRPCIncorrectJSON;
assert: failed
]

{ #category : #accessing }
JRPCHttpServerTest >> testRequestWithoutParameters [

server addHandlerNamed: 'zero' block: [ 0 ].

self assert: ( self newJRPCClient callMethod: 'zero' withId: 3 ) result equals: 0
]

{ #category : #accessing }
JRPCHttpServerTest >> testValidRequest [

server addHandlerNamed: 'sum' block: [ :a :b | a + b ].

self assert: ( self newJRPCClient callMethod: 'sum' arguments: #(1 3) withId: 1 ) result equals: 4
]
22 changes: 22 additions & 0 deletions src/JRPC-Tests/JRPCTCPServerTest.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Class {
#name : #JRPCTCPServerTest,
#superclass : #JRPCAbstractServerTest,
#category : #'JRPC-Tests'
}

{ #category : #'instance creation' }
JRPCTCPServerTest >> newJRPCClient [
^ JRPCClient tcpForAddress: NetNameResolver localHostAddress port: self port
]

{ #category : #running }
JRPCTCPServerTest >> setUp [

super setUp.
self checkPortAvailability.
server := JRPCServer tcp.
server
port: self port.
self assert: server port equals: self port.
server start
]

0 comments on commit 18ca38f

Please sign in to comment.