Compare commits
10 Commits
aa82b32397
...
33b412dd85
Author | SHA1 | Date | |
---|---|---|---|
33b412dd85 | |||
a1cc664f41 | |||
61de51c0ae | |||
adda9747be | |||
440d9fb579 | |||
ad2d978fb4 | |||
3551d0f4dc | |||
baf4e882db | |||
e8def487d4 | |||
f137795414 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
*.exe
|
*.exe
|
||||||
hsp-go
|
hsp-go
|
||||||
|
|
||||||
|
116
cmd/hspnet.go
116
cmd/hspnet.go
@ -4,9 +4,10 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/LandaMm/hsp-go/hsp"
|
"github.com/LandaMm/hsp-go/hsp"
|
||||||
@ -16,7 +17,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Header struct {
|
type Header struct {
|
||||||
Key string
|
Key string
|
||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ func (hl *HeaderList) Set(arg string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
hl.Headers = append(hl.Headers, Header{
|
hl.Headers = append(hl.Headers, Header{
|
||||||
Key: key,
|
Key: key,
|
||||||
Value: value,
|
Value: value,
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
@ -89,7 +90,7 @@ func PrintPacket(pkt *hsp.Packet) error {
|
|||||||
default:
|
default:
|
||||||
fmt.Printf("ERR: Unsupported data format: %s\n", df.String())
|
fmt.Printf("ERR: Unsupported data format: %s\n", df.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +119,7 @@ func StartServer(addr *hsp.Adddress) {
|
|||||||
srv := server.NewServer(*addr)
|
srv := server.NewServer(*addr)
|
||||||
fmt.Printf("Server created on address: %s\n", srv.Addr.String())
|
fmt.Printf("Server created on address: %s\n", srv.Addr.String())
|
||||||
|
|
||||||
handler := make(chan net.Conn, 1)
|
handler := make(chan *hsp.Connection, 1)
|
||||||
|
|
||||||
router := server.NewRouter()
|
router := server.NewRouter()
|
||||||
|
|
||||||
@ -154,11 +155,8 @@ func StartServer(addr *hsp.Adddress) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartSession(addr *hsp.Adddress, df *hsp.DataFormat, headerList *HeaderList) {
|
func StartSession(options *client.ClientOptions) {
|
||||||
url := addr.String() + addr.Route
|
c := client.NewClient(options)
|
||||||
fmt.Println("Starting session on", url)
|
|
||||||
|
|
||||||
c := client.NewClient(headerList.Map())
|
|
||||||
|
|
||||||
rl, err := readline.New("> ")
|
rl, err := readline.New("> ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -172,32 +170,67 @@ func StartSession(addr *hsp.Adddress, df *hsp.DataFormat, headerList *HeaderList
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
rl.SetPrompt("Route > ")
|
||||||
|
route, err := rl.Readline()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
rl.SetPrompt("Data > ")
|
||||||
line, err := rl.Readline()
|
line, err := rl.Readline()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
var rsp *hsp.Response
|
var rsp *hsp.Response
|
||||||
|
var rerr error
|
||||||
|
|
||||||
switch df.Format {
|
if strings.HasPrefix(line, "/file") {
|
||||||
case hsp.DF_TEXT:
|
what := strings.TrimLeft(line, "/file")
|
||||||
rsp, err = c.SendText(url, line)
|
isJson := false
|
||||||
case hsp.DF_JSON:
|
var filename string
|
||||||
var data any
|
if strings.HasPrefix(what, ":json ") {
|
||||||
err = json.Unmarshal([]byte(line), &data)
|
isJson = true
|
||||||
if err != nil {
|
filename = strings.TrimLeft(what, ":json ")
|
||||||
fmt.Println("ERR: Invalid JSON for request:", err)
|
} else {
|
||||||
|
filename = strings.TrimLeft(what, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
rsp, err = c.SendJson(url, data)
|
file, err := os.Open(filename)
|
||||||
case hsp.DF_BYTES:
|
if err != nil {
|
||||||
rsp, err = c.SendBytes(url, []byte(line))
|
fmt.Printf("ERR: Failed to open file '%s': %v\n", filename, err)
|
||||||
default:
|
continue
|
||||||
fmt.Println("ERR: Unsupported data format:", df.Format)
|
}
|
||||||
return
|
|
||||||
|
if !isJson {
|
||||||
|
buf, err := io.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ERR: Failed to read from file '%s': %v\n", filename, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rsp, err = c.SendBytes(route, buf)
|
||||||
|
} else {
|
||||||
|
var data any
|
||||||
|
|
||||||
|
decoder := json.NewDecoder(file)
|
||||||
|
err = decoder.Decode(&data)
|
||||||
|
if err == nil {
|
||||||
|
rsp, err = c.SendJson(route, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if strings.HasPrefix(line, "/json ") {
|
||||||
|
var data any
|
||||||
|
err = json.Unmarshal([]byte(strings.TrimLeft(line, "/json ")), &data)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("ERR: Invalid JSON for request:", err)
|
||||||
|
} else {
|
||||||
|
rsp, err = c.SendJson(route, data)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rsp, rerr = c.SendText(route, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if rerr != nil {
|
||||||
fmt.Println("ERR: Failed to send text to server:", err)
|
fmt.Println("ERR: Failed to send text to server:", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -216,17 +249,16 @@ func main() {
|
|||||||
var service string
|
var service string
|
||||||
var address string
|
var address string
|
||||||
|
|
||||||
var dataFormat string
|
|
||||||
|
|
||||||
var headerList HeaderList
|
var headerList HeaderList
|
||||||
|
var auth string
|
||||||
|
|
||||||
flag.StringVar(&host, "host", "localhost", "specify server host")
|
flag.StringVar(&host, "host", "localhost", "specify server host")
|
||||||
flag.StringVar(&service, "port", "998", "specify server port")
|
flag.StringVar(&service, "port", "998", "specify server port")
|
||||||
flag.StringVar(&address, "addr", "localhost:998", "specify target address")
|
flag.StringVar(&address, "addr", "localhost:998", "specify target address")
|
||||||
|
|
||||||
flag.Var(&headerList, "H", "provide additional header")
|
flag.StringVar(&auth, "auth", "", "provide auth credentials")
|
||||||
|
|
||||||
flag.StringVar(&dataFormat, "format", "text", "specify request's data format")
|
flag.Var(&headerList, "H", "provide additional header")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
@ -242,27 +274,11 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
addr, err := hsp.ParseAddress(address)
|
options := &client.ClientOptions{
|
||||||
if err != nil {
|
Headers: headerList.Map(),
|
||||||
fmt.Printf("ERR: Invalid address %s: %v\n", address, err)
|
Auth: auth,
|
||||||
return
|
BaseURL: address,
|
||||||
}
|
}
|
||||||
|
|
||||||
var df *hsp.DataFormat
|
StartSession(options)
|
||||||
|
|
||||||
switch dataFormat {
|
|
||||||
case hsp.DF_TEXT:
|
|
||||||
df = hsp.TextDataFormat()
|
|
||||||
case hsp.DF_JSON:
|
|
||||||
df = hsp.JsonDataFormat()
|
|
||||||
case hsp.DF_BYTES:
|
|
||||||
df = hsp.BytesDataFormat()
|
|
||||||
default:
|
|
||||||
fmt.Println("ERR: Invalid format selected for requests:", dataFormat)
|
|
||||||
}
|
|
||||||
|
|
||||||
StartSession(addr, df, &headerList)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,18 +7,18 @@ import (
|
|||||||
|
|
||||||
type Adddress struct {
|
type Adddress struct {
|
||||||
Host string
|
Host string
|
||||||
Port string
|
Port string
|
||||||
Route string
|
Route string
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseAddress(address string) (*Adddress, error) {
|
func ParseAddress(address string) (*Adddress, error) {
|
||||||
parts := strings.SplitN(address, "/", 2)
|
parts := strings.Split(address, "/")
|
||||||
|
|
||||||
var route string
|
var route string
|
||||||
if len(parts) == 1 {
|
if len(parts) == 1 {
|
||||||
route = "/"
|
route = "/"
|
||||||
} else if len(parts) > 1 {
|
} else if len(parts) > 1 {
|
||||||
route = "/" + strings.Join(parts[1:], "")
|
route = "/" + strings.Join(parts[1:], "/")
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("Failed to parse address: %s", address)
|
return nil, fmt.Errorf("Failed to parse address: %s", address)
|
||||||
}
|
}
|
||||||
@ -30,18 +30,29 @@ func ParseAddress(address string) (*Adddress, error) {
|
|||||||
if strings.Contains(addr, ":") {
|
if strings.Contains(addr, ":") {
|
||||||
p := strings.Split(addr, ":")
|
p := strings.Split(addr, ":")
|
||||||
if len(p) >= 2 {
|
if len(p) >= 2 {
|
||||||
port = p[len(p) - 1]
|
port = p[len(p)-1]
|
||||||
addr = p[0]
|
addr = p[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Adddress{
|
return &Adddress{
|
||||||
Host: addr,
|
Host: addr,
|
||||||
Port: port,
|
Port: port,
|
||||||
Route: route,
|
Route: route,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Adddress) Extend(extension string) (*Adddress, error) {
|
||||||
|
route := strings.TrimRight(a.Route, "/")
|
||||||
|
ext := strings.TrimLeft(extension, "/")
|
||||||
|
|
||||||
|
return &Adddress{
|
||||||
|
Host: a.Host,
|
||||||
|
Port: a.Port,
|
||||||
|
Route: route + "/" + ext,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Adddress) String() string {
|
func (a *Adddress) String() string {
|
||||||
return fmt.Sprintf("%s:%s", a.Host, a.Port)
|
return fmt.Sprintf("%s:%s", a.Host, a.Port)
|
||||||
}
|
}
|
||||||
|
@ -4,32 +4,59 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"maps"
|
"maps"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/LandaMm/hsp-go/hsp"
|
"github.com/LandaMm/hsp-go/hsp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type ClientOptions struct {
|
||||||
Headers map[string]string
|
Headers map[string]string
|
||||||
|
// TODO: in future support multiple types of auth (credentials, key etc.)
|
||||||
|
Auth string
|
||||||
|
BaseURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(headers map[string]string) *Client {
|
type Client struct {
|
||||||
|
Options *ClientOptions
|
||||||
|
Base *hsp.Adddress
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(options *ClientOptions) *Client {
|
||||||
|
if options == nil {
|
||||||
|
options = &ClientOptions{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var base *hsp.Adddress
|
||||||
|
|
||||||
|
if len(options.BaseURL) > 0 {
|
||||||
|
addr, err := hsp.ParseAddress(options.BaseURL)
|
||||||
|
if err == nil {
|
||||||
|
base = addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
Headers: headers,
|
Options: options,
|
||||||
|
Base: base,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) BuildHeaders(address *hsp.Adddress, df *hsp.DataFormat) map[string]string {
|
func (c *Client) BuildHeaders(address *hsp.Adddress, df *hsp.DataFormat) map[string]string {
|
||||||
headers := make(map[string]string)
|
headers := make(map[string]string)
|
||||||
|
|
||||||
if len(c.Headers) > 0 {
|
if len(c.Options.Headers) > 0 {
|
||||||
maps.Copy(headers, c.Headers)
|
maps.Copy(headers, c.Options.Headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
headers[hsp.H_ROUTE] = address.Route
|
headers[hsp.H_ROUTE] = address.Route
|
||||||
headers[hsp.H_DATA_FORMAT] = df.String()
|
headers[hsp.H_DATA_FORMAT] = df.String()
|
||||||
|
|
||||||
|
if len(c.Options.Auth) > 0 {
|
||||||
|
headers[hsp.H_AUTH] = c.Options.Auth
|
||||||
|
}
|
||||||
|
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +102,14 @@ func (c *Client) SingleHit(addr *hsp.Adddress, pkt *hsp.Packet) (*hsp.Packet, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) SendText(address, text string) (*hsp.Response, error) {
|
func (c *Client) SendText(address, text string) (*hsp.Response, error) {
|
||||||
addr, err := hsp.ParseAddress(address)
|
var addr *hsp.Adddress
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if c.Base != nil {
|
||||||
|
addr, err = c.Base.Extend(address)
|
||||||
|
} else {
|
||||||
|
addr, err = hsp.ParseAddress(address)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -96,7 +130,14 @@ func (c *Client) SendText(address, text string) (*hsp.Response, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) SendJson(address string, data any) (*hsp.Response, error) {
|
func (c *Client) SendJson(address string, data any) (*hsp.Response, error) {
|
||||||
addr, err := hsp.ParseAddress(address)
|
var addr *hsp.Adddress
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if c.Base != nil {
|
||||||
|
addr, err = c.Base.Extend(address)
|
||||||
|
} else {
|
||||||
|
addr, err = hsp.ParseAddress(address)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -120,7 +161,14 @@ func (c *Client) SendJson(address string, data any) (*hsp.Response, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) SendBytes(address string, data []byte) (*hsp.Response, error) {
|
func (c *Client) SendBytes(address string, data []byte) (*hsp.Response, error) {
|
||||||
addr, err := hsp.ParseAddress(address)
|
var addr *hsp.Adddress
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if c.Base != nil {
|
||||||
|
addr, err = c.Base.Extend(address)
|
||||||
|
} else {
|
||||||
|
addr, err = hsp.ParseAddress(address)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -12,6 +12,7 @@ const HSP_PORT = "998"
|
|||||||
const (
|
const (
|
||||||
H_STATUS = "status"
|
H_STATUS = "status"
|
||||||
H_DATA_FORMAT = "data-format"
|
H_DATA_FORMAT = "data-format"
|
||||||
|
H_AUTH = "auth"
|
||||||
H_ROUTE = "route"
|
H_ROUTE = "route"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -26,10 +27,11 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
STATUS_SUCCESS = 0
|
STATUS_SUCCESS = 0
|
||||||
STATUS_NOTFOUND = 69
|
STATUS_NOTFOUND = 69
|
||||||
STATUS_INTERNALERR = 129
|
STATUS_INTERNALERR = 129
|
||||||
STATUS_RECEIVED = 1
|
STATUS_UNAUTHORIZED = 49
|
||||||
|
STATUS_RECEIVED = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
var DATA_FORMATS map[string]string = map[string]string{
|
var DATA_FORMATS map[string]string = map[string]string{
|
||||||
|
@ -11,7 +11,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PacketVersion int = 1
|
PacketVersion int = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
type RawPacket struct {
|
type RawPacket struct {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@ -11,7 +13,7 @@ type Server struct {
|
|||||||
Addr hsp.Adddress
|
Addr hsp.Adddress
|
||||||
routePrefix string // TODO: Support route prefix, e.g listening on localhost/api
|
routePrefix string // TODO: Support route prefix, e.g listening on localhost/api
|
||||||
Running bool
|
Running bool
|
||||||
ConnChan chan net.Conn
|
ConnChan chan *hsp.Connection
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
@ -24,7 +26,7 @@ func NewServer(addr hsp.Adddress) *Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) SetListener(ln chan net.Conn) {
|
func (s *Server) SetListener(ln chan *hsp.Connection) {
|
||||||
s.ConnChan = ln
|
s.ConnChan = ln
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,8 +50,42 @@ func (s *Server) Start() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keys, err := hsp.GenerateKeyPair()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive client's public key
|
||||||
|
clientKey := make([]byte, 32)
|
||||||
|
n, err := io.ReadFull(conn, clientKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n != 32 {
|
||||||
|
return fmt.Errorf("Received invalid client's key with %d bytes (expected 32 bytes)", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send our public key to client
|
||||||
|
n, err = conn.Write(keys.Public[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n != 32 {
|
||||||
|
return fmt.Errorf("Couldn't send 32 bytes of public key (%d sent instead)", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedKey, err := hsp.DeriveSharedKey(keys.Private, [32]byte(clientKey))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if s.ConnChan != nil {
|
if s.ConnChan != nil {
|
||||||
s.ConnChan <- conn
|
connection := hsp.NewConnection(conn, keys, sharedKey)
|
||||||
|
s.ConnChan <- connection
|
||||||
|
} else {
|
||||||
|
conn.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user