github.com/atlassian/git-lob@v0.0.0-20150806085256-2386a5ed291a/providers/smart/persistent.go (about)

     1  package smart
     2  
     3  import (
     4  	"bufio"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  )
    10  
    11  // Transport implementation that uses a persistent connection to perform many
    12  // operations in serial, without having to initiate a connection each time
    13  // Most common use of this is SSH, althouth the underlying connection streams are
    14  // abstracted to allow other connections if required.
    15  type PersistentTransport struct {
    16  	// The persistent connection we're using (implemented by another class)
    17  	Connection io.ReadWriteCloser
    18  	// Buffered reader we use to scan for ends of JSON
    19  	BufferedReader *bufio.Reader
    20  }
    21  
    22  // Note *not* using net/rpc and net/rpc/jsonrpc because we want more control
    23  // golang's rpc requires a certain method format (Object.Method) and also doesn't easily
    24  // support interleaving with raw byte streams like we need to.
    25  // as per http://www.jsonrpc.org/specification
    26  type JsonRequest struct {
    27  	// Go's JSON support only uses public fields but JSON-RPC requires lower case
    28  	Id     int
    29  	Method string
    30  	// RawMessage allows us to store late-resolved, message-specific nested types
    31  	// requires an extra couple of steps though; even though RawMessage is a []byte, it's not
    32  	// JSON itself. You need to convert JSON to/from RawMessage as well as JSON to/from the structure
    33  	// - see RawMessage's own UnmarshalJSON/MarshalJSON for this extra step
    34  	Params *json.RawMessage
    35  }
    36  type JsonResponse struct {
    37  	Id    int
    38  	Error interface{}
    39  	// RawMessage allows us to store late-resolved, message-specific nested types
    40  	// requires an extra couple of steps though; even though RawMessage is a []byte, it's not
    41  	// JSON itself. You need to convert JSON to/from RawMessage as well as JSON to/from the structure
    42  	// - see RawMessage's own UnmarshalJSON/MarshalJSON for this extra step
    43  	Result *json.RawMessage
    44  }
    45  
    46  var (
    47  	latestRequestId int = 1
    48  )
    49  
    50  func NewJsonRequest(method string, params interface{}) (*JsonRequest, error) {
    51  	ret := &JsonRequest{
    52  		Id:     latestRequestId,
    53  		Method: method,
    54  	}
    55  	var err error
    56  	ret.Params, err = embedStructInJsonRawMessage(params)
    57  	latestRequestId++
    58  	return ret, err
    59  }
    60  
    61  func NewJsonResponse(id int, result interface{}) (*JsonResponse, error) {
    62  	ret := &JsonResponse{
    63  		Id: id,
    64  	}
    65  	var err error
    66  	ret.Result, err = embedStructInJsonRawMessage(result)
    67  	return ret, err
    68  }
    69  func NewJsonErrorResponse(id int, err interface{}) *JsonResponse {
    70  	ret := &JsonResponse{
    71  		Id:    id,
    72  		Error: err,
    73  	}
    74  	return ret
    75  }
    76  
    77  func embedStructInJsonRawMessage(in interface{}) (*json.RawMessage, error) {
    78  	// Encode nested struct ready for transmission so that it can be late unmarshalled at the other end
    79  	// Need to do this & declare as RawMessage rather than interface{} in struct otherwise unmarshalling
    80  	// at other end will turn it into a simple array/map
    81  	// Doesn't affect the wire bytes; they're still nested JSON in the same way as if you marshalled the whole struct
    82  	// this is just a golang method to defer resolving on unmarshal
    83  	ret := &json.RawMessage{}
    84  	innerbytes, err := json.Marshal(in)
    85  	if err != nil {
    86  		return ret, fmt.Errorf("Unable to marshal struct to JSON: %v %v", in, err.Error())
    87  	}
    88  	err = ret.UnmarshalJSON(innerbytes)
    89  	if err != nil {
    90  		return ret, fmt.Errorf("Unable to convert JSON to RawMessage: %v %v", string(innerbytes), err.Error())
    91  	}
    92  
    93  	return ret, nil
    94  
    95  }
    96  
    97  // Create a new persistent transport & connect
    98  func NewPersistentTransport(conn io.ReadWriteCloser) *PersistentTransport {
    99  	return &PersistentTransport{
   100  		Connection:     conn,
   101  		BufferedReader: bufio.NewReader(conn),
   102  	}
   103  }
   104  
   105  type ExitRequest struct {
   106  }
   107  type ExitResponse struct {
   108  }
   109  
   110  // Release any resources associated with this transport (including any persostent connections)
   111  func (self *PersistentTransport) Release() {
   112  	if self.Connection != nil {
   113  		// terminate server-side
   114  		params := ExitRequest{}
   115  		resp := ExitResponse{}
   116  		err := self.doFullJSONRequestResponse("Exit", &params, &resp)
   117  		if err != nil {
   118  			fmt.Println("Problem exiting persistent transport:", err)
   119  		}
   120  
   121  		self.Connection.Close()
   122  		self.Connection = nil
   123  	}
   124  	self.BufferedReader = nil
   125  }
   126  
   127  // Perform a full JSON-RPC style call with JSON request and response
   128  func (self *PersistentTransport) doFullJSONRequestResponse(method string, params interface{}, result interface{}) error {
   129  
   130  	req, err := NewJsonRequest(method, params)
   131  	if err != nil {
   132  		return err
   133  	}
   134  	err = self.sendJSONRequest(req)
   135  	if err != nil {
   136  		return err
   137  	}
   138  	err = self.readFullJSONResponse(req, result)
   139  	if err != nil {
   140  		return err
   141  	}
   142  	// result is now populated
   143  	return nil
   144  
   145  }
   146  
   147  // Perform a JSON request that results in a byte stream as a response & download to out (with callbacks if required)
   148  func (self *PersistentTransport) doJSONRequestDownload(method string, params interface{},
   149  	sz int64, out io.Writer, callback TransportProgressCallback) error {
   150  
   151  	req, err := NewJsonRequest(method, params)
   152  	if err != nil {
   153  		return err
   154  	}
   155  	err = self.sendJSONRequest(req)
   156  	if err != nil {
   157  		return err
   158  	}
   159  	err = self.receiveRawData(sz, out, callback)
   160  	if err != nil {
   161  		return err
   162  	}
   163  
   164  	return nil
   165  
   166  }
   167  
   168  // Late-bind a method-specific structure from the raw message
   169  func ExtractStructFromJsonRawMessage(raw *json.RawMessage, out interface{}) error {
   170  	nestedbytes, err := raw.MarshalJSON()
   171  	if err != nil {
   172  		return fmt.Errorf("Unable to extract type-specific JSON from: %v\n%v", string(*raw), err.Error())
   173  	}
   174  	err = json.Unmarshal(nestedbytes, &out)
   175  	if err != nil {
   176  		return fmt.Errorf("Unable to decode type-specific result: %v\n%v", string(nestedbytes), err.Error())
   177  	}
   178  	return nil
   179  
   180  }
   181  
   182  // Send a JSON request but don't read any response
   183  func (self *PersistentTransport) sendJSONRequest(req interface{}) error {
   184  	if self.Connection == nil || self.BufferedReader == nil {
   185  		return errors.New("Not connected")
   186  	}
   187  
   188  	reqbytes, err := json.Marshal(req)
   189  	if err != nil {
   190  		return fmt.Errorf("Error encoding %v to JSON: %v", err.Error())
   191  	}
   192  	// Append the binary 0 delimiter that server uses to read up to
   193  	reqbytes = append(reqbytes, byte(0))
   194  	_, err = self.Connection.Write(reqbytes)
   195  	if err != nil {
   196  		return fmt.Errorf("Error writing request bytes to connection: %v", err.Error())
   197  	}
   198  
   199  	return nil
   200  }
   201  
   202  func (self *PersistentTransport) readJSONResponse() (*JsonResponse, error) {
   203  	jsonbytes, err := self.BufferedReader.ReadBytes(byte(0))
   204  	if err != nil {
   205  		return nil, fmt.Errorf("Unable to read response from server: %v", err.Error())
   206  	}
   207  	// remove terminator before unmarshalling
   208  	jsonbytes = jsonbytes[:len(jsonbytes)-1]
   209  	response := &JsonResponse{}
   210  	err = json.Unmarshal(jsonbytes, response)
   211  	if err != nil {
   212  		return nil, fmt.Errorf("Unable to decode JSON response from server: %v\n%v", string(jsonbytes), err.Error())
   213  	}
   214  	return response, nil
   215  }
   216  
   217  // Check a response object; req can be nil, if so doesn't check that Ids match
   218  func (self *PersistentTransport) checkJSONResponse(req *JsonRequest, resp *JsonResponse) error {
   219  	if resp.Error != nil {
   220  		return fmt.Errorf("Error response from server: %v", resp.Error)
   221  	}
   222  	if req != nil && req.Id != resp.Id {
   223  		return fmt.Errorf("Response from server has wrong Id, request: %d response: %d", req.Id, resp.Id)
   224  	}
   225  	return nil
   226  }
   227  
   228  // Read a JSON response, check it, and pull out the nested method-specific & write to result
   229  // originalReq is optional and can be left nil but if supplied Ids will be checked for matching
   230  func (self *PersistentTransport) readFullJSONResponse(originalReq *JsonRequest, result interface{}) error {
   231  	// read response (buffered) up to binary 0 which terminates JSON
   232  	response, err := self.readJSONResponse()
   233  	if err != nil {
   234  		return err
   235  	}
   236  	// early validation
   237  	err = self.checkJSONResponse(originalReq, response)
   238  	if err != nil {
   239  		return err
   240  	}
   241  	// response.Result is left as raw since it depends on the type of the expected result
   242  	// so now unmarshal the nested part
   243  	err = ExtractStructFromJsonRawMessage(response.Result, &result)
   244  	if err != nil {
   245  		return err
   246  	}
   247  	return nil
   248  }
   249  
   250  const PersistentTransportBufferSize = int64(131072)
   251  
   252  func (self *PersistentTransport) sendRawData(sz int64, source io.Reader, callback TransportProgressCallback) error {
   253  
   254  	if sz == 0 {
   255  		return nil
   256  	}
   257  
   258  	var copysize int64 = 0
   259  	for {
   260  		c := PersistentTransportBufferSize
   261  		if (sz - copysize) < c {
   262  			c = sz - copysize
   263  		}
   264  		if c <= 0 {
   265  			break
   266  		}
   267  		n, err := io.CopyN(self.Connection, source, c)
   268  		copysize += n
   269  		if n > 0 && callback != nil && sz > 0 {
   270  			callback(copysize, sz)
   271  		}
   272  		if err != nil {
   273  			return err
   274  		}
   275  	}
   276  	if copysize != sz {
   277  		return fmt.Errorf("Transferred bytes did not match expected size; transferred %d, expected %d", copysize, sz)
   278  	}
   279  
   280  	return nil
   281  }
   282  
   283  func (self *PersistentTransport) receiveRawData(sz int64, out io.Writer, callback TransportProgressCallback) error {
   284  
   285  	if sz == 0 {
   286  		return nil
   287  	}
   288  
   289  	var copysize int64 = 0
   290  	for {
   291  		c := PersistentTransportBufferSize
   292  		if (sz - copysize) < c {
   293  			c = sz - copysize
   294  		}
   295  		if c <= 0 {
   296  			break
   297  		}
   298  		// Must read from buffered reader consistently
   299  		n, err := io.CopyN(out, self.BufferedReader, c)
   300  		copysize += n
   301  		if n > 0 && callback != nil && sz > 0 {
   302  			callback(copysize, sz)
   303  		}
   304  		if err != nil {
   305  			return err
   306  		}
   307  	}
   308  	if copysize != sz {
   309  		return fmt.Errorf("Transferred bytes did not match expected size; transferred %d, expected %d", copysize, sz)
   310  	}
   311  
   312  	return nil
   313  }
   314  
   315  // Just a specially identified persistent connection error so we can re-try
   316  type ConnectionError error
   317  
   318  type QueryCapsRequest struct {
   319  }
   320  
   321  type QueryCapsResponse struct {
   322  	Caps []string
   323  }
   324  
   325  // Ask the server for a list of capabilities
   326  func (self *PersistentTransport) QueryCaps() ([]string, error) {
   327  	params := QueryCapsRequest{}
   328  	resp := QueryCapsResponse{}
   329  	err := self.doFullJSONRequestResponse("QueryCaps", &params, &resp)
   330  	if err != nil {
   331  		return nil, err
   332  	}
   333  	return resp.Caps, nil
   334  }
   335  
   336  type SetEnabledCapsRequest struct {
   337  	EnableCaps []string
   338  }
   339  type SetEnabledCapsResponse struct {
   340  }
   341  
   342  // Request that the server enable capabilities for this exchange (note, non-persistent transports can store & send this with every request)
   343  func (self *PersistentTransport) SetEnabledCaps(caps []string) error {
   344  	params := SetEnabledCapsRequest{EnableCaps: caps}
   345  	resp := SetEnabledCapsResponse{}
   346  	err := self.doFullJSONRequestResponse("SetEnabledCaps", &params, &resp)
   347  	if err != nil {
   348  		return err
   349  	}
   350  	return nil
   351  }
   352  
   353  type FileExistsRequest struct {
   354  	LobSHA   string
   355  	Type     string
   356  	ChunkIdx int
   357  }
   358  type FileExistsResponse struct {
   359  	Exists bool
   360  	Size   int64
   361  }
   362  
   363  // Return whether LOB metadata exists on the server
   364  func (self *PersistentTransport) MetadataExists(lobsha string) (bool, int64, error) {
   365  	params := FileExistsRequest{
   366  		LobSHA: lobsha,
   367  		Type:   "meta",
   368  	}
   369  	resp := FileExistsResponse{}
   370  	err := self.doFullJSONRequestResponse("FileExists", &params, &resp)
   371  	if err != nil {
   372  		return false, 0, err
   373  	}
   374  	return resp.Exists, resp.Size, nil
   375  }
   376  
   377  // Return whether LOB chunk content exists on the server
   378  func (self *PersistentTransport) ChunkExists(lobsha string, chunk int) (bool, int64, error) {
   379  	params := FileExistsRequest{
   380  		LobSHA:   lobsha,
   381  		Type:     "chunk",
   382  		ChunkIdx: chunk,
   383  	}
   384  	resp := FileExistsResponse{}
   385  	err := self.doFullJSONRequestResponse("FileExists", &params, &resp)
   386  	if err != nil {
   387  		return false, 0, err
   388  	}
   389  	return resp.Exists, resp.Size, nil
   390  }
   391  
   392  type FileExistsOfSizeRequest struct {
   393  	LobSHA   string
   394  	Type     string
   395  	ChunkIdx int
   396  	Size     int64
   397  }
   398  type FileExistsOfSizeResponse struct {
   399  	Result bool
   400  }
   401  
   402  // Return whether LOB chunk content exists on the server, and is of a specific size
   403  func (self *PersistentTransport) ChunkExistsAndIsOfSize(lobsha string, chunk int, sz int64) (bool, error) {
   404  	params := FileExistsOfSizeRequest{
   405  		LobSHA:   lobsha,
   406  		Type:     "chunk",
   407  		ChunkIdx: chunk,
   408  		Size:     sz,
   409  	}
   410  	resp := FileExistsOfSizeResponse{}
   411  	err := self.doFullJSONRequestResponse("FileExistsOfSize", &params, &resp)
   412  	if err != nil {
   413  		return false, err
   414  	}
   415  	return resp.Result, nil
   416  }
   417  
   418  type LOBExistsRequest struct {
   419  	LobSHA string
   420  }
   421  type LOBExistsResponse struct {
   422  	Exists bool
   423  	Size   int64
   424  }
   425  
   426  // Return whether LOB exists in entirety on the server
   427  func (self *PersistentTransport) LOBExists(lobsha string) (bool, int64, error) {
   428  	params := LOBExistsRequest{
   429  		LobSHA: lobsha,
   430  	}
   431  	resp := LOBExistsResponse{}
   432  	err := self.doFullJSONRequestResponse("LOBExists", &params, &resp)
   433  	if err != nil {
   434  		return false, 0, err
   435  	}
   436  	return resp.Exists, resp.Size, nil
   437  }
   438  
   439  type UploadFileRequest struct {
   440  	LobSHA   string
   441  	Type     string
   442  	ChunkIdx int
   443  	Size     int64
   444  }
   445  type UploadFileStartResponse struct {
   446  	OKToSend bool
   447  }
   448  type UploadFileCompleteResponse struct {
   449  	ReceivedOK bool
   450  }
   451  
   452  // Upload metadata for a LOB (from a stream); no progress callback as very small
   453  func (self *PersistentTransport) UploadMetadata(lobsha string, sz int64, data io.Reader) error {
   454  	params := UploadFileRequest{
   455  		LobSHA: lobsha,
   456  		Type:   "meta",
   457  		Size:   sz,
   458  	}
   459  	resp := UploadFileStartResponse{}
   460  	err := self.doFullJSONRequestResponse("UploadFile", &params, &resp)
   461  	if err != nil {
   462  		return fmt.Errorf("Error while uploading metadata for %v (while sending UploadFile JSON request): %v", lobsha, err.Error())
   463  	}
   464  	if resp.OKToSend {
   465  		// Send that data (all at once, metafiles aren't big)
   466  		err = self.sendRawData(sz, data, nil)
   467  		if err != nil {
   468  			return fmt.Errorf("Error while uploading metadata for %v (while sending raw content): %v", lobsha, err.Error())
   469  		}
   470  		// Now read response to sent data
   471  		received := UploadFileCompleteResponse{}
   472  		err = self.readFullJSONResponse(nil, &received)
   473  		if err != nil {
   474  			return fmt.Errorf("Error while uploading metadata for %v (response to raw content): %v", lobsha, err.Error())
   475  		}
   476  		if !received.ReceivedOK {
   477  			return fmt.Errorf("Data not fully received while uploading metadata for %v: Unknown server error", lobsha)
   478  		}
   479  
   480  	} else {
   481  		return fmt.Errorf("Server rejected request to upload metadata for %v (no other error)", lobsha)
   482  	}
   483  	return nil
   484  }
   485  
   486  // Upload chunk content for a LOB (from a stream); must call back progress
   487  func (self *PersistentTransport) UploadChunk(lobsha string, chunk int, sz int64, data io.Reader, callback TransportProgressCallback) error {
   488  	params := UploadFileRequest{
   489  		LobSHA:   lobsha,
   490  		Type:     "chunk",
   491  		ChunkIdx: chunk,
   492  		Size:     sz,
   493  	}
   494  	resp := UploadFileStartResponse{}
   495  	err := self.doFullJSONRequestResponse("UploadFile", &params, &resp)
   496  	if err != nil {
   497  		return fmt.Errorf("Error while uploading chunk %d for %v (while sending UploadFile JSON request): %v", chunk, lobsha, err.Error())
   498  	}
   499  	if resp.OKToSend {
   500  		// Send data, this does it in batches and calls back
   501  		err = self.sendRawData(sz, data, callback)
   502  		if err != nil {
   503  			return fmt.Errorf("Error while uploading chunk %d for %v (while sending raw content): %v", chunk, lobsha, err.Error())
   504  		}
   505  		// Now read response to sent data
   506  		received := UploadFileCompleteResponse{}
   507  		err = self.readFullJSONResponse(nil, &received)
   508  		if err != nil {
   509  			return fmt.Errorf("Error while uploading chunk %d for %v (response to raw content): %v", chunk, lobsha, err.Error())
   510  		}
   511  		if !received.ReceivedOK {
   512  			return fmt.Errorf("Data not fully received while uploading chunk %d for %v: Unknown server error", chunk, lobsha)
   513  		}
   514  
   515  	} else {
   516  		return fmt.Errorf("Server rejected request to upload chunk %d for %v (no other error)", chunk, lobsha)
   517  	}
   518  	return nil
   519  }
   520  
   521  type DownloadFilePrepareRequest struct {
   522  	LobSHA   string
   523  	Type     string
   524  	ChunkIdx int
   525  }
   526  type DownloadFilePrepareResponse struct {
   527  	Size int64
   528  }
   529  type DownloadFileStartRequest struct {
   530  	LobSHA   string
   531  	Type     string
   532  	ChunkIdx int
   533  	Size     int64
   534  }
   535  
   536  // Download metadata for a LOB (to a stream); no progress callback as very small
   537  func (self *PersistentTransport) DownloadMetadata(lobsha string, out io.Writer) error {
   538  	prepparams := DownloadFilePrepareRequest{
   539  		LobSHA: lobsha,
   540  		Type:   "meta",
   541  	}
   542  	resp := DownloadFilePrepareResponse{}
   543  	err := self.doFullJSONRequestResponse("DownloadFilePrepare", &prepparams, &resp)
   544  	if err != nil {
   545  		return fmt.Errorf("Error while downloading metadata for %v (while sending DownloadFilePrepare JSON request): %v", lobsha, err.Error())
   546  	}
   547  	startparams := DownloadFileStartRequest{
   548  		LobSHA: lobsha,
   549  		Type:   "meta",
   550  		Size:   resp.Size,
   551  	}
   552  
   553  	// Response is just raw byte data - no callback as small enough not to need one
   554  	err = self.doJSONRequestDownload("DownloadFileStart", &startparams, resp.Size, out, nil)
   555  	if err != nil {
   556  		return fmt.Errorf("Error while downloading metadata for %v (during download): %v", lobsha, err.Error())
   557  	}
   558  
   559  	return nil
   560  }
   561  
   562  // Download chunk content for a LOB (from a stream); must call back progress
   563  // This is a non-delta download operation, just provide entire chunk content
   564  func (self *PersistentTransport) DownloadChunk(lobsha string, chunk int, out io.Writer, callback TransportProgressCallback) error {
   565  	prepparams := DownloadFilePrepareRequest{
   566  		LobSHA:   lobsha,
   567  		Type:     "chunk",
   568  		ChunkIdx: chunk,
   569  	}
   570  	resp := DownloadFilePrepareResponse{}
   571  	err := self.doFullJSONRequestResponse("DownloadFilePrepare", &prepparams, &resp)
   572  	if err != nil {
   573  		return fmt.Errorf("Error while downloading chunk %d for %v (while sending DownloadFilePrepare JSON request): %v", chunk, lobsha, err.Error())
   574  	}
   575  	startparams := DownloadFileStartRequest{
   576  		LobSHA:   lobsha,
   577  		Type:     "chunk",
   578  		ChunkIdx: chunk,
   579  		Size:     resp.Size,
   580  	}
   581  
   582  	// Response is just raw byte data - no callback as small enough not to need one
   583  	err = self.doJSONRequestDownload("DownloadFileStart", &startparams, resp.Size, out, callback)
   584  	if err != nil {
   585  		return fmt.Errorf("Error while downloading chunk %d for %v (during download): %v", chunk, lobsha, err.Error())
   586  	}
   587  
   588  	return nil
   589  
   590  }
   591  
   592  type GetFirstCompleteLOBFromListRequest struct {
   593  	LobSHAs []string
   594  }
   595  type GetFirstCompleteLOBFromListResponse struct {
   596  	FirstSHA string
   597  }
   598  
   599  // Return the LOB which the server has a complete copy of, from a list of candidates
   600  // Server must test in the order provided & return the earliest one which is complete on the server
   601  // Server doesn't have to test full integrity of LOB, just completeness (check size against meta)
   602  // Return a blank string if none are available
   603  func (self *PersistentTransport) GetFirstCompleteLOBFromList(candidateSHAs []string) (string, error) {
   604  	params := GetFirstCompleteLOBFromListRequest{candidateSHAs}
   605  	resp := GetFirstCompleteLOBFromListResponse{}
   606  	err := self.doFullJSONRequestResponse("PickCompleteLOB", &params, &resp)
   607  	if err != nil {
   608  		return "", fmt.Errorf("Error asking server for first LOB from list %v: %v", candidateSHAs, err.Error())
   609  	}
   610  	return resp.FirstSHA, nil
   611  }
   612  
   613  type UploadDeltaRequest struct {
   614  	BaseLobSHA   string
   615  	TargetLobSHA string
   616  	Size         int64
   617  }
   618  type UploadDeltaStartResponse struct {
   619  	OKToSend bool
   620  }
   621  type UploadDeltaCompleteResponse struct {
   622  	ReceivedOK bool
   623  }
   624  
   625  // Upload a binary delta to apply against a LOB the server already has, to generate a new LOB
   626  // Deltas apply to whole LOB content and are not per-chunk
   627  // Returns a boolean to determine whether the upload was accepted or not (server may prefer not to accept, not an error)
   628  // In the case of false return, client will fall back to non-delta upload.
   629  // On true, server must return nil error only after data is fully received, applied, saved as targetSHA and the
   630  // integrity confirmed by recalculating the SHA of the final patched data.
   631  // This only does the content, not the metadata; you should have already called UploadMetadata beforehand.
   632  func (self *PersistentTransport) UploadDelta(baseSHA, targetSHA string, deltaSize int64, data io.Reader, callback TransportProgressCallback) (bool, error) {
   633  	params := UploadDeltaRequest{
   634  		BaseLobSHA:   baseSHA,
   635  		TargetLobSHA: targetSHA,
   636  		Size:         deltaSize,
   637  	}
   638  	resp := UploadDeltaStartResponse{}
   639  	err := self.doFullJSONRequestResponse("UploadDelta", &params, &resp)
   640  	if err != nil {
   641  		return false, fmt.Errorf("Error calling UploadDelta JSON request from %v to %v: %v", baseSHA, targetSHA, err.Error())
   642  	}
   643  	// Server can opt not to accept the delta, caller should fall back to simpler upload if so
   644  	var sentOK bool
   645  	if resp.OKToSend {
   646  		// Send data, this does it in batches and calls back
   647  		err = self.sendRawData(deltaSize, data, callback)
   648  		if err != nil {
   649  			return false, fmt.Errorf("Error uploading delta content from %v to %v: %v", baseSHA, targetSHA, err.Error())
   650  		}
   651  		// Now read response to sent data
   652  		received := UploadDeltaCompleteResponse{}
   653  		err = self.readFullJSONResponse(nil, &received)
   654  		if err != nil {
   655  			return false, fmt.Errorf("Error in UploadDelta from %v to %v (response to raw content): %v", baseSHA, targetSHA, err.Error())
   656  		}
   657  		if !received.ReceivedOK {
   658  			return false, fmt.Errorf("Data not fully received in UploadDelta from %v to %v: Unknown server error", baseSHA, targetSHA)
   659  		}
   660  		sentOK = true
   661  
   662  	}
   663  	return sentOK, nil
   664  }
   665  
   666  type DownloadDeltaPrepareRequest struct {
   667  	BaseLobSHA   string
   668  	TargetLobSHA string
   669  }
   670  type DownloadDeltaPrepareResponse struct {
   671  	Size int64
   672  }
   673  type DownloadDeltaStartRequest struct {
   674  	BaseLobSHA   string
   675  	TargetLobSHA string
   676  	Size         int64
   677  }
   678  
   679  // Prepare a binary delta between 2 LOBs and report the size
   680  func (self *PersistentTransport) DownloadDeltaPrepare(baseSHA, targetSHA string) (int64, error) {
   681  	prepparams := DownloadDeltaPrepareRequest{
   682  		BaseLobSHA:   baseSHA,
   683  		TargetLobSHA: targetSHA,
   684  	}
   685  	resp := DownloadDeltaPrepareResponse{}
   686  	err := self.doFullJSONRequestResponse("DownloadDeltaPrepare", &prepparams, &resp)
   687  	if err != nil {
   688  		return 0, fmt.Errorf("Error in DownloadDeltaPrepare from %v to %v: %v", baseSHA, targetSHA, err.Error())
   689  	}
   690  	return resp.Size, nil
   691  }
   692  
   693  // Generate and download a binary delta that the client can apply locally to generate a new LOB
   694  // Deltas apply to whole LOB content and are not per-chunk
   695  // The server should respect sizeLimit and if the delta is larger than that, abandon the process
   696  // Return a bool to indicate whether the delta went ahead or not (client will fall back to non-delta on false)
   697  func (self *PersistentTransport) DownloadDelta(baseSHA, targetSHA string, sizeLimit int64, out io.Writer, callback TransportProgressCallback) (bool, error) {
   698  	sz, err := self.DownloadDeltaPrepare(baseSHA, targetSHA)
   699  	if err != nil {
   700  		return false, err
   701  	}
   702  	if sz > sizeLimit {
   703  		// delta is too big
   704  		return false, nil
   705  	}
   706  	startparams := DownloadDeltaStartRequest{
   707  		BaseLobSHA:   baseSHA,
   708  		TargetLobSHA: targetSHA,
   709  		Size:         sz,
   710  	}
   711  
   712  	// Response is just raw byte data - no callback as small enough not to need one
   713  	err = self.doJSONRequestDownload("DownloadDeltaStart", &startparams, sz, out, callback)
   714  	if err != nil {
   715  		return false, fmt.Errorf("Error while downloading LOB delta from %v to %v: %v", baseSHA, targetSHA, err.Error())
   716  	}
   717  	// It's up to the caller to apply the delta
   718  	return true, nil
   719  }