github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/liveshare/ports.go (about) 1 package liveshare 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 8 "github.com/sourcegraph/jsonrpc2" 9 ) 10 11 // Port describes a port exposed by the container. 12 type Port struct { 13 SourcePort int `json:"sourcePort"` 14 DestinationPort int `json:"destinationPort"` 15 SessionName string `json:"sessionName"` 16 StreamName string `json:"streamName"` 17 StreamCondition string `json:"streamCondition"` 18 BrowseURL string `json:"browseUrl"` 19 IsPublic bool `json:"isPublic"` 20 IsTCPServerConnectionEstablished bool `json:"isTCPServerConnectionEstablished"` 21 HasTLSHandshakePassed bool `json:"hasTLSHandshakePassed"` 22 Privacy string `json:"privacy"` 23 } 24 25 type PortChangeKind string 26 27 const ( 28 PortChangeKindStart PortChangeKind = "start" 29 PortChangeKindUpdate PortChangeKind = "update" 30 ) 31 32 type PortNotification struct { 33 Success bool // Helps us disambiguate between the SharingSucceeded/SharingFailed events 34 // The following are properties included in the SharingSucceeded/SharingFailed events sent by the server sharing service in the Codespace 35 Port int `json:"port"` 36 ChangeKind PortChangeKind `json:"changeKind"` 37 ErrorDetail string `json:"errorDetail"` 38 StatusCode int `json:"statusCode"` 39 } 40 41 // WaitForPortNotification waits for a port notification to be received. It returns the notification 42 // or an error if the notification is not received before the context is cancelled or it fails 43 // to parse the notification. 44 func (s *Session) WaitForPortNotification(ctx context.Context, port int, notifType PortChangeKind) (*PortNotification, error) { 45 // We use 1-buffered channels and non-blocking sends so that 46 // no goroutine gets stuck. 47 notificationCh := make(chan *PortNotification, 1) 48 errCh := make(chan error, 1) 49 50 h := func(success bool) func(*jsonrpc2.Conn, *jsonrpc2.Request) { 51 return func(conn *jsonrpc2.Conn, req *jsonrpc2.Request) { 52 notification := new(PortNotification) 53 if err := json.Unmarshal(*req.Params, ¬ification); err != nil { 54 select { 55 case errCh <- fmt.Errorf("error unmarshaling notification: %w", err): 56 default: 57 } 58 return 59 } 60 notification.Success = success 61 if notification.Port == port && notification.ChangeKind == notifType { 62 select { 63 case notificationCh <- notification: 64 default: 65 } 66 } 67 } 68 } 69 deregisterSuccess := s.registerRequestHandler("serverSharing.sharingSucceeded", h(true)) 70 deregisterFailure := s.registerRequestHandler("serverSharing.sharingFailed", h(false)) 71 defer deregisterSuccess() 72 defer deregisterFailure() 73 74 for { 75 select { 76 case <-ctx.Done(): 77 return nil, ctx.Err() 78 case err := <-errCh: 79 return nil, err 80 case notification := <-notificationCh: 81 return notification, nil 82 } 83 } 84 } 85 86 // GetSharedServers returns a description of each container port 87 // shared by a prior call to StartSharing by some client. 88 func (s *Session) GetSharedServers(ctx context.Context) ([]*Port, error) { 89 var response []*Port 90 if err := s.rpc.do(ctx, "serverSharing.getSharedServers", []string{}, &response); err != nil { 91 return nil, err 92 } 93 94 return response, nil 95 } 96 97 // UpdateSharedServerPrivacy controls port permissions and visibility scopes for who can access its URLs 98 // in the browser. 99 func (s *Session) UpdateSharedServerPrivacy(ctx context.Context, port int, visibility string) error { 100 return s.rpc.do(ctx, "serverSharing.updateSharedServerPrivacy", []interface{}{port, visibility}, nil) 101 }