package localserver import ( "context" "encoding/json" "fmt" "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" DELETE_ZONE = "delete_zone" DISCONNECT_ZONE_MEMBER = "disconnect_zone_member" ) type ZoneGrpcMiddleware struct { Manager *ZoneManager stream SignalingService_LinkClient } func NewZoneGrpcMiddleware(manager *ZoneManager) (zoneGrpcMiddleware *ZoneGrpcMiddleware) { zoneGrpcMiddleware = &ZoneGrpcMiddleware{ Manager: manager, } return } func (zm *ZoneGrpcMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) { bs, err := json.Marshal(map[string]string{ "from": zm.Manager.ID, "to": to, "candidate": candidate.ToJSON().Candidate, "sdpMid": *candidate.ToJSON().SDPMid, "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), }) if err != nil { return } err = zm.stream.Send(&SignalingMessage{ Type: string(ZONE_WEBRTC_CANDIDATE), From: zm.Manager.ID, To: to, Payload: bs, }) return } func (zm *ZoneGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage, stream SignalingService_LinkClient) (err error) { done, errCh := make(chan struct{}), make(chan error) fmt.Println("got req", req.Type, string(req.Payload)) go func() { var payload map[string]string if e := json.Unmarshal(req.Payload, &payload); e != nil { errCh <- e return } switch req.Type { case string(INCOMING_ZONE_MEMBER): case string(DELETE_ZONE): if err := validateRequest(payload, "zoneId"); err != nil { return } if err := zm.Manager.DeleteZone(payload["zoneId"]); err != nil { errCh <- err return } case string(LEAVING_ZONE_MEMBER): if err := validateRequest(payload, "zoneId"); err != nil { return } if err := zm.Manager.HandleLeavingMember(req.GetFrom(), payload["zoneId"], false); err != nil { errCh <- err return } case string(REMOVED_ZONE_AUTHORIZED_MEMBER): if err := validateRequest(payload, "zoneId", "userId"); err != nil { return } if err = atomicallyExecute(zm.Manager.zoneFlag, func() (err error) { reqChan := make(chan *ZoneRequest) if _, ok := zm.Manager.Zones[payload["zoneId"]]; !ok { err = fmt.Errorf("no such zone") return } done, e := zm.Manager.Zones[payload["zoneId"]].ZoneRequestScheduler.Schedule(reqChan) go func() { defer close(reqChan) // reqChan <- &ZoneRequest{ // ReqType: string(REMOVE_USER), // From: payload["userId"], // Payload: map[string]interface{}{ // "userId": payload["userId"], // }, // } reqChan <- &ZoneRequest{ ReqType: string(REMOVED_ZONE_AUTHORIZED_MEMBER), From: payload["userId"], Payload: map[string]interface{}{ "userId": payload["userId"], }, } }() select { case <-done: case err = <-e: } return }); err != nil { errCh <- err return } case string(NEW_AUTHORIZED_ZONE_MEMBER): if err := validateRequest(payload, "zoneId", "userId"); err != nil { return } if err = atomicallyExecute(zm.Manager.zoneFlag, func() (err error) { reqChan := make(chan *ZoneRequest) if _, ok := zm.Manager.Zones[payload["zoneId"]]; !ok { err = fmt.Errorf("no such zone") return } done, e := zm.Manager.Zones[payload["zoneId"]].ZoneRequestScheduler.Schedule(reqChan) go func() { defer close(reqChan) reqChan <- &ZoneRequest{ ReqType: string(NEW_AUTHORIZED_ZONE_MEMBER), From: payload["userId"], Payload: map[string]interface{}{ "userId": payload["userId"], }, } // reqChan <- &ZoneRequest{ // ReqType: string(ADD_USER), // From: payload["userId"], // Payload: map[string]interface{}{ // "userId": payload["userId"], // }, // } }() select { case <-done: case err = <-e: } return }); err != nil { errCh <- err return } case string(NEW_ZONE): logger.Println(payload) if err := validateRequest(payload, "zoneId", "zoneName", "zoneImageURL", "zoneOwner", "zoneCreationDate"); err != nil { errCh <- err return } zone, err := NewZone(zm.Manager.ID, payload["zoneId"], payload["zoneName"], payload["zoneImageURL"], payload["zoneOwner"], payload["zoneCreationDate"], true, []string{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(payload, SDP); err != nil { errCh <- err return } if err := zm.Manager.HandleOffer(ctx, req.GetFrom(), req.GetTo(), payload, zm.signalCandidate); err != nil { errCh <- err return } case string(ZONE_ANSWER): if err := validateRequest(payload, SDP); err != nil { errCh <- err return } if err := zm.Manager.HandleAnswer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil { errCh <- err return } case string(ZONE_COUNTER_OFFER): if err := zm.Manager.HandleCounterOffer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil { errCh <- err return } case string(ZONE_WEBRTC_RENNEGOTIATION_OFFER): logger.Println("received negotiation offer") if err := validateRequest(payload, SDP); err != nil { errCh <- err return } if err := zm.Manager.HandleRennegotiationOffer(req.GetFrom(), payload[SDP]); err != nil { errCh <- err return } case string(ZONE_WEBRTC_RENNEGOTIATION_ANSWER): logger.Println("received negotiation answer") if err := validateRequest(payload, SDP); err != nil { errCh <- err return } if err := zm.Manager.HandleRennegotiationAnswer(req.GetFrom(), payload[SDP]); err != nil { errCh <- err return } case string(ZONE_WEBRTC_CANDIDATE): if err := validateRequest(payload, "candidate", "sdpMLineIndex", "sdpMid"); err != nil { errCh <- err return } logger.Println(payload) i, err := strconv.Atoi(payload["sdpMLineIndex"]) if err != nil { errCh <- err return } SDPMLineIndex := uint16(i) sdpMid := payload["sdpMid"] logger.Println(sdpMid, SDPMLineIndex) if err := zm.Manager.AddCandidate(&webrtc.ICECandidateInit{ Candidate: payload["candidate"], SDPMid: &sdpMid, SDPMLineIndex: &SDPMLineIndex, }, req.GetFrom()); err != nil { errCh <- err return } default: logger.Println("no request for zone grpc middleware") logger.Println(payload) logger.Println(req.Type) } done <- struct{}{} }() select { case <-ctx.Done(): return ctx.Err() case err = <-errCh: return case <-done: return } }