diff --git a/rvpn/envelope/envelope.go b/rvpn/envelope/envelope.go new file mode 100644 index 0000000..b484b40 --- /dev/null +++ b/rvpn/envelope/envelope.go @@ -0,0 +1,43 @@ +package envelope + +import "time" +import "encoding/json" +import "bytes" +import "io" + +//Envelope -- Standard daplie response structure +type Envelope struct { + TransactionType string `json:"type"` + Schema string `json:"schema"` + TransactionTimeStamp int64 `json:"txts"` + TransactionID int64 `json:"txid"` + Error string `json:"error"` + ErrorDescription string `json:"error_description"` + ErrorURI string `json:"error_uri"` + Result interface{} `json:"result"` +} + +//NewEnvelope -- Constructor +func NewEnvelope(transactionType string) (p *Envelope) { + transactionID++ + + p = new(Envelope) + p.TransactionType = transactionType + p.TransactionID = transactionID + p.TransactionTimeStamp = time.Now().Unix() + p.Error = "ok" + + return +} + +//Generate -- encode into JSON and return string +func (e *Envelope) Generate() string { + buf := new(bytes.Buffer) + json.NewEncoder(buf).Encode(e) + return buf.String() +} + +//GenerateWriter -- +func (e *Envelope) GenerateWriter(w io.Writer) { + json.NewEncoder(w).Encode(e) +} diff --git a/rvpn/envelope/setup.go b/rvpn/envelope/setup.go new file mode 100644 index 0000000..ccd443b --- /dev/null +++ b/rvpn/envelope/setup.go @@ -0,0 +1,19 @@ +package envelope + +import ( + "log" + "os" +) + +var ( + loginfo *log.Logger + logdebug *log.Logger + logFlags = log.Ldate | log.Lmicroseconds | log.Lshortfile + transactionID int64 +) + +func init() { + loginfo = log.New(os.Stdout, "INFO: envelope: ", logFlags) + logdebug = log.New(os.Stdout, "DEBUG: envelope:", logFlags) + transactionID = 1 +} diff --git a/rvpn/genericlistener/api_collect_server.go b/rvpn/genericlistener/api_collect_server.go new file mode 100644 index 0000000..813fb8d --- /dev/null +++ b/rvpn/genericlistener/api_collect_server.go @@ -0,0 +1,31 @@ +package genericlistener + +import ( + "fmt" + "time" +) + +//ServerAPI -- Structure to support the server API +type ServerAPI struct { + ServerName string `json:"server_name"` + ServerID int64 `json:"server_id"` + Domains []*DomainAPI `json:"domains"` + Duration float64 `json:"duration"` + BytesIn int64 `json:"bytes_in"` + BytesOut int64 `json:"bytes_out"` + Source string `json:"source_address"` +} + +//NewServerAPI - Constructor +func NewServerAPI(c *Connection) (s *ServerAPI) { + s = new(ServerAPI) + s.ServerName = fmt.Sprintf("%p", c) + s.ServerID = c.ConnectionID() + s.Domains = make([]*DomainAPI, 0) + s.Duration = time.Since(c.ConnectTime()).Seconds() + s.BytesIn = c.BytesIn() + s.BytesOut = c.BytesOut() + s.Source = c.source + + return +} diff --git a/rvpn/genericlistener/server_api.go b/rvpn/genericlistener/api_collect_servers.go similarity index 68% rename from rvpn/genericlistener/server_api.go rename to rvpn/genericlistener/api_collect_servers.go index 2f84e27..152322e 100644 --- a/rvpn/genericlistener/server_api.go +++ b/rvpn/genericlistener/api_collect_servers.go @@ -5,23 +5,27 @@ import ( "time" ) -//ServerAPI -- Structure to support the server API -type ServerAPI struct { +//ServersAPI -- Structure to support the server API +type ServersAPI struct { ServerName string `json:"server_name"` + ServerID int64 `json:"server_id"` Domains []*DomainAPI `json:"domains"` Duration float64 `json:"duration"` BytesIn int64 `json:"bytes_in"` BytesOut int64 `json:"bytes_out"` + Source string `json:"source_address"` } -//NewServerAPI - Constructor -func NewServerAPI(c *Connection) (s *ServerAPI) { - s = new(ServerAPI) +//NewServersAPI - Constructor +func NewServersAPI(c *Connection) (s *ServersAPI) { + s = new(ServersAPI) s.ServerName = fmt.Sprintf("%p", c) + s.ServerID = c.ConnectionID() s.Domains = make([]*DomainAPI, 0) s.Duration = time.Since(c.ConnectTime()).Seconds() s.BytesIn = c.BytesIn() s.BytesOut = c.BytesOut() + s.Source = c.Source() for d := range c.DomainTrack { dt := c.DomainTrack[d] @@ -33,12 +37,12 @@ func NewServerAPI(c *Connection) (s *ServerAPI) { //ServerAPIContainer -- Holder for all the Servers type ServerAPIContainer struct { - Servers []*ServerAPI `json:"servers"` + Servers []*ServersAPI `json:"servers"` } //NewServerAPIContainer -- Constructor func NewServerAPIContainer() (p *ServerAPIContainer) { p = new(ServerAPIContainer) - p.Servers = make([]*ServerAPI, 0) + p.Servers = make([]*ServersAPI, 0) return p } diff --git a/rvpn/genericlistener/api_endpoints.go b/rvpn/genericlistener/api_endpoints.go deleted file mode 100644 index 92ffa03..0000000 --- a/rvpn/genericlistener/api_endpoints.go +++ /dev/null @@ -1,25 +0,0 @@ -package genericlistener - -import "net/http" - -type apiEndPoint struct { - pack string - endpoint string - method func(w http.ResponseWriter, r *http.Request) -} - -type APIEndPoints struct { - endPoint map[string]*apiEndPoint -} - -//NewAPIEndPoints -- Constructor -func NewAPIEndPoints() (p *APIEndPoints) { - p = new(apiEndPoints) - p.endPoint = make(map[string]*apiEndPoint) - return -} - -func (p *apiEndPoints) add(pack string, endpoint string, method func(w http.ResponseWriter, r *http.Request)) { - - router.HandleFunc("/api/"+rDNSPackageName+"servers", apiServers) -} diff --git a/rvpn/genericlistener/api_interface.go b/rvpn/genericlistener/api_interface.go index fc6ac23..688b9ff 100644 --- a/rvpn/genericlistener/api_interface.go +++ b/rvpn/genericlistener/api_interface.go @@ -2,16 +2,18 @@ package genericlistener import ( "context" - "encoding/json" "fmt" "net/http" + "runtime" + "strconv" "strings" + "git.daplie.com/Daplie/go-rvpn-server/rvpn/envelope" "github.com/gorilla/mux" ) const ( - rDNSPackageName = "com.daplie.rvpn" + endPointPrefix = "/api/com.daplie.rvpn/" ) var connectionTable *Table @@ -23,8 +25,6 @@ func handleAdminClient(ctx context.Context, oneConn *oneConnListener) { connectionTable = ctx.Value(ctxConnectionTable).(*Table) router := mux.NewRouter().StrictSlash(true) - endpoints := make(map[string]string) - router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { loginfo.Println("HandleFunc /") switch url := r.URL.Path; url { @@ -41,10 +41,12 @@ func handleAdminClient(ctx context.Context, oneConn *oneConnListener) { router.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") - fmt.Fprintln(w, "Welcome..press Servers to access stats") + fmt.Fprintln(w, "Welcome..press Servers to access stats") }) - router.HandleFunc("/api/"+rDNSPackageName+"servers", apiServers) + router.HandleFunc(endPointPrefix+"servers", getServersEndpoint).Methods("GET") + router.HandleFunc(endPointPrefix+"server/", getServerEndpoint).Methods("GET") + router.HandleFunc(endPointPrefix+"server/{server-id}", getServerEndpoint).Methods("GET") s := &http.Server{ Addr: ":80", @@ -63,17 +65,60 @@ func handleAdminClient(ctx context.Context, oneConn *oneConnListener) { } } -func apiServers(w http.ResponseWriter, r *http.Request) { - fmt.Println("here") +func getServersEndpoint(w http.ResponseWriter, r *http.Request) { + pc, _, _, _ := runtime.Caller(0) + loginfo.Println(runtime.FuncForPC(pc).Name()) + serverContainer := NewServerAPIContainer() for c := range connectionTable.Connections() { - serverAPI := NewServerAPI(c) + serverAPI := NewServersAPI(c) serverContainer.Servers = append(serverContainer.Servers, serverAPI) } w.Header().Set("Content-Type", "application/json; charset=UTF-8") - json.NewEncoder(w).Encode(serverContainer) + + env := envelope.NewEnvelope("servers/GET") + env.Result = serverContainer + env.GenerateWriter(w) + + //json.NewEncoder(w).Encode(serverContainer) } + +func getServerEndpoint(w http.ResponseWriter, r *http.Request) { + pc, _, _, _ := runtime.Caller(0) + loginfo.Println(runtime.FuncForPC(pc).Name()) + + env := envelope.NewEnvelope("server/GET") + + params := mux.Vars(r) + if id, ok := params["server-id"]; !ok { + env.Error = "server-id is missing" + env.ErrorURI = r.RequestURI + env.ErrorDescription = "server API requires a server-id" + } else { + serverID, err := strconv.Atoi(id) + if err != nil { + env.Error = "server-id is not an integer" + env.ErrorURI = r.RequestURI + env.ErrorDescription = "server API requires a server-id" + + } else { + conn, err := connectionTable.GetConnection(int64(serverID)) + if err != nil { + env.Error = "server-id was not found" + env.ErrorURI = r.RequestURI + env.ErrorDescription = "missing server-id, make sure desired service-id is in servers" + } else { + loginfo.Println("test") + serverAPI := NewServerAPI(conn) + env.Result = serverAPI + + } + } + } + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + env.GenerateWriter(w) +} diff --git a/rvpn/genericlistener/connection.go b/rvpn/genericlistener/connection.go index 6112e28..c7cd016 100755 --- a/rvpn/genericlistener/connection.go +++ b/rvpn/genericlistener/connection.go @@ -63,10 +63,15 @@ type Connection struct { ///wssState tracks a highlevel status of the connection, false means do nothing. wssState bool + + //connectionID + connectionID int64 } //NewConnection -- Constructor func NewConnection(connectionTable *Table, conn *websocket.Conn, remoteAddress string, initialDomains []interface{}, connectionTrack *Tracking) (p *Connection) { + connectionID = connectionID + 1 + p = new(Connection) p.mutex = &sync.Mutex{} p.connectionTable = connectionTable @@ -85,6 +90,7 @@ func NewConnection(connectionTable *Table, conn *websocket.Conn, remoteAddress s } p.State(true) + p.connectionID = connectionID return } @@ -124,6 +130,11 @@ func (c *Connection) SendCh() chan *SendTrack { return c.send } +//Source -- +func (c *Connection) Source() string { + return c.source +} + func (c *Connection) addIn(num int64) { c.bytesIn = c.bytesIn + num } @@ -167,6 +178,11 @@ func (c *Connection) Update() { c.lastUpdate = time.Now() } +//ConnectionID - Get +func (c *Connection) ConnectionID() int64 { + return c.connectionID +} + //NextWriter -- Wrapper to allow a high level state check before offering NextWriter //The libary failes if client abends during write-cycle. a fast moving write is not caught before socket state bubbles up //A synchronised state is maintained diff --git a/rvpn/genericlistener/connection_table.go b/rvpn/genericlistener/connection_table.go index ff57042..e0a2f6e 100755 --- a/rvpn/genericlistener/connection_table.go +++ b/rvpn/genericlistener/connection_table.go @@ -66,6 +66,19 @@ func (c *Table) reaper(delay int, idle int) { } } +//GetConnection -- find connection by server-id +func (c *Table) GetConnection(serverID int64) (conn *Connection, err error) { + for conn := range c.connections { + if conn.ConnectionID() == serverID { + return conn, err + } + } + + err = fmt.Errorf("Server-id %d not found", serverID) + + return nil, err +} + //Run -- Execute func (c *Table) Run(ctx context.Context) { loginfo.Println("ConnectionTable starting") diff --git a/rvpn/genericlistener/setup.go b/rvpn/genericlistener/setup.go index 79d6113..6541c3b 100644 --- a/rvpn/genericlistener/setup.go +++ b/rvpn/genericlistener/setup.go @@ -3,15 +3,21 @@ package genericlistener import ( "log" "os" + "runtime" ) var ( - loginfo *log.Logger - logdebug *log.Logger - logFlags = log.Ldate | log.Lmicroseconds | log.Lshortfile + loginfo *log.Logger + logdebug *log.Logger + logFlags = log.Ldate | log.Lmicroseconds | log.Lshortfile + connectionID int64 ) func init() { loginfo = log.New(os.Stdout, "INFO: genericlistener: ", logFlags) logdebug = log.New(os.Stdout, "DEBUG: genericlistener:", logFlags) + pc, _, _, _ := runtime.Caller(0) + loginfo.Println(runtime.FuncForPC(pc).Name()) + + connectionID = 0 }