Procházet zdrojové kódy

websocket not accept registrations

master
sp před 4 roky
rodič
revize
abad453b31
8 změnil soubory, kde provedl 291 přidání a 75 odebrání
  1. +59
    -0
      apiV1PayoutEx.go
  2. +1
    -1
      apiV1PeopleList.go
  3. +2
    -4
      apiV1logout.go
  4. +128
    -38
      apiv1.go
  5. +50
    -12
      websocket.go
  6. +34
    -0
      websocket_incoming.go
  7. +5
    -3
      websocket_message.go
  8. +12
    -17
      websocket_queue.go

+ 59
- 0
apiV1PayoutEx.go Zobrazit soubor

@@ -0,0 +1,59 @@
package main

import (
"biukop.com/sfm/loan"
log "github.com/sirupsen/logrus"
"net/http"
"strconv"
)

func apiV1PayOutExGet(w http.ResponseWriter, r *http.Request, ss *loan.Session) {
strId := r.URL.Path[len(apiV1Prefix+"payout-ex/"):]
ret := loan.PayOutEx{}
id, e := strconv.Atoi(strId)
if e != nil {
log.Error("invalid id for PayOutEx", strId, e.Error())
apiV1Client403Error(w, r, ss)
}
e = ret.Read(int64(id))
if e != nil {
log.Error("failed to read PayOutEx", strId, e.Error())
apiV1Server500Error(w, r)
}

apiV1SendJson(ret, w, r, ss)
return
}

func apiV1PayOutExListGet(w http.ResponseWriter, r *http.Request, ss *loan.Session) {
// strId := r.URL.Path[len(apiV1Prefix+"payout-ex-list/"):]
input, e := decodeJsonFullLoanOverview(r)

// TODO; Generalize that input, and return a list of filters and sort

// Todo: do some serious query for the data in the Grid, and make it a pattern. Get it done!

if e != nil {
apiV1EmptyResponse(w, r, ss)
} else {

switch ss.GetRole() {
case "broker":
input.Filter.Filters = append(input.Filter.Filters, loan.FullLoanSummaryFilter{
Field: "broker_ids",
Operator: "contains",
Value: loan.JsonString(ss.User)})
break
case "user":
input.Filter.Filters = append(input.Filter.Filters, loan.FullLoanSummaryFilter{
Field: "client_ids",
Operator: "contains",
Value: loan.JsonString(ss.User)})
break
}

data := loan.QFullLLoanSummary(input)
//send out
apiV1SendJson(data, w, r, ss)
}
}

+ 1
- 1
apiV1PeopleList.go Zobrazit soubor

@@ -107,7 +107,7 @@ func apiV1PeopleExtraGet(w http.ResponseWriter, r *http.Request, ss *loan.Sessio
id := r.URL.Path[len(apiV1Prefix+"people-extra/"):]
ret := UserExtra{}
ret.Role = loan.GetRoleById(id)
//TODO; check manager and account role
switch ret.Role {
case "people":
apiV1SendJson(ret, w, r, ss)

+ 2
- 4
apiV1logout.go Zobrazit soubor

@@ -11,10 +11,8 @@ func apiV1Logout(w http.ResponseWriter, r *http.Request, ss *loan.Session) {
res := apiV1ResponseBlank()
WsNotifyLogout(ss)
ss.Expire = time.Now().Add(-10000) //make sure it expired

ssEmpty := loan.Session{}
log.Info("Logout user ", ss.User, " from session ", ss.Id)
//send out
apiV1AddTrackingCookie(w, r, &ssEmpty) //always the last one to set cookies
res.sendJson(w)
apiV1AddTrackingCookie(w, r, ss) //always the last one to set cookies
_, _ = res.sendJson(w)
}

+ 128
- 38
apiv1.go Zobrazit soubor

@@ -77,7 +77,6 @@ func setupApiV1Handler() []apiV1HandlerMap {
{"GET", "login-available/", apiV1LoginAvailable},

{"POST", "lender-upload/", apiV1UploadsPost},
{"GET", "lender-upload/", apiV1UploadOriginalFileGet},

{"GET", "upload-analysis/", apiV1UploadAnalysis},
{"PUT", "upload-analysis/", apiV1UploadCreateAnalysis},
@@ -94,6 +93,8 @@ func setupApiV1Handler() []apiV1HandlerMap {

{"GET", "lender-list/", apiV1LenderList},

{"GET", "payout-ex/", apiV1PayOutExGet},

{"GET", "login", apiV1DumpRequest},
}
} else { //production
@@ -148,7 +149,6 @@ func setupApiV1Handler() []apiV1HandlerMap {
{"GET", "login-available/", apiV1LoginAvailable},

{"POST", "lender-upload/", apiV1UploadsPost},
{"GET", "lender-upload/", apiV1UploadOriginalFileGet},

{"GET", "upload-analysis/", apiV1UploadAnalysis},
{"PUT", "upload-analysis/", apiV1UploadCreateAnalysis},
@@ -165,6 +165,8 @@ func setupApiV1Handler() []apiV1HandlerMap {

{"GET", "lender-list/", apiV1LenderList},

{"GET", "payout-ex/", apiV1PayOutExGet},

{"GET", "login", apiV1EmptyResponse},
}
}
@@ -188,43 +190,69 @@ func apiV1Main(w http.ResponseWriter, r *http.Request) {
logRequestDebug(httputil.DumpRequest(r, true))
}

session := apiV1InitSession(r)
if config.Debug {
log.Debugf("session : %+v", session)
session := loan.Session{}
session.MarkEmpty()
bypassSession := apiV1NoNeedSession(r)
if !bypassSession { // no point to create session for preflight
session = apiV1InitSession(r)
if config.Debug {
log.Debugf("session : %+v", session)
}
}

//search through handler
path := r.URL.Path[len(apiV1Prefix):] //strip API prefix
handled := false
for _, node := range apiV1Handler {
//log.Println(node, path, strings.HasPrefix(path, node.Path))
if (r.Method == node.Method || node.Method == "*") && strings.HasPrefix(path, node.Path) {
if (strings.ToUpper(r.Method) == node.Method || node.Method == "*") && strings.HasPrefix(path, node.Path) {
handled = true
node.Handler(w, r, &session)
e := session.Write() //finish this session to DB
if e != nil {
log.Warnf("Failed to Save Session %+v \n reason \n%s\n", session, e.Error())
}
return
break //stop search handler further
}
}

if !bypassSession { // no point to write session for preflight
e := session.Write() //finish this session to DB
if e != nil {
log.Warnf("Failed to Save Session %+v \n reason \n%s\n", session, e.Error())
}
}

//Catch for all UnHandled Request
e := session.Write() //finish this session to DB
if e != nil {
log.Warnf("Failed to Save Session %+v \n reason \n%s\n", session, e.Error())
if !handled {
if config.Debug {
apiV1DumpRequest(w, r, &session)
} else {
apiV1EmptyResponse(w, r, &session)
}
}
if config.Debug {
apiV1DumpRequest(w, r, &session)
} else {
apiV1EmptyResponse(w, r, &session)
}

func apiV1NoNeedSession(r *http.Request) bool {
if r.Method == "OPTIONS" {
return true
}

path := r.URL.Path[len(apiV1Prefix):] //strip API prefix

if r.Method == "GET" {
if strings.HasPrefix(path, "avatar/") ||
strings.HasPrefix(path, "upload-original/") ||
strings.HasPrefix(path, "upload-as-image/") ||
strings.HasPrefix(path, "upload-as-analysis/") ||
strings.HasPrefix(path, "upload-as-thumbnail/") ||
strings.HasPrefix(path, "upload-as-pdf/") {
return true
}
}
return false
}

func apiV1InitSession(r *http.Request) (session loan.Session) {
session.MarkEmpty()

//track browser, and take session from cookie
cookieSession, e := apiV1InitSessionByBrowserId(r)
cookieSession, e := apiV1InitSessionByCookie(r)
if e == nil {
session = cookieSession
}
@@ -255,8 +283,8 @@ func setupCrossOriginResponse(w *http.ResponseWriter, r *http.Request) {
method := r.Header.Get("Access-Control-Request-Method")
(*w).Header().Set("Access-Control-Allow-Origin", origin) //for that specific origin
(*w).Header().Set("Access-Control-Allow-Credentials", "true")
(*w).Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, "+method)
(*w).Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, Cookie, Biukop-Session, Biukop-Session-Token, Biukop-Session-Expire, "+requestedHeaders)
(*w).Header().Set("Access-Control-Allow-Methods", removeDupHeaderOptions("POST, GET, OPTIONS, PUT, DELETE, "+method))
(*w).Header().Set("Access-Control-Allow-Headers", removeDupHeaderOptions("Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, Cookie, Biukop-Session, Biukop-Session-Token, Biukop-Session-Expire, "+requestedHeaders))
}

func apiV1GetMachineId(r *http.Request) string {
@@ -275,11 +303,35 @@ func apiV1GetMachineId(r *http.Request) string {
return mid
}

func apiV1GetCORSHeaders(r *http.Request) (ret string) {
requestedHeaders := r.Header.Get("Access-control-Request-Headers")
ret = "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, Cookie, Biukop-Session, Biukop-Session-Token, Biukop-Session-Expire," + requestedHeaders
return removeDupHeaderOptions(ret)
}

func removeDupHeaderOptions(inStr string) (out string) {
headers := map[string]struct{}{}
strings.ReplaceAll(inStr, " ", "") // remove space
headerArray := strings.Split(inStr, ",") // split
for _, v := range headerArray {
headers[v] = struct{}{} // same key will overwrite each other
}
out = ""
for k, _ := range headers {
if out != "" {
out += ", "
}
out += k
}
return
}

func apiV1GetMachineIdFromSession(ss *loan.Session) string {
return ss.GetStr("Biukop-Mid")
}
func apiV1InitSessionByBrowserId(r *http.Request) (session loan.Session, e error) {
func apiV1InitSessionByCookie(r *http.Request) (session loan.Session, e error) {
var sid string
session.MarkEmpty()
mid := apiV1GetMachineId(r)
inCookie, e := r.Cookie("Biukop-Session")
if e == nil {
@@ -297,29 +349,60 @@ func apiV1InitSessionByBrowserId(r *http.Request) (session loan.Session, e error
}

func apiV1AddTrackingCookie(w http.ResponseWriter, r *http.Request, session *loan.Session) {
//set session header too.
w.Header().Add("Access-Control-Expose-Headers", "Biukop-Session")
if strings.ToUpper(r.Method) == "OPTION" {
return
}
w.Header().Add("Access-Control-Expose-Headers", apiV1GetCORSHeaders(r))
apiV1AddTrackingSession(w, r, session)
apiV1AddTrackingMachineId(w, r, session)
}

func apiV1AddTrackingSession(w http.ResponseWriter, r *http.Request, session *loan.Session) {
sessionId := ""
if session == nil {
log.Warn("non-exist session, empty Id sent", session)
w.Header().Add("Biukop-Session", "")
} else {
w.Header().Add("Biukop-Session", session.Id)
if session.Id == "" {
log.Warn("empty session, empty Id sent", session)
} else {
w.Header().Add("Biukop-Session", session.Id)
sessionId = session.Id
}

}

//add tracking cookie
expiration := time.Now().Add(365 * 24 * time.Hour)
cookie := http.Cookie{
Name: "Biukop-Session",
Value: sessionId, // may be ""
Expires: time.Now().Add(365 * 24 * time.Hour),
Path: "/",
Secure: true,
SameSite: http.SameSiteNoneMode}
http.SetCookie(w, &cookie)

}
func apiV1AddTrackingMachineId(w http.ResponseWriter, r *http.Request, session *loan.Session) {
mid := apiV1GetMachineId(r)
cookie := http.Cookie{Name: "Biukop-Mid", Value: mid, Expires: expiration, Path: "/", Secure: true, SameSite: http.SameSiteNoneMode}
expiration := time.Now().Add(365 * 24 * time.Hour)

w.Header().Add("Biukop-Mid", mid)

cookie := http.Cookie{
Name: "Biukop-Mid",
Value: mid,
Expires: expiration,
Path: "/",
Secure: true,
SameSite: http.SameSiteNoneMode}
http.SetCookie(w, &cookie)

if session != nil {
cookie = http.Cookie{Name: "Biukop-Session", Value: session.Id, Expires: expiration, Path: "/", Secure: true, SameSite: http.SameSiteNoneMode}
http.SetCookie(w, &cookie)
}
}

func apiV1InitSessionByHttpHeader(r *http.Request) (ss loan.Session, e error) {
sid := r.Header.Get("Biukop-Session")
ss.MarkEmpty()
sid := apiV1GetSessionIdFromRequest(r)

//make sure session id is given
if sid != "" {
e = ss.Retrieve(r)
@@ -332,13 +415,20 @@ func apiV1InitSessionByHttpHeader(r *http.Request) (ss loan.Session, e error) {
e = errors.New("session not found: " + sid)
}
return
return
}

func apiV1ErrorCheck(e error) {
if nil != e {
panic(e.Error()) //TODO: detailed error check, truck all caller
func apiV1GetSessionIdFromRequest(r *http.Request) string {
sid := ""
inCookie, e := r.Cookie("Biukop-Session")
if e == nil {
sid = inCookie.Value
}

headerSid := r.Header.Get("Biukop-Session")
if headerSid != "" {
sid = headerSid
}
return sid
}

func apiV1Server500Error(w http.ResponseWriter, r *http.Request) {

+ 50
- 12
websocket.go Zobrazit soubor

@@ -25,11 +25,8 @@ func apiV1WebSocketHandler(w http.ResponseWriter, r *http.Request) {
return
}

mid := apiV1GetMachineId(r)
wsq.MapMidToConnection(mid, ws)

// helpful log statement to show connections
log.Println("Websocket Api/V1: Client Connected", mid)
log.Println("Websocket Api/V1: Client Connected", r.RemoteAddr)

wsReader(ws)
}
@@ -50,21 +47,62 @@ func wsReader(conn *websocket.Conn) {
log.Println(err)
return
}
// print out that message for clarity
// fmt.Println(string(p))

if err := conn.WriteMessage(messageType, p); err != nil {
wsq.del(conn)
log.Println(err)
return
// WsEchoIncomingMessage(conn, string(p), messageType)
switch messageType {
case websocket.TextMessage:
WsProcessingTxtMessage(conn, string(p))
break
case websocket.BinaryMessage:
WsProcessingBinaryMessage(conn, p)
break
case websocket.PingMessage:
break
case websocket.PongMessage:
break
}

if string(p) == "send dummy string for 500 times" {
go wsDummySender(conn)
}
}

func WsProcessingTxtMessage(conn *websocket.Conn, str string) {
if str == "send dummy string for 500 times" {
go wsDummySender(conn)
}

// must be json message with key, value pairs
incoming := make(map[string]string)
e := json.Unmarshal([]byte(str), &incoming)
if e != nil {
log.Error("ws incoming msg error not json", str, e)
return
}

key := incoming["t"]
for _, v := range apiV1WsHandler {
if key == v.Keyword {
v.Handler(conn, incoming)
return // we have already processed
}
}
}

func WsProcessingBinaryMessage(conn *websocket.Conn, data []byte) {
return
}

func WsEchoIncomingMessage(conn *websocket.Conn, msg string, messageType int) {
// print out that message for clarity
fmt.Println(msg)

// this is echo
if err := conn.WriteMessage(messageType, []byte(msg)); err != nil {
wsq.del(conn)
log.Println(err)
return
}
}

func WsBroadCast(msg string) {
wsq.wsDoBroadcast(msg, "")
}

+ 34
- 0
websocket_incoming.go Zobrazit soubor

@@ -0,0 +1,34 @@
package main

import (
"fmt"
"github.com/gorilla/websocket"
log "github.com/sirupsen/logrus"
)

type apiV1WsHandlerMap struct {
Keyword string // keyword
Handler func(conn *websocket.Conn, msg map[string]string)
}

var apiV1WsHandler = []apiV1WsHandlerMap{
{"echo", apiV1WsEcho},
{"register-session", apiV1WsRegisterSession},
}

func apiV1WsEcho(conn *websocket.Conn, msg map[string]string) {
// print out that message for clarity
fmt.Println(msg)

// this is echo
if err := conn.WriteMessage(websocket.TextMessage, []byte(msg["t"])); err != nil {
wsq.del(conn)
log.Println(err)
}
}

func apiV1WsRegisterSession(conn *websocket.Conn, msg map[string]string) {
sid := msg["session"]
wsq.MapSessionToConnection(sid, conn)
log.Info("ws register session:", sid)
}

+ 5
- 3
websocket_message.go Zobrazit soubor

@@ -1,9 +1,11 @@
package main

import "biukop.com/sfm/loan"
import (
"biukop.com/sfm/loan"
log "github.com/sirupsen/logrus"
)

func WsNotifyNewLogin(ss *loan.Session) {

msg := make(map[string]string)
msg["T"] = "login"
msg["Mid"] = apiV1GetMachineIdFromSession(ss)
@@ -14,7 +16,6 @@ func WsNotifyNewLogin(ss *loan.Session) {
}

func WsNotifyLogout(ss *loan.Session) {

msg := make(map[string]string)
msg["T"] = "logout"
msg["Mid"] = apiV1GetMachineIdFromSession(ss)
@@ -22,4 +23,5 @@ func WsNotifyLogout(ss *loan.Session) {
msg["Sid"] = ss.Id
msg["Role"] = ss.GetRole()
WsBroadCastExceptMe(ss.Id, msg)
log.Info(ss, msg)
}

+ 12
- 17
websocket_queue.go Zobrazit soubor

@@ -7,24 +7,25 @@ import (

type wsQ struct {
wsMutex sync.Mutex
Clients map[*websocket.Conn]struct{}
Clients map[*websocket.Conn]string
ClientsBySession map[string]*websocket.Conn
}

var wsq = wsQ{
Clients: make(map[*websocket.Conn]struct{}),
Clients: make(map[*websocket.Conn]string),
ClientsBySession: make(map[string]*websocket.Conn),
}

func (m *wsQ) addConnection(c *websocket.Conn) {
m.wsMutex.Lock()
m.Clients[c] = struct{}{}
m.Clients[c] = ""
m.wsMutex.Unlock()
}

func (m *wsQ) MapMidToConnection(mid string, c *websocket.Conn) {
func (m *wsQ) MapSessionToConnection(sid string, c *websocket.Conn) {
m.wsMutex.Lock()
m.ClientsBySession[mid] = c
m.ClientsBySession[sid] = c
m.Clients[c] = sid
m.wsMutex.Unlock()
}

@@ -50,24 +51,18 @@ func (m *wsQ) getConnBySessionId(sid string) (conn *websocket.Conn) {

func (m *wsQ) del(conn *websocket.Conn) {
m.wsMutex.Lock()
delete(m.Clients, conn)
for k, v := range m.ClientsBySession {
if v == conn {
delete(m.ClientsBySession, k)
break
}
if sid, exist := m.Clients[conn]; exist {
delete(m.Clients, conn)
delete(m.ClientsBySession, sid)
}
m.wsMutex.Unlock()
}

func (m *wsQ) delBySessionId(sid string) {
m.wsMutex.Lock()
for k, v := range m.ClientsBySession {
if k == sid {
delete(m.ClientsBySession, k)
break
}
delete(m.Clients, v)
if conn, exist := m.ClientsBySession[sid]; exist {
delete(m.ClientsBySession, sid)
delete(m.Clients, conn)
}
m.wsMutex.Unlock()
}

Načítá se…
Zrušit
Uložit