package localserver import ( "context" "strconv" "github.com/pion/webrtc/v3" ) const ( ZONE_OFFER ReqType = "zone_offer" ZONE_ANSWER ReqType = "zone_answer" ZONE_COUNTER_OFFER ReqType = "zone_counter_offer" JOIN_ZONE ReqType = "join_hosted_squad" ZONE_ACCESS_DENIED ReqType = "zone_squad_access_denied" QUIT_ZONE ReqType = "zone_stop_call" ZONE_ACCESS_GRANTED ReqType = "zone_access_granted" INCOMING_ZONE_MEMBER ReqType = "incoming_zone_member" LEAVING_ZONE_MEMBER ReqType = "leaving_zone_member" ZONE_WEBRTC_RENNEGOTIATION_OFFER ReqType = "zone_rennegotiation_offer" ZONE_WEBRTC_RENNEGOTIATION_ANSWER ReqType = "zone_rennegotiation_answer" ZONE_WEBRTC_CANDIDATE ReqType = "zone_webrtc_candidate" NEW_ZONE ReqType = "new_zone" NEW_AUTHORIZED_ZONE_MEMBER ReqType = "new_authorized_zone_member" REMOVED_ZONE_AUTHORIZED_MEMBER ReqType = "removed_zone_authorized_member" ) type ZoneGrpcMiddleware struct { Manager *ZoneManager stream GrpcManager_LinkClient } func NewZoneGrpcMiddleware(manager *ZoneManager) (zoneGrpcMiddleware *ZoneGrpcMiddleware) { zoneGrpcMiddleware = &ZoneGrpcMiddleware{ Manager: manager, } return } func (zm *ZoneGrpcMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) { err = zm.stream.Send(&Request{ Type: string(ZONE_WEBRTC_CANDIDATE), From: zm.Manager.ID, Token: "none", Payload: map[string]string{ "from": zm.Manager.ID, "to": to, "candidate": candidate.ToJSON().Candidate, "sdpMid": *candidate.ToJSON().SDPMid, "sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), }, }) return } func (zm *ZoneGrpcMiddleware) Process(ctx context.Context, req *Response, stream GrpcManager_LinkClient) (err error) { done, errCh := make(chan struct{}), make(chan error) go func() { switch req.Type { case string(INCOMING_ZONE_MEMBER): case string(LEAVING_ZONE_MEMBER): if err := validateRequest(req.GetPayload(), "zoneId", "userId"); err != nil { return } if err := zm.Manager.HandleLeavingMember(req.Payload["userId"], req.Payload["zoneId"]); err != nil { errCh <- err return } case string(REMOVED_ZONE_AUTHORIZED_MEMBER): if err := validateRequest(req.GetPayload(), "zoneId", "userId"); err != nil { return } if err = atomicallyExecute(zm.Manager.zoneFlag, func() (err error) { reqChan := make(chan *ZoneRequest) done, e := zm.Manager.Zones[req.Payload["zoneId"]].ZoneRequestScheduler.Schedule(reqChan) go func() { defer close(reqChan) reqChan <- &ZoneRequest{ ReqType: string(REMOVE_USER), From: req.Payload["userId"], Payload: map[string]interface{}{ "userId": req.Payload["userId"], }, } reqChan <- &ZoneRequest{ ReqType: string(REMOVED_ZONE_AUTHORIZED_MEMBER), From: req.Payload["userId"], Payload: map[string]interface{}{ "userId": req.Payload["userId"], }, } }() select { case <-done: case err = <-e: } return }); err != nil { errCh <- err return } case string(NEW_AUTHORIZED_ZONE_MEMBER): if err := validateRequest(req.GetPayload(), "zoneId", "userId"); err != nil { return } if err = atomicallyExecute(zm.Manager.zoneFlag, func() (err error) { reqChan := make(chan *ZoneRequest) done, e := zm.Manager.Zones[req.Payload["zoneId"]].ZoneRequestScheduler.Schedule(reqChan) go func() { defer close(reqChan) reqChan <- &ZoneRequest{ ReqType: string(ADD_USER), From: req.Payload["userId"], Payload: map[string]interface{}{ "userId": req.Payload["userId"], }, } reqChan <- &ZoneRequest{ ReqType: string(NEW_AUTHORIZED_ZONE_MEMBER), From: req.Payload["userId"], Payload: map[string]interface{}{ "userId": req.Payload["userId"], }, } }() select { case <-done: case err = <-e: } return }); err != nil { errCh <- err return } case string(NEW_ZONE): logger.Println(req.Payload) if err := validateRequest(req.GetPayload(), "zoneId", "zoneName", "zoneImageURL", "zoneOwner", "zoneCreationDate"); err != nil { errCh <- err return } zone, err := NewZone(zm.Manager.ID, req.Payload["zoneId"], req.Payload["zoneName"], req.Payload["zoneImageURL"], req.Payload["zoneOwner"], req.Payload["zoneCreationDate"], true, []string{req.Payload["zoneOwner"]}) if err != nil { errCh <- err return } _ = atomicallyExecute(zm.Manager.zoneFlag, func() (err error) { zm.Manager.Zones[zone.ID] = zone return }) case string(ZONE_OFFER): if err := validateRequest(req.GetPayload(), FROM, TO, SDP); err != nil { errCh <- err return } if err := zm.Manager.HandleOffer(ctx, req.GetPayload(), zm.signalCandidate); err != nil { errCh <- err return } case string(ZONE_ANSWER): if err := validateRequest(req.GetPayload(), FROM, TO, SDP); err != nil { errCh <- err return } if err := zm.Manager.HandleAnswer(ctx, req.GetPayload()); err != nil { errCh <- err return } case string(ZONE_COUNTER_OFFER): if err := validateRequest(req.GetPayload(), FROM); err != nil { errCh <- err return } if err := zm.Manager.HandleCounterOffer(ctx, req.Payload); err != nil { errCh <- err return } case string(ZONE_WEBRTC_RENNEGOTIATION_OFFER): logger.Println("received negotiation offer") if err := validateRequest(req.GetPayload(), FROM, SDP); err != nil { errCh <- err return } if err := zm.Manager.HandleRennegotiationOffer(req.Payload[FROM], req.Payload[SDP]); err != nil { errCh <- err return } case string(ZONE_WEBRTC_RENNEGOTIATION_ANSWER): logger.Println("received negotiation answer") if err := validateRequest(req.GetPayload(), FROM, SDP); err != nil { errCh <- err return } if err := zm.Manager.HandleRennegotiationAnswer(req.Payload[FROM], req.Payload[SDP]); err != nil { errCh <- err return } case string(ZONE_WEBRTC_CANDIDATE): if err := validateRequest(req.GetPayload(), FROM, "candidate", "sdpMlineIndex", "sdpMid"); err != nil { errCh <- err return } logger.Println(req.Payload) i, err := strconv.Atoi(req.Payload["sdpMlineIndex"]) if err != nil { errCh <- err return } sdpMlineIndex := uint16(i) sdpMid := req.Payload["sdpMid"] logger.Println(sdpMid, sdpMlineIndex) if err := zm.Manager.AddCandidate(&webrtc.ICECandidateInit{ Candidate: req.Payload["candidate"], SDPMid: &sdpMid, SDPMLineIndex: &sdpMlineIndex, }, req.Payload[FROM]); err != nil { errCh <- err return } default: logger.Println("no request for zon grpc middleware") logger.Println(req.GetPayload()) logger.Println(req.Type) } done <- struct{}{} }() select { case <-ctx.Done(): return ctx.Err() case err = <-errCh: return case <-done: return } }