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, &notification); 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  }