github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/synchronization/endpoint/remote/protocol.go (about)

     1  package remote
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  )
     7  
     8  const (
     9  	// controlStreamBufferSize is the buffer size to use for control stream
    10  	// buffering. It should be ideally large enough to fill the kernel buffer
    11  	// for whatever stream is being used as a transport, which in our case is
    12  	// typically an OS pipe.
    13  	controlStreamCompressedBufferSize = 64 * 1024
    14  	// controlStreamCompressorBufferSize is the buffer size to use for
    15  	// compressor input and decompressor output.
    16  	controlStreamUncompressedBufferSize = 64 * 1024
    17  )
    18  
    19  // ensureValid ensures that the InitializeSynchronizationRequest's invariants
    20  // are respected.
    21  func (r *InitializeSynchronizationRequest) ensureValid() error {
    22  	// A nil initialize request is not valid.
    23  	if r == nil {
    24  		return errors.New("nil initialize request")
    25  	}
    26  
    27  	// Ensure that the session identifier is non-empty.
    28  	if r.Session == "" {
    29  		return errors.New("empty session identifier")
    30  	}
    31  
    32  	// Ensure that the session version is supported.
    33  	if !r.Version.Supported() {
    34  		return errors.New("unsupported session version")
    35  	}
    36  
    37  	// Ensure that the configuration is valid.
    38  	if err := r.Configuration.EnsureValid(false); err != nil {
    39  		return fmt.Errorf("invalid configuration: %w", err)
    40  	}
    41  
    42  	// Ensure that the root path is non-empty.
    43  	if r.Root == "" {
    44  		return errors.New("empty root path")
    45  	}
    46  
    47  	// There's no need to validate Alpha - either value is correct.
    48  
    49  	// Success.
    50  	return nil
    51  }
    52  
    53  // ensureValid ensures that the InitializeSynchronizationResponse's invariants
    54  // are respected.
    55  func (r *InitializeSynchronizationResponse) ensureValid() error {
    56  	// A nil initialize response is not valid.
    57  	if r == nil {
    58  		return errors.New("nil initialize response")
    59  	}
    60  
    61  	// Success.
    62  	return nil
    63  }
    64  
    65  // ensureValid ensures that the PollRequest's invariants are respected.
    66  func (r *PollRequest) ensureValid() error {
    67  	// A nil poll request is not valid.
    68  	if r == nil {
    69  		return errors.New("nil poll request")
    70  	}
    71  
    72  	// Success.
    73  	return nil
    74  }
    75  
    76  // ensureValid ensures that the PollCompletionRequest's invariants are
    77  // respected.
    78  func (r *PollCompletionRequest) ensureValid() error {
    79  	// A nil poll completion request is not valid.
    80  	if r == nil {
    81  		return errors.New("nil poll completion request")
    82  	}
    83  
    84  	// Success.
    85  	return nil
    86  }
    87  
    88  // ensureValid ensures that the PollResponse's invariants are respected.
    89  func (r *PollResponse) ensureValid() error {
    90  	// A nil poll response is not valid.
    91  	if r == nil {
    92  		return errors.New("nil poll response")
    93  	}
    94  
    95  	// Success.
    96  	return nil
    97  }
    98  
    99  // ensureValid ensures that the ScanRequest's invariants are respected.
   100  func (r *ScanRequest) ensureValid() error {
   101  	// A nil scan request is not valid.
   102  	if r == nil {
   103  		return errors.New("nil scan request")
   104  	}
   105  
   106  	// Ensure that the baseline snapshot signature is valid.
   107  	if err := r.BaselineSnapshotSignature.EnsureValid(); err != nil {
   108  		return fmt.Errorf("invalid baseline snapshot signature: %w", err)
   109  	}
   110  
   111  	// Full is correct regardless of value, so no validation is required.
   112  
   113  	// Success.
   114  	return nil
   115  }
   116  
   117  // ensureValid ensures that the ScanCompletionRequest's invariants are
   118  // respected.
   119  func (r *ScanCompletionRequest) ensureValid() error {
   120  	// A nil scan completion request is not valid.
   121  	if r == nil {
   122  		return errors.New("nil scan completion request")
   123  	}
   124  
   125  	// Success.
   126  	return nil
   127  }
   128  
   129  // ensureValid ensures that the ScanResponse's invariants are respected.
   130  func (r *ScanResponse) ensureValid() error {
   131  	// A nil scan response is not valid.
   132  	if r == nil {
   133  		return errors.New("nil scan response")
   134  	}
   135  
   136  	// Ensure that each snapshot delta operation is valid.
   137  	for _, operation := range r.SnapshotDelta {
   138  		if err := operation.EnsureValid(); err != nil {
   139  			return fmt.Errorf("invalid snapshot delta operation: %w", err)
   140  		}
   141  	}
   142  
   143  	// If an error is set, then make sure that no snapshot delta was provided.
   144  	if r.Error != "" {
   145  		if len(r.SnapshotDelta) > 0 {
   146  			return errors.New("non-empty snapshot delta present on error")
   147  		}
   148  	}
   149  
   150  	// Success.
   151  	return nil
   152  }
   153  
   154  // ensureValid ensures that the StageRequest's invariants are respected.
   155  func (r *StageRequest) ensureValid() error {
   156  	// A nil stage request is not valid.
   157  	if r == nil {
   158  		return errors.New("nil stage request")
   159  	}
   160  
   161  	// Ensure that there are a non-zero number of paths. This isn't an invariant
   162  	// that we really *need* to enforce, as our logic is capable of handling it,
   163  	// but it's a useful check to make sure that the client is avoiding
   164  	// transmission in these cases.
   165  	if len(r.Paths) == 0 {
   166  		return errors.New("no paths present")
   167  	}
   168  
   169  	// NOTE: We could perform an additional check that the specified paths are
   170  	// unique, but this isn't quite so cheap, and it won't break anything if
   171  	// they're not.
   172  
   173  	// HACK: We don't verify that the paths are valid (and we'd have a hard time
   174  	// doing so in any sense other than syntactically) because we use the
   175  	// filesystem.Opener infrastructure to properly traverse the synchronization
   176  	// root. It would also be expensive to verify the correctness of these paths
   177  	// and it would be of little benefit. I'd class this as a hack because it's
   178  	// sort of a layering violation (the message shouldn't know about the code
   179  	// that uses it), but I'm willing to live with it because the message is so
   180  	// tightly coupled to the endpoint implementation anyway.
   181  
   182  	// Ensure that the number of digests matches the number of paths.
   183  	if len(r.Digests) != len(r.Paths) {
   184  		return errors.New("digest count does not match path count")
   185  	}
   186  
   187  	// NOTE: We could perform an additional check that the specified digests are
   188  	// valid, but this isn't really necessary, and we'd have to handle varying
   189  	// digest lengths.
   190  
   191  	// Success.
   192  	return nil
   193  }
   194  
   195  // ensureValid ensures that StageResponse's invariants are respected.
   196  func (r *StageResponse) ensureValid(paths []string) error {
   197  	// A nil stage response is not valid.
   198  	if r == nil {
   199  		return errors.New("nil stage response")
   200  	}
   201  
   202  	// Verify that path and signature counts are sane. We have to take into
   203  	// account the shorthand that's used when all paths are requested.
   204  	p, s := len(r.Paths), len(r.Signatures)
   205  	if p == 0 && s > 0 {
   206  		if s != len(paths) {
   207  			return errors.New("signature count does not match original path count")
   208  		}
   209  		p = s
   210  	}
   211  	if p != s {
   212  		return errors.New("number of paths not equal to number of signatures")
   213  	} else if p > len(paths) {
   214  		return errors.New("number of paths requested greater than original path count")
   215  	}
   216  
   217  	// Verify that all signatures are valid.
   218  	for _, signature := range r.Signatures {
   219  		if err := signature.EnsureValid(); err != nil {
   220  			return fmt.Errorf("invalid rsync signature: %w", err)
   221  		}
   222  	}
   223  
   224  	// Verify that paths and signatures are not present if there's an error.
   225  	if r.Error != "" {
   226  		if len(r.Paths) > 0 {
   227  			return errors.New("paths/signatures present on error")
   228  		}
   229  	}
   230  
   231  	// Success.
   232  	return nil
   233  }
   234  
   235  // ensureValid ensures that SupplyRequest's invariants are respected.
   236  func (r *SupplyRequest) ensureValid() error {
   237  	// A nil supply request is not valid.
   238  	if r == nil {
   239  		return errors.New("nil supply request")
   240  	}
   241  
   242  	// Ensure that the number of paths matches the number of signatures.
   243  	if len(r.Paths) != len(r.Signatures) {
   244  		return errors.New("number of paths does not match number of signatures")
   245  	}
   246  
   247  	// Ensure that all signatures are valid.
   248  	for _, s := range r.Signatures {
   249  		if err := s.EnsureValid(); err != nil {
   250  			return fmt.Errorf("invalid base signature detected: %w", err)
   251  		}
   252  	}
   253  
   254  	// Success.
   255  	return nil
   256  }
   257  
   258  // ensureValid ensures that TransitionRequest's invariants are respected.
   259  func (r *TransitionRequest) ensureValid() error {
   260  	// A nil transition request is not valid.
   261  	if r == nil {
   262  		return errors.New("nil transition request")
   263  	}
   264  
   265  	// Ensure that each change is valid. Each should contain only synchronizable
   266  	// content.
   267  	for _, change := range r.Transitions {
   268  		if err := change.EnsureValid(true); err != nil {
   269  			return fmt.Errorf("invalid transition: %w", err)
   270  		}
   271  	}
   272  
   273  	// Success.
   274  	return nil
   275  }
   276  
   277  // ensureValid ensures that the TransitionCompletionRequest's invariants are
   278  // respected.
   279  func (r *TransitionCompletionRequest) ensureValid() error {
   280  	// A nil transition completion request is not valid.
   281  	if r == nil {
   282  		return errors.New("nil transition completion request")
   283  	}
   284  
   285  	// Success.
   286  	return nil
   287  }
   288  
   289  // ensureValid ensures that TransitionResponse's invariants are respected.
   290  func (r *TransitionResponse) ensureValid(expectedCount int) error {
   291  	// A nil transition response is not valid.
   292  	if r == nil {
   293  		return errors.New("nil transition response")
   294  	}
   295  
   296  	// Ensure that the number of results matches the number expected.
   297  	if len(r.Results) != expectedCount {
   298  		return errors.New("unexpected number of results returned")
   299  	}
   300  
   301  	// Validate that each result is a valid archive. Each should contain only
   302  	// synchronizable content.
   303  	for _, result := range r.Results {
   304  		if err := result.EnsureValid(true); err != nil {
   305  			return fmt.Errorf("invalid result returned: %w", err)
   306  		}
   307  	}
   308  
   309  	// Validate that each problem is a valid problem specification.
   310  	for _, problem := range r.Problems {
   311  		if err := problem.EnsureValid(); err != nil {
   312  			return fmt.Errorf("invalid problem returned: %w", err)
   313  		}
   314  	}
   315  
   316  	// Success.
   317  	return nil
   318  }
   319  
   320  // ensureValid ensures that EndpointRequest's invariants are respected.
   321  func (r *EndpointRequest) ensureValid() error {
   322  	// A nil endpoint request is not valid.
   323  	if r == nil {
   324  		return errors.New("nil endpoint request")
   325  	}
   326  
   327  	// Ensure that exactly one field is set.
   328  	set := 0
   329  	if r.Poll != nil {
   330  		set++
   331  	}
   332  	if r.Scan != nil {
   333  		set++
   334  	}
   335  	if r.Stage != nil {
   336  		set++
   337  	}
   338  	if r.Supply != nil {
   339  		set++
   340  	}
   341  	if r.Transition != nil {
   342  		set++
   343  	}
   344  	if set != 1 {
   345  		return errors.New("invalid number of fields set")
   346  	}
   347  
   348  	// Success.
   349  	return nil
   350  }