TCP
Pode has a generic TCP server type inbuilt. Unlike the other server types, the TCP server lets you build pretty much anything over TCP. The TCP server supports non-TLS, as well as implicit and explicit TLS endpoints.
Where a web server using Routes, a TCP server uses Verbs - think of them like "commands" or "phrases". Requests sent will be matched to a Verb, and that Verb's logic invoked (more info below).
Important
This server type is still in the early days, so if you find any bugs or have any suggestions, please feel free to raise them over on GitHub or Discord! 😄
Usage
To create a TCP server you need to create an appropriate Endpoint with the TCP protocol, plus some Verbs.
The following example will create a TCP endpoint listening on localhost:9000
, and creates a simple Verb to respond back to a connected client:
Start-PodeServer {
Add-PodeEndpoint -Address localhost -Port 9000 -Protocol Tcp -CRLFMessageEnd -Acknowledge 'Welcome!'
Add-PodeVerb -Verb 'HELLO' -ScriptBlock {
Write-PodeTcpClient -Message 'HI'
}
}
Note
If you use telnet to test/send data to a TCP server, ensure that the Endpoint has the -CRLFMessageEnd
switch. If missed, the server will receive data in single characters.
Start the above server, and then in a different terminal start telnet and send "HELLO" - pressing Enter to send:
$> telnet localhost 9000
S> Welcome!
C> HELLO
S> HI
When you send "HELLO", the server will respond with "HI". If you can't use telnet, then there's a quick test script below to use.
Important
If you're using telnet to send data, backspaces will be recorded as sent data.
Acknowledge
If you'd like to send an initial message to clients as soon as they connect, you can set an -Acknowledge
message on the Endpoint:
Add-PodeEndpoint -Address localhost -Port 9000 -Protocol Tcp -CRLFMessageEnd -Acknowledge 'Welcome!'
If you connect via telnet now:
$> telnet localhost 9000
S> Welcome!
Verbs
Verbs work like Routes, whereby you can setup many of them and Pode will invoke the Verb's logic that best matches the data sent. Data sent usually should be in a structured format, such as <COMMAND> <PARAMETERS>
- akin to SMTP and FTP. However, data can be unstructured, and this is where the wildcard Verb comes in handy.
To create a Verb you use Add-PodeVerb
:
Add-PodeVerb -Verb 'HELLO' -ScriptBlock {
Write-PodeTcpClient -Message 'HI'
}
Parameters
Also similar to Routes, Verbs support parameters in the format :<name>
. These parameters will be available in $TcpEvent.Parameters
:
Add-PodeVerb -Verb 'HELLO :username' -ScriptBlock {
Write-PodeTcpClient -Message "HI, $($TcpEvent.Parameters.username)"
}
Wildcard
The wildcard Verb, denoted via -Verb *
, is a catch-all for any data that doesn't match other defined Verbs. You could use this Verb to write back to the client that they sent invalid data:
Add-PodeVerb -Verb * -ScriptBlock {
Write-PodeTcpClient -Message 'Unrecognised verb sent'
}
Or, you can access the data sent by the client via either $TcpEvent.Request.RawBody
or $TcpEvent.Request.Body
. RawBody is a byte array of the data, where as Body is a UTF8 decoded string of the RawBody. This should allow you to then parse any freestyle data as you see fit, such as a simple web server:
Start-PodeServer {
Add-PodeEndpoint -Address localhost -Port 9000 -Protocol Tcp
Add-PodeVerb -Verb * -Close -ScriptBlock {
$TcpEvent.Request.Body | Out-Default
Write-PodeTcpClient -Message "HTTP/1.1 200 `r`nConnection: close`r`n`r`n<b>Hello, there</b>"
}
}
Running the above server and navigating to http://localhost:9000
will greet you with a message. The raw data sent by the browser will be display on the terminal; this could be parsed to do different things.
SSL Upgrade
If you're using explicit TLS on your TCP server, then at some point you'll want to upgrade the connection to using SSL.
There are two ways of achieving this; one is to use a simple command verb, and the -UpgradeToSsl
switch:
Add-PodeVerb -Verb 'STARTTLS' -UpgradeToSsl
The second way is to call the upgrade method directly:
Add-PodeVerb -Verb 'STARTTLS' -ScriptBlock {
Write-PodeTcpClient -Message 'TLS GO AHEAD'
$TcpEvent.Request.UpgradeToSSL()
}
Close
At some point you'll likely need to close the connection from the server side. There are two ways of achieving this; one is to use a simple command verb, and the -Close
switch:
Add-PodeVerb -Verb 'QUIT' -Close
The second way is to call Close-PodeTcpClient
directly:
Add-PodeVerb -Verb 'QUIT' -ScriptBlock {
Write-PodeTcpClient -Message 'Bye!'
Close-PodeTcpClient
}
Read Data
In the above examples you've seen Write-PodeTcpClient
being used. This function simply sends data back to a connected client, but what if we want to read data? Sometimes, instead of ending a Verb's logic and letting Pode wait for the next data, you might want to receive this data yourself in a Verb. For this there is Read-PodeTcpClient
:
Add-PodeVerb -Verb 'HELLO' -ScriptBlock {
Write-PodeTcpClient -Message "Hi! What's your name?"
$name = Read-PodeTcpClient -CRLFMessageEnd
Write-PodeTcpClient -Message "Hi, $($name)!"
}
TLS
You can enable TLS for your endpoints by supplying the normal relevant certificates parameters on Add-PodeEndpoint
, and setting the -Protocol
to Tcps
. You can also toggle between implicit and explicit TLS by using the -TlsMode
parameter.
By default the TLS mode is implicit, and the default port for implicit TLS is 9002; for explicit TLS it's 9003. These can of course be customised using -Port
.
Start-PodeServer {
Add-PodeEndpoint -Address * -Protocol Tcps -SelfSigned -TlsMode Explicit
Add-PodeEndpoint -Address * -Protocol Tcps -SelfSigned -TlsMode Implicit
Add-PodeVerb -Verb 'HELLO' -ScriptBlock {
Write-PodeTcpClient -Message 'HI'
}
}
Objects
TcpEvent
Verbs will be passed the $TcpEvent
object, that contains the Request, Response, and other properties:
Name | Type | Description |
---|---|---|
Request | object | The raw Request object |
Response | object | The raw Response object |
Lockable | hashtable | A synchronized hashtable that can be used with Lock-PodeObject |
Endpoint | hashtable | Contains the Address and Protocol of the endpoint being hit - such as "pode.example.com" or "127.0.0.2", or HTTP or HTTPS for the Protocol |
Parameters | hashtable | Contains the parsed parameter values from the Verb's path |
Timestamp | datetime | The current date and time of the Request |
Test Send
The following function can be used to test sending messages to a TCP server. This is a modified version of the function found here.
function Send-TCPMessage($Endpoint, $Port, $Message) {
# Setup connection
$Address = [System.Net.IPAddress]::Parse([System.Net.Dns]::GetHostAddresses($EndPoint))
$Socket = [System.Net.Sockets.TcpClient]::new($Address, $Port)
# Setup stream writer
$Stream = $Socket.GetStream()
$Writer = [System.IO.StreamWriter]::new($Stream)
# Write message to stream
$Writer.WriteLine($Message)
$Writer.Flush()
# Close connection and stream
Start-Sleep -Seconds 1
$Stream.Close()
$Socket.Close()
}
ie:
Send-TCPMessage -Port 9000 -EndPoint 127.0.0.1 -Message "HELLO"
Note
If you have the -CRLFMessageEnd
switch specified, you'll need to add `r`n
to the end of the -Message