Fossil

Check-in [505d9d48eb]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add documentation for the --transport-command options on the various sync commands. Improvements to the documentation of the sync protocol.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 505d9d48eb5966c0bc04779e043eaf245f56e2554a28e3e6b29770932bd07107
User & Date: drh 2021-12-22 20:40:46.717
Context
2021-12-22
20:45
Update the change log with recent enhancements. ... (check-in: 88a72f1e72 user: drh tags: trunk)
20:40
Add documentation for the --transport-command options on the various sync commands. Improvements to the documentation of the sync protocol. ... (check-in: 505d9d48eb user: drh tags: trunk)
17:11
Add the --transport-command option to the "fossil clone" command. ... (check-in: 14b3f48e3c user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/clone.c.
130
131
132
133
134
135
136

137
138
139
140
141
142
143
**    --nocompress               Omit extra delta compression
**    --no-open                  Clone only.  Do not open a check-out.
**    --once                     Don't remember the URI.
**    --private                  Also clone private branches
**    --save-http-password       Remember the HTTP password without asking
**    --ssh-command|-c SSH       Use SSH as the "ssh" command
**    --ssl-identity FILENAME    Use the SSL identity if requested by the server

**    -u|--unversioned           Also sync unversioned content
**    -v|--verbose               Show more statistics in output
**    --workdir DIR              Also open a checkout in DIR
**
** See also: [[init]], [[open]]
*/
void clone_cmd(void){







>







130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
**    --nocompress               Omit extra delta compression
**    --no-open                  Clone only.  Do not open a check-out.
**    --once                     Don't remember the URI.
**    --private                  Also clone private branches
**    --save-http-password       Remember the HTTP password without asking
**    --ssh-command|-c SSH       Use SSH as the "ssh" command
**    --ssl-identity FILENAME    Use the SSL identity if requested by the server
**    --transport-command CMD    Use CMD to move messages to the server and back
**    -u|--unversioned           Also sync unversioned content
**    -v|--verbose               Show more statistics in output
**    --workdir DIR              Also open a checkout in DIR
**
** See also: [[init]], [[open]]
*/
void clone_cmd(void){
Changes to src/http.c.
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
  zUplink = fossil_temp_filename();
  zDownlink = fossil_temp_filename();
  zFullUrl = url_full(&g.url);
  zCmd = mprintf("%s %$ %$ %$", g.zHttpCmd, zFullUrl,zUplink,zDownlink);
  fossil_free(zFullUrl);
  blob_write_to_file(pSend, zUplink);
  if( g.fHttpTrace ){
    fossil_print("RUN: %s\n", zCmd);
  }
  rc = fossil_system(zCmd);
  if( rc ){
    fossil_warning("Transport command failed: %s\n", zCmd);
  }    
  fossil_free(zCmd);
  file_delete(zUplink);







|







230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
  zUplink = fossil_temp_filename();
  zDownlink = fossil_temp_filename();
  zFullUrl = url_full(&g.url);
  zCmd = mprintf("%s %$ %$ %$", g.zHttpCmd, zFullUrl,zUplink,zDownlink);
  fossil_free(zFullUrl);
  blob_write_to_file(pSend, zUplink);
  if( g.fHttpTrace ){
    fossil_print("RUN %s\n", zCmd);
  }
  rc = fossil_system(zCmd);
  if( rc ){
    fossil_warning("Transport command failed: %s\n", zCmd);
  }    
  fossil_free(zCmd);
  file_delete(zUplink);
Changes to src/http_transport.c.
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
  blob_append_escaped_arg(&zCmd, pUrlData->fossil, 1);
  blob_append(&zCmd, " test-http", 10);
  if( pUrlData->path && pUrlData->path[0] ){
    blob_append_escaped_arg(&zCmd, pUrlData->path, 1);
  }else{
    fossil_fatal("ssh:// URI does not specify a path to the repository");
  }
  if( g.fSshTrace ){
    fossil_print("%s\n", blob_str(&zCmd));  /* Show the whole SSH command */
  }
  popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid, 0);
  if( sshPid==0 ){
    socket_set_errmsg("cannot start ssh tunnel using [%b]", &zCmd);
  }
  blob_reset(&zCmd);
  return sshPid==0;







|
|







139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
  blob_append_escaped_arg(&zCmd, pUrlData->fossil, 1);
  blob_append(&zCmd, " test-http", 10);
  if( pUrlData->path && pUrlData->path[0] ){
    blob_append_escaped_arg(&zCmd, pUrlData->path, 1);
  }else{
    fossil_fatal("ssh:// URI does not specify a path to the repository");
  }
  if( g.fSshTrace || g.fHttpTrace ){
    fossil_print("RUN %s\n", blob_str(&zCmd));  /* Show the whole SSH command */
  }
  popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid, 0);
  if( sshPid==0 ){
    socket_set_errmsg("cannot start ssh tunnel using [%b]", &zCmd);
  }
  blob_reset(&zCmd);
  return sshPid==0;
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
      rc = ssl_open(pUrlData);
      if( rc==0 ) transport.isOpen = 1;
#else
      socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support");
      rc = 1;
#endif
    }else if( pUrlData->isFile ){
      sqlite3_uint64 iRandId;
      if( !db_looks_like_a_repository(pUrlData->name) ){
        fossil_fatal("not a fossil repository: \"%s\"", pUrlData->name);
      }
      sqlite3_randomness(sizeof(iRandId), &iRandId);
      transport.zOutFile = mprintf("%s-%llu-out.http",
                                       g.zRepositoryName, iRandId);
      transport.zInFile = mprintf("%s-%llu-in.http",
                                       g.zRepositoryName, iRandId);
      transport.pFile = fossil_fopen(transport.zOutFile, "wb");
      if( transport.pFile==0 ){
        fossil_fatal("cannot output temporary file: %s", transport.zOutFile);
      }
      transport.isOpen = 1;
    }else{
      rc = socket_open(pUrlData);







<



<
|
<
|
<







175
176
177
178
179
180
181

182
183
184

185

186

187
188
189
190
191
192
193
      rc = ssl_open(pUrlData);
      if( rc==0 ) transport.isOpen = 1;
#else
      socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support");
      rc = 1;
#endif
    }else if( pUrlData->isFile ){

      if( !db_looks_like_a_repository(pUrlData->name) ){
        fossil_fatal("not a fossil repository: \"%s\"", pUrlData->name);
      }

      transport.zOutFile = fossil_temp_filename();

      transport.zInFile = fossil_temp_filename();

      transport.pFile = fossil_fopen(transport.zOutFile, "wb");
      if( transport.pFile==0 ){
        fossil_fatal("cannot output temporary file: %s", transport.zOutFile);
      }
      transport.isOpen = 1;
    }else{
      rc = socket_open(pUrlData);
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
    }else if( pUrlData->isFile ){
      if( transport.pFile ){
        fclose(transport.pFile);
        transport.pFile = 0;
      }
      file_delete(transport.zInFile);
      file_delete(transport.zOutFile);
      free(transport.zInFile);
      free(transport.zOutFile);
    }else{
      socket_close();
    }
    transport.isOpen = 0;
  }
}








|
|







220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
    }else if( pUrlData->isFile ){
      if( transport.pFile ){
        fclose(transport.pFile);
        transport.pFile = 0;
      }
      file_delete(transport.zInFile);
      file_delete(transport.zOutFile);
      sqlite3_free(transport.zInFile);
      sqlite3_free(transport.zOutFile);
    }else{
      socket_close();
    }
    transport.isOpen = 0;
  }
}

278
279
280
281
282
283
284

285
286
287
288
289
290
291
  if( pUrlData->isFile ){
    char *zCmd;
    fclose(transport.pFile);
    zCmd = mprintf("%$ http --in %$ --out %$ --ipaddr 127.0.0.1"
                   " %$ --localauth",
       g.nameOfExe, transport.zOutFile, transport.zInFile, pUrlData->name
    );

    fossil_system(zCmd);
    free(zCmd);
    transport.pFile = fossil_fopen(transport.zInFile, "rb");
  }
}

/*







>







274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
  if( pUrlData->isFile ){
    char *zCmd;
    fclose(transport.pFile);
    zCmd = mprintf("%$ http --in %$ --out %$ --ipaddr 127.0.0.1"
                   " %$ --localauth",
       g.nameOfExe, transport.zOutFile, transport.zInFile, pUrlData->name
    );
    if( g.fHttpTrace ) fossil_print("RUN %s\n", zCmd);
    fossil_system(zCmd);
    free(zCmd);
    transport.pFile = fossil_fopen(transport.zInFile, "rb");
  }
}

/*
Changes to src/sync.c.
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253





254
255
256
257
258
259
260
  }
  if( ((*pSyncFlags) & SYNC_PULL)!=0
   && find_option("share-links",0,0)!=0
  ){
    *pSyncFlags |= SYNC_SHARE_LINKS;
  }

  /* Undocumented option:  --transport-command COMMAND
  **
  ** Causes COMMAND to be run with three arguments in order to talk
  ** to the server.
  **
  **       COMMAND URL PAYLOAD REPLY
  **
  ** URL is the server name.  PAYLOAD is the name of a temporary file
  ** that will contain the xfer-protocol payload to send to the server.
  ** REPLY is a temporary filename in which COMMAND should write the
  ** content of the reply from the server.





  */
  g.zHttpCmd = find_option("transport-command",0,1);

  url_proxy_options();
  clone_ssh_find_options();
  if( !uvOnly ) db_find_and_open_repository(0, 0);
  db_open_config(0, 1);







|










>
>
>
>
>







236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
  }
  if( ((*pSyncFlags) & SYNC_PULL)!=0
   && find_option("share-links",0,0)!=0
  ){
    *pSyncFlags |= SYNC_SHARE_LINKS;
  }

  /* Option:  --transport-command COMMAND
  **
  ** Causes COMMAND to be run with three arguments in order to talk
  ** to the server.
  **
  **       COMMAND URL PAYLOAD REPLY
  **
  ** URL is the server name.  PAYLOAD is the name of a temporary file
  ** that will contain the xfer-protocol payload to send to the server.
  ** REPLY is a temporary filename in which COMMAND should write the
  ** content of the reply from the server.
  **
  ** CMD is reponsible for HTTP redirects.  The following Fossil command
  ** can be used for CMD to achieve a working sync:
  **
  **      fossil test-httpmsg --xfer
  */
  g.zHttpCmd = find_option("transport-command",0,1);

  url_proxy_options();
  clone_ssh_find_options();
  if( !uvOnly ) db_find_and_open_repository(0, 0);
  db_open_config(0, 1);
315
316
317
318
319
320
321


322
323
324
325
326
327
328
**   --private                  Pull private branches too
**   --project-code CODE        Use CODE as the project code
**   --proxy PROXY              Use the specified HTTP proxy
**   -R|--repository REPO       Local repository to pull into
**   --share-links              Share links to mirror repos
**   --ssl-identity FILE        Local SSL credentials, if requested by remote
**   --ssh-command SSH          Use SSH as the "ssh" command


**   -v|--verbose               Additional (debugging) output
**   --verily                   Exchange extra information with the remote
**                              to ensure no content is overlooked
**
** See also: [[clone]], [[config]], [[push]], [[remote]], [[sync]]
*/
void pull_cmd(void){







>
>







320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
**   --private                  Pull private branches too
**   --project-code CODE        Use CODE as the project code
**   --proxy PROXY              Use the specified HTTP proxy
**   -R|--repository REPO       Local repository to pull into
**   --share-links              Share links to mirror repos
**   --ssl-identity FILE        Local SSL credentials, if requested by remote
**   --ssh-command SSH          Use SSH as the "ssh" command
**   --transport-command CMD    Use external command CMD to move messages
**                              between client and server
**   -v|--verbose               Additional (debugging) output
**   --verily                   Exchange extra information with the remote
**                              to ensure no content is overlooked
**
** See also: [[clone]], [[config]], [[push]], [[remote]], [[sync]]
*/
void pull_cmd(void){
366
367
368
369
370
371
372


373
374
375
376
377
378
379
**   --no-http-compression      Do not compress HTTP traffic
**   --once                     Do not remember URL for subsequent syncs
**   --proxy PROXY              Use the specified HTTP proxy
**   --private                  Push private branches too
**   -R|--repository REPO       Local repository to push from
**   --ssl-identity FILE        Local SSL credentials, if requested by remote
**   --ssh-command SSH          Use SSH as the "ssh" command


**   -v|--verbose               Additional (debugging) output
**   --verily                   Exchange extra information with the remote
**                              to ensure no content is overlooked
**
** See also: [[clone]], [[config]], [[pull]], [[remote]], [[sync]]
*/
void push_cmd(void){







>
>







373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
**   --no-http-compression      Do not compress HTTP traffic
**   --once                     Do not remember URL for subsequent syncs
**   --proxy PROXY              Use the specified HTTP proxy
**   --private                  Push private branches too
**   -R|--repository REPO       Local repository to push from
**   --ssl-identity FILE        Local SSL credentials, if requested by remote
**   --ssh-command SSH          Use SSH as the "ssh" command
**   --transport-command CMD    Use external command CMD to communicate with
**                              the server
**   -v|--verbose               Additional (debugging) output
**   --verily                   Exchange extra information with the remote
**                              to ensure no content is overlooked
**
** See also: [[clone]], [[config]], [[pull]], [[remote]], [[sync]]
*/
void push_cmd(void){
414
415
416
417
418
419
420


421
422
423
424
425
426
427
**   --once                     Do not remember URL for subsequent syncs
**   --proxy PROXY              Use the specified HTTP proxy
**   --private                  Sync private branches too
**   -R|--repository REPO       Local repository to sync with
**   --share-links              Share links to mirror repos
**   --ssl-identity FILE        Local SSL credentials, if requested by remote
**   --ssh-command SSH          Use SSH as the "ssh" command


**   -u|--unversioned           Also sync unversioned content
**   -v|--verbose               Additional (debugging) output
**   --verily                   Exchange extra information with the remote
**                              to ensure no content is overlooked
**
** See also: [[clone]], [[pull]], [[push]], [[remote]]
*/







>
>







423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
**   --once                     Do not remember URL for subsequent syncs
**   --proxy PROXY              Use the specified HTTP proxy
**   --private                  Sync private branches too
**   -R|--repository REPO       Local repository to sync with
**   --share-links              Share links to mirror repos
**   --ssl-identity FILE        Local SSL credentials, if requested by remote
**   --ssh-command SSH          Use SSH as the "ssh" command
**   --transport-command CMD    Use external command CMD to move message
**                              between the client and the server
**   -u|--unversioned           Also sync unversioned content
**   -v|--verbose               Additional (debugging) output
**   --verily                   Exchange extra information with the remote
**                              to ensure no content is overlooked
**
** See also: [[clone]], [[pull]], [[push]], [[remote]]
*/
Changes to www/sync.wiki.
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90























91
92
93
94
95
96
97
to represent the listener and initiator of the interaction, respectively.
Nothing in this protocol requires that the server actually be a back-room
processor housed in a datacenter, nor does the client need to be a desktop
or handheld device.  For the purposes of this article "client" simply means
the repository that initiates the conversation and "server" is the repository
that responds.  Nothing more.</p>

<h4>2.0.1 Encrypted Transport</h4>

<p>In the current implementation of Fossil, the server only
understands HTTP requests.  The client can send either
clear-text HTTP requests or encrypted HTTPS requests.  But when
HTTPS requests are sent, they first must be decrypted by a web server
or proxy before being passed to the Fossil server.  This limitation
may be relaxed in a future release.</p>
























<h3>2.1 Server Identification</h3>

<p>The server is identified by a URL argument that accompanies the
push, pull, or sync command on the client.  (As a convenience to
users, the URL can be omitted on the client command and the same URL
from the most recent push, pull, or sync will be reused.  This saves
typing in the common case where the client does multiple syncs to







|








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
to represent the listener and initiator of the interaction, respectively.
Nothing in this protocol requires that the server actually be a back-room
processor housed in a datacenter, nor does the client need to be a desktop
or handheld device.  For the purposes of this article "client" simply means
the repository that initiates the conversation and "server" is the repository
that responds.  Nothing more.</p>

<h4>2.0.1 HTTPS Transport</h4>

<p>In the current implementation of Fossil, the server only
understands HTTP requests.  The client can send either
clear-text HTTP requests or encrypted HTTPS requests.  But when
HTTPS requests are sent, they first must be decrypted by a web server
or proxy before being passed to the Fossil server.  This limitation
may be relaxed in a future release.</p>

<h4>2.0.2 SSH Transport</h4>

<p>When doing a sync using an "ssh:..." URL, the same HTTP transport protocol
is used.  Fossil simply uses [https://en.wikipedia.org/wiki/Secure_Shell|ssh]
to start an instance of the [/help?cmd=test-http|fossil test-http] command
running on the remote machine.  It then sends HTTP requests and gets back HTTP
replies over the SSH connection, rather than sending and receiving over an
internet socket.  To see the specific "ssh" command that the Fossil client
runs in order to set up a connection, add either of the the "--httptrace" or
"--sshtrace" options to the "fossil sync" command line.

<h4>2.0.3 FILE Transport</h4>

<p>When doing a sync using a "file:..." URL, the same HTTP protocol is
still used.  But instead of sending each HTTP request over a socket or
via SSH, the HTTP request is written into a temporary file.  The client
then invokes the [/help?cmd=http|fossil http] command in a subprocess
to process the request and and generate a reply.  The client then reads
the HTTP reply out of a temporary file on disk, and deletes the two
temporary files.  To see the specific "fossil http" command that is run
in order to implement the "file:" transport, add the "--httptrace"
option to the "fossil sync" command.

<h3>2.1 Server Identification</h3>

<p>The server is identified by a URL argument that accompanies the
push, pull, or sync command on the client.  (As a convenience to
users, the URL can be omitted on the client command and the same URL
from the most recent push, pull, or sync will be reused.  This saves
typing in the common case where the client does multiple syncs to
1003
1004
1005
1006
1007
1008
1009




































<li>Clusters are artifacts that contain IDs of other artifacts.
<li>Clusters are created automatically on the server during a pull.
<li>Repositories keep track of all artifacts that are not named in any
cluster and send igot messages for those artifacts.
<li>Repositories keep track of all the phantoms they hold and send
gimme messages for those artifacts.
</ol>











































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
<li>Clusters are artifacts that contain IDs of other artifacts.
<li>Clusters are created automatically on the server during a pull.
<li>Repositories keep track of all artifacts that are not named in any
cluster and send igot messages for those artifacts.
<li>Repositories keep track of all the phantoms they hold and send
gimme messages for those artifacts.
</ol>

<h2>7.0 Troubleshooting And Debugging Hints</h2>

<p>
If you run the [/help?cmd=sync|fossil sync] command
(or [/help?cmd=pull|pull] or [/help?cmd=push|push] or
[/help?cmd=clone|clone]) with the --httptrace option, Fossil
will keep a copy of each HTTP request and reply in files
named:
<ul>
<li> <tt>http-request-</tt><i>N</i><tt>.txt</tt>
<li> <tt>http-reply-</tt><i>N</i><tt>.txt</tt>
</ul>

<p>In the above, <i>N</i> is a integer that increments with each
round-trip.  If you are having trouble on the server side,
you can run the "[/help?cmd=test-http|fossil test-http]" command in a
debugger using one the "http-request-N.txt" files as input and
single step through the processing performed by the server.

<p>The "--transport-command CMD" option on [/help?cmd=sync|fossil sync]
(and similar) causes the external program "CMD" to be used to move
the sync message to the server and retrieve the sync reply.  The
CMD is given three arguments:
<ol>
<li> The URL of the server
<li> The name of a temporary file that contains the output-bound sync
     protocol text, with the HTTP headers
<li> The name of a temporary file into which the CMD should write the
     reply sync protocol text, again without any HTTP headers
</ol>

<p>In a complex debugging situation, you can run the command
"fossil sync --transport-command ./debugging_script" where
"debugging_script" is some script of your own that invokes
the anomolous behavior your are trying to debug.