API Rest Interface answers to servers and server/{id}

- built envelope based on AJ spec, self standing package with transaction ID increment and unix time stamps
- fixed servers end point
- created server/{id} end point
- created rest mappings for GET
This commit is contained in:
Henry Camacho 2017-03-13 16:46:11 -05:00
parent b88817e4d1
commit dd7d63baf6
9 changed files with 197 additions and 45 deletions

43
rvpn/envelope/envelope.go Normal file
View File

@ -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)
}

19
rvpn/envelope/setup.go Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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, "<html>Welcome..press <a href=/api/servers>Servers</a> to access stats</html>")
fmt.Fprintln(w, "<html>Welcome..press <a href=/api/com.daplie.rvpn/servers>Servers</a> to access stats</html>")
})
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)
}

View File

@ -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

View File

@ -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")

View File

@ -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
}