Fossil SCM
The xfer mechanism has been completely reworked to better support delta compression and to require fewer round-trips. The wire protocol is roughly the same but is different enough that you will need to recompile before sync will work.
Commit
edbb332d548cc19d0f884d4c9277cbc81d3feb32
Parent
573a464cb7be4d4…
2 files changed
+8
-7
+94
-67
+8
-7
| --- src/verify.c | ||
| +++ src/verify.c | ||
| @@ -43,19 +43,20 @@ | ||
| 43 | 43 | blob_zero(&uuid); |
| 44 | 44 | db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 45 | 45 | if( blob_size(&uuid)!=UUID_SIZE ){ |
| 46 | 46 | fossil_panic("not a valid rid: %d", rid); |
| 47 | 47 | } |
| 48 | - content_get(rid, &content); | |
| 49 | - sha1sum_blob(&content, &hash); | |
| 50 | - blob_reset(&content); | |
| 51 | - if( blob_compare(&uuid, &hash) ){ | |
| 52 | - fossil_fatal("hash of rid %d (%b) does not match its uuid (%b)", | |
| 53 | - rid, &hash, &uuid); | |
| 48 | + if( content_get(rid, &content) ){ | |
| 49 | + sha1sum_blob(&content, &hash); | |
| 50 | + blob_reset(&content); | |
| 51 | + if( blob_compare(&uuid, &hash) ){ | |
| 52 | + fossil_fatal("hash of rid %d (%b) does not match its uuid (%b)", | |
| 53 | + rid, &hash, &uuid); | |
| 54 | + } | |
| 55 | + blob_reset(&hash); | |
| 54 | 56 | } |
| 55 | 57 | blob_reset(&uuid); |
| 56 | - blob_reset(&hash); | |
| 57 | 58 | } |
| 58 | 59 | |
| 59 | 60 | /* |
| 60 | 61 | ** |
| 61 | 62 | */ |
| 62 | 63 |
| --- src/verify.c | |
| +++ src/verify.c | |
| @@ -43,19 +43,20 @@ | |
| 43 | blob_zero(&uuid); |
| 44 | db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 45 | if( blob_size(&uuid)!=UUID_SIZE ){ |
| 46 | fossil_panic("not a valid rid: %d", rid); |
| 47 | } |
| 48 | content_get(rid, &content); |
| 49 | sha1sum_blob(&content, &hash); |
| 50 | blob_reset(&content); |
| 51 | if( blob_compare(&uuid, &hash) ){ |
| 52 | fossil_fatal("hash of rid %d (%b) does not match its uuid (%b)", |
| 53 | rid, &hash, &uuid); |
| 54 | } |
| 55 | blob_reset(&uuid); |
| 56 | blob_reset(&hash); |
| 57 | } |
| 58 | |
| 59 | /* |
| 60 | ** |
| 61 | */ |
| 62 |
| --- src/verify.c | |
| +++ src/verify.c | |
| @@ -43,19 +43,20 @@ | |
| 43 | blob_zero(&uuid); |
| 44 | db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 45 | if( blob_size(&uuid)!=UUID_SIZE ){ |
| 46 | fossil_panic("not a valid rid: %d", rid); |
| 47 | } |
| 48 | if( content_get(rid, &content) ){ |
| 49 | sha1sum_blob(&content, &hash); |
| 50 | blob_reset(&content); |
| 51 | if( blob_compare(&uuid, &hash) ){ |
| 52 | fossil_fatal("hash of rid %d (%b) does not match its uuid (%b)", |
| 53 | rid, &hash, &uuid); |
| 54 | } |
| 55 | blob_reset(&hash); |
| 56 | } |
| 57 | blob_reset(&uuid); |
| 58 | } |
| 59 | |
| 60 | /* |
| 61 | ** |
| 62 | */ |
| 63 |
+94
-67
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -36,13 +36,16 @@ | ||
| 36 | 36 | Blob *pOut; /* Compose our reply here */ |
| 37 | 37 | Blob line; /* The current line of input */ |
| 38 | 38 | Blob aToken[5]; /* Tokenized version of line */ |
| 39 | 39 | Blob err; /* Error message text */ |
| 40 | 40 | int nToken; /* Number of tokens in line */ |
| 41 | - int nIGot; /* Number of "igot" messages sent */ | |
| 42 | - int nFile; /* Number of files sent or received */ | |
| 43 | - int nDelta; /* Number of deltas sent or received */ | |
| 41 | + int nIGotSent; /* Number of "igot" messages sent */ | |
| 42 | + int nGimmeSent; /* Number of gimme messages sent */ | |
| 43 | + int nFileSent; /* Number of files sent */ | |
| 44 | + int nDeltaSent; /* Number of deltas sent */ | |
| 45 | + int nFileRcvd; /* Number of files received */ | |
| 46 | + int nDeltaRcvd; /* Number of deltas received */ | |
| 44 | 47 | int nDanglingFile; /* Number of dangling deltas received */ |
| 45 | 48 | int mxSend; /* Stop sending "file" with pOut reaches this size */ |
| 46 | 49 | }; |
| 47 | 50 | |
| 48 | 51 | |
| @@ -100,20 +103,21 @@ | ||
| 100 | 103 | blob_extract(pXfer->pIn, n, &content); |
| 101 | 104 | if( pXfer->nToken==4 ){ |
| 102 | 105 | Blob src; |
| 103 | 106 | int srcid = rid_from_uuid(&pXfer->aToken[2], 1); |
| 104 | 107 | if( content_get(srcid, &src)==0 ){ |
| 105 | - content_put(&content, blob_str(&hash), srcid); | |
| 108 | + content_put(&content, blob_str(&pXfer->aToken[1]), srcid); | |
| 106 | 109 | blob_appendf(pXfer->pOut, "gimme %b\n", &pXfer->aToken[2]); |
| 110 | + pXfer->nGimmeSent++; | |
| 107 | 111 | pXfer->nDanglingFile++; |
| 108 | 112 | return; |
| 109 | 113 | } |
| 110 | - pXfer->nDelta++; | |
| 114 | + pXfer->nDeltaRcvd++; | |
| 111 | 115 | blob_delta_apply(&src, &content, &content); |
| 112 | 116 | blob_reset(&src); |
| 113 | 117 | }else{ |
| 114 | - pXfer->nFile++; | |
| 118 | + pXfer->nFileRcvd++; | |
| 115 | 119 | } |
| 116 | 120 | sha1sum_blob(&content, &hash); |
| 117 | 121 | if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){ |
| 118 | 122 | blob_appendf(&pXfer->err, "content does not match sha1 hash"); |
| 119 | 123 | } |
| @@ -139,51 +143,35 @@ | ||
| 139 | 143 | Blob *pContent, /* The content of the file to send */ |
| 140 | 144 | Blob *pUuid, /* The UUID of the file to send */ |
| 141 | 145 | int srcId /* Send as a delta against this record */ |
| 142 | 146 | ){ |
| 143 | 147 | static const char *azQuery[] = { |
| 144 | - "SELECT srcid FROM delta JOIN pending ON pending.rid=delta.srcid" | |
| 145 | - " WHERE delta.rid=%d" | |
| 146 | - " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=srcid)", | |
| 147 | - | |
| 148 | - "SELECT delta.rid FROM delta JOIN pending ON pending.rid=delta.rid" | |
| 149 | - " WHERE srcid=%d" | |
| 150 | - " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=delta.rid)", | |
| 151 | - | |
| 152 | - "SELECT pid FROM plink JOIN pending ON rid=pid" | |
| 148 | + "SELECT pid FROM plink" | |
| 153 | 149 | " WHERE cid=%d" |
| 154 | 150 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)", |
| 155 | 151 | |
| 156 | - "SELECT cid FROM plink JOIN pending ON rid=cid" | |
| 157 | - " WHERE pid=%d" | |
| 158 | - " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=cid)", | |
| 159 | - | |
| 160 | - "SELECT pid FROM mlink JOIN pending ON rid=pid" | |
| 152 | + "SELECT pid FROM mlink" | |
| 161 | 153 | " WHERE fid=%d" |
| 162 | 154 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)", |
| 163 | - | |
| 164 | - "SELECT fid FROM mlink JOIN pending ON rid=fid" | |
| 165 | - " WHERE pid=%d" | |
| 166 | - " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=fid)", | |
| 167 | 155 | }; |
| 168 | 156 | int i; |
| 169 | 157 | Blob src, delta; |
| 170 | 158 | int size = 0; |
| 171 | 159 | |
| 172 | 160 | for(i=0; srcId==0 && i<count(azQuery); i++){ |
| 173 | 161 | srcId = db_int(0, azQuery[i], rid); |
| 174 | 162 | } |
| 175 | - if( srcId && content_get(srcId, &src) ){ | |
| 163 | + if( srcId>0 && content_get(srcId, &src) ){ | |
| 176 | 164 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcId); |
| 177 | 165 | blob_delta_create(&src, pContent, &delta); |
| 178 | 166 | size = blob_size(&delta); |
| 179 | 167 | if( size>=blob_size(pContent)-50 ){ |
| 180 | 168 | size = 0; |
| 181 | 169 | }else{ |
| 182 | 170 | blob_appendf(pXfer->pOut, "file %b %s %d\n", pUuid, zUuid, size); |
| 183 | 171 | blob_append(pXfer->pOut, blob_buffer(&delta), size); |
| 184 | - blob_appendf(pXfer->pOut, "\n", 1); | |
| 172 | + /* blob_appendf(pXfer->pOut, "\n", 1); */ | |
| 185 | 173 | } |
| 186 | 174 | blob_reset(&delta); |
| 187 | 175 | free(zUuid); |
| 188 | 176 | blob_reset(&src); |
| 189 | 177 | } |
| @@ -190,10 +178,18 @@ | ||
| 190 | 178 | return size; |
| 191 | 179 | } |
| 192 | 180 | |
| 193 | 181 | /* |
| 194 | 182 | ** Send the file identified by rid. |
| 183 | +** | |
| 184 | +** The pUuid can be NULL in which case the correct UUID is computed | |
| 185 | +** from the rid. | |
| 186 | +** | |
| 187 | +** If srcId is positive, then a delta is sent against that srcId. | |
| 188 | +** If srcId is zero, then an attempt is made to find an appropriate | |
| 189 | +** file to delta against. If srcId is negative, the file is sent | |
| 190 | +** without deltaing. | |
| 195 | 191 | */ |
| 196 | 192 | static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int srcId){ |
| 197 | 193 | Blob content, uuid; |
| 198 | 194 | int size = 0; |
| 199 | 195 | |
| @@ -208,11 +204,11 @@ | ||
| 208 | 204 | } |
| 209 | 205 | pUuid = &uuid; |
| 210 | 206 | } |
| 211 | 207 | if( pXfer->mxSend<=blob_size(pXfer->pOut) ){ |
| 212 | 208 | blob_appendf(pXfer->pOut, "igot %b\n", pUuid); |
| 213 | - pXfer->nIGot++; | |
| 209 | + pXfer->nIGotSent++; | |
| 214 | 210 | blob_reset(&uuid); |
| 215 | 211 | return; |
| 216 | 212 | } |
| 217 | 213 | content_get(rid, &content); |
| 218 | 214 | |
| @@ -221,13 +217,13 @@ | ||
| 221 | 217 | } |
| 222 | 218 | if( size==0 ){ |
| 223 | 219 | int size = blob_size(&content); |
| 224 | 220 | blob_appendf(pXfer->pOut, "file %b %d\n", pUuid, size); |
| 225 | 221 | blob_append(pXfer->pOut, blob_buffer(&content), size); |
| 226 | - pXfer->nFile++; | |
| 222 | + pXfer->nFileSent++; | |
| 227 | 223 | }else{ |
| 228 | - pXfer->nDelta++; | |
| 224 | + pXfer->nDeltaSent++; | |
| 229 | 225 | } |
| 230 | 226 | db_multi_exec("INSERT INTO sent VALUES(%d)", rid); |
| 231 | 227 | blob_reset(&uuid); |
| 232 | 228 | } |
| 233 | 229 | |
| @@ -293,10 +289,11 @@ | ||
| 293 | 289 | Stmt q; |
| 294 | 290 | db_prepare(&q, "SELECT uuid FROM phantom JOIN blob USING(rid)"); |
| 295 | 291 | while( db_step(&q)==SQLITE_ROW ){ |
| 296 | 292 | const char *zUuid = db_column_text(&q, 0); |
| 297 | 293 | blob_appendf(pXfer->pOut, "gimme %s\n", zUuid); |
| 294 | + pXfer->nGimmeSent++; | |
| 298 | 295 | } |
| 299 | 296 | db_finalize(&q); |
| 300 | 297 | } |
| 301 | 298 | |
| 302 | 299 | |
| @@ -367,11 +364,10 @@ | ||
| 367 | 364 | ** This is the transfer handler on the server side. The transfer |
| 368 | 365 | ** message has been uncompressed and placed in the g.cgiIn blob. |
| 369 | 366 | ** Process this message and form an appropriate reply. |
| 370 | 367 | */ |
| 371 | 368 | void page_xfer(void){ |
| 372 | - int nToken; | |
| 373 | 369 | int isPull = 0; |
| 374 | 370 | int isPush = 0; |
| 375 | 371 | int nErr = 0; |
| 376 | 372 | Xfer xfer; |
| 377 | 373 | |
| @@ -379,10 +375,11 @@ | ||
| 379 | 375 | blobarray_zero(xfer.aToken, count(xfer.aToken)); |
| 380 | 376 | cgi_set_content_type(g.zContentType); |
| 381 | 377 | blob_zero(&xfer.err); |
| 382 | 378 | xfer.pIn = &g.cgiIn; |
| 383 | 379 | xfer.pOut = cgi_output_blob(); |
| 380 | + xfer.mxSend = db_get_int("max-download", 1000000); | |
| 384 | 381 | |
| 385 | 382 | db_begin_transaction(); |
| 386 | 383 | db_multi_exec( |
| 387 | 384 | "CREATE TEMP TABLE sent(rid INTEGER PRIMARY KEY);" |
| 388 | 385 | ); |
| @@ -393,11 +390,11 @@ | ||
| 393 | 390 | ** file UUID DELTASRC SIZE \n CONTENT |
| 394 | 391 | ** |
| 395 | 392 | ** Accept a file from the client. |
| 396 | 393 | */ |
| 397 | 394 | if( blob_eq(&xfer.aToken[0], "file") ){ |
| 398 | - if( !isPush ){ | |
| 395 | + if( !g.okWrite ){ | |
| 399 | 396 | cgi_reset_content(); |
| 400 | 397 | @ error not\sauthorized\sto\swrite |
| 401 | 398 | nErr++; |
| 402 | 399 | break; |
| 403 | 400 | } |
| @@ -416,11 +413,11 @@ | ||
| 416 | 413 | */ |
| 417 | 414 | if( blob_eq(&xfer.aToken[0], "gimme") |
| 418 | 415 | && xfer.nToken==2 |
| 419 | 416 | && blob_is_uuid(&xfer.aToken[1]) |
| 420 | 417 | ){ |
| 421 | - if( isPull ){ | |
| 418 | + if( g.okRead ){ | |
| 422 | 419 | int rid = rid_from_uuid(&xfer.aToken[1], 0); |
| 423 | 420 | if( rid ){ |
| 424 | 421 | send_file(&xfer, rid, &xfer.aToken[1], 0); |
| 425 | 422 | } |
| 426 | 423 | } |
| @@ -432,11 +429,11 @@ | ||
| 432 | 429 | */ |
| 433 | 430 | if( xfer.nToken==2 |
| 434 | 431 | && blob_eq(&xfer.aToken[0], "igot") |
| 435 | 432 | && blob_is_uuid(&xfer.aToken[1]) |
| 436 | 433 | ){ |
| 437 | - if( isPush ){ | |
| 434 | + if( g.okWrite ){ | |
| 438 | 435 | rid_from_uuid(&xfer.aToken[1], 1); |
| 439 | 436 | } |
| 440 | 437 | }else |
| 441 | 438 | |
| 442 | 439 | |
| @@ -446,11 +443,11 @@ | ||
| 446 | 443 | */ |
| 447 | 444 | if( xfer.nToken==2 |
| 448 | 445 | && blob_eq(&xfer.aToken[0], "leaf") |
| 449 | 446 | && blob_is_uuid(&xfer.aToken[1]) |
| 450 | 447 | ){ |
| 451 | - if( isPull ){ | |
| 448 | + if( g.okRead ){ | |
| 452 | 449 | int rid = rid_from_uuid(&xfer.aToken[1], 0); |
| 453 | 450 | leaf_response(&xfer, rid); |
| 454 | 451 | } |
| 455 | 452 | }else |
| 456 | 453 | |
| @@ -457,11 +454,11 @@ | ||
| 457 | 454 | /* pull SERVERCODE PROJECTCODE |
| 458 | 455 | ** push SERVERCODE PROJECTCODE |
| 459 | 456 | ** |
| 460 | 457 | ** The client wants either send or receive |
| 461 | 458 | */ |
| 462 | - if( nToken==3 | |
| 459 | + if( xfer.nToken==3 | |
| 463 | 460 | && (blob_eq(&xfer.aToken[0], "pull") || blob_eq(&xfer.aToken[0], "push")) |
| 464 | 461 | && blob_is_uuid(&xfer.aToken[1]) |
| 465 | 462 | && blob_is_uuid(&xfer.aToken[2]) |
| 466 | 463 | ){ |
| 467 | 464 | const char *zSCode; |
| @@ -511,28 +508,36 @@ | ||
| 511 | 508 | /* clone |
| 512 | 509 | ** |
| 513 | 510 | ** The client knows nothing. Tell all. |
| 514 | 511 | */ |
| 515 | 512 | if( blob_eq(&xfer.aToken[0], "clone") ){ |
| 513 | + int rootid; | |
| 516 | 514 | login_check_credentials(); |
| 517 | 515 | if( !g.okRead || !g.okHistory ){ |
| 518 | 516 | cgi_reset_content(); |
| 519 | 517 | @ error not\sauthorized\sto\sclone |
| 520 | 518 | nErr++; |
| 521 | 519 | break; |
| 522 | 520 | } |
| 523 | 521 | isPull = 1; |
| 524 | 522 | @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x")) |
| 525 | - send_leaves(&xfer); | |
| 523 | + rootid = db_int(0, | |
| 524 | + "SELECT pid FROM plink AS a" | |
| 525 | + " WHERE NOT EXISTS(SELECT 1 FROM plink WHERE cid=a.pid)" | |
| 526 | + ); | |
| 527 | + if( rootid ){ | |
| 528 | + send_file(&xfer, rootid, 0, -1); | |
| 529 | + leaf_response(&xfer, rootid); | |
| 530 | + } | |
| 526 | 531 | }else |
| 527 | 532 | |
| 528 | 533 | /* login USER NONCE SIGNATURE |
| 529 | 534 | ** |
| 530 | 535 | ** Check for a valid login. This has to happen before anything else. |
| 531 | 536 | */ |
| 532 | 537 | if( blob_eq(&xfer.aToken[0], "login") |
| 533 | - && nToken==4 | |
| 538 | + && xfer.nToken==4 | |
| 534 | 539 | ){ |
| 535 | 540 | if( disableLogin ){ |
| 536 | 541 | g.okRead = g.okWrite = 1; |
| 537 | 542 | }else{ |
| 538 | 543 | check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3]); |
| @@ -597,21 +602,21 @@ | ||
| 597 | 602 | */ |
| 598 | 603 | void client_sync(int pushFlag, int pullFlag, int cloneFlag){ |
| 599 | 604 | int go = 1; /* Loop until zero */ |
| 600 | 605 | const char *zSCode = db_get("server-code", "x"); |
| 601 | 606 | const char *zPCode = db_get("project-code", 0); |
| 602 | - int nMsg = 0; | |
| 603 | - int nReq = 0; | |
| 607 | + int nMsg = 0; /* Number of messages sent or received */ | |
| 608 | + int nCycle = 0; /* Number of round trips to the server */ | |
| 604 | 609 | int nFileSend = 0; |
| 605 | - int nNoFileCycle = 0; | |
| 606 | 610 | Blob send; /* Text we are sending to the server */ |
| 607 | 611 | Blob recv; /* Reply we got back from the server */ |
| 608 | 612 | Xfer xfer; /* Transfer data */ |
| 609 | 613 | |
| 610 | 614 | memset(&xfer, 0, sizeof(xfer)); |
| 611 | 615 | xfer.pIn = &recv; |
| 612 | 616 | xfer.pOut = &send; |
| 617 | + xfer.mxSend = db_get_int("max-upload", 250000); | |
| 613 | 618 | |
| 614 | 619 | assert( pushFlag || pullFlag || cloneFlag ); |
| 615 | 620 | assert( !g.urlIsFile ); /* This only works for networking */ |
| 616 | 621 | |
| 617 | 622 | db_begin_transaction(); |
| @@ -620,15 +625,14 @@ | ||
| 620 | 625 | ); |
| 621 | 626 | blobarray_zero(xfer.aToken, count(xfer.aToken)); |
| 622 | 627 | blob_zero(&send); |
| 623 | 628 | blob_zero(&recv); |
| 624 | 629 | blob_zero(&xfer.err); |
| 630 | + blob_zero(&xfer.line); | |
| 625 | 631 | |
| 626 | 632 | |
| 627 | 633 | while( go ){ |
| 628 | - go = 0; | |
| 629 | - nReq = nMsg = 0; | |
| 630 | 634 | |
| 631 | 635 | /* Generate a request to be sent to the server. |
| 632 | 636 | ** Always begin with a clone, pull, or push message |
| 633 | 637 | */ |
| 634 | 638 | |
| @@ -637,32 +641,36 @@ | ||
| 637 | 641 | pushFlag = 0; |
| 638 | 642 | pullFlag = 0; |
| 639 | 643 | nMsg++; |
| 640 | 644 | }else if( pullFlag ){ |
| 641 | 645 | blob_appendf(&send, "pull %s %s\n", zSCode, zPCode); |
| 642 | - send_leaves(&xfer); | |
| 646 | + nMsg++; | |
| 643 | 647 | request_phantoms(&xfer); |
| 644 | - nMsg++; | |
| 648 | + send_leaves(&xfer); | |
| 645 | 649 | } |
| 646 | 650 | if( pushFlag ){ |
| 647 | 651 | blob_appendf(&send, "push %s %s\n", zSCode, zPCode); |
| 648 | 652 | nMsg++; |
| 649 | 653 | } |
| 650 | 654 | |
| 651 | 655 | /* Exchange messages with the server */ |
| 652 | - nFileSend = xfer.nFile + xfer.nDelta + xfer.nDanglingFile; | |
| 653 | - printf("Send: %3d files, %3d requests, %3d other msgs, %8d bytes\n", | |
| 654 | - nFileSend, nReq, nMsg, blob_size(&send)); | |
| 655 | - xfer.nFile = 0; | |
| 656 | - xfer.nDelta = 0; | |
| 657 | - xfer.nDanglingFile = 0; | |
| 658 | - nReq = nMsg = 0; | |
| 656 | + nFileSend = xfer.nFileSent + xfer.nDeltaSent; | |
| 657 | + printf("Send: %10d bytes, %3d messages, %3d files (%d+%d)\n", | |
| 658 | + blob_size(&send), nMsg+xfer.nGimmeSent+xfer.nIGotSent, | |
| 659 | + nFileSend, xfer.nFileSent, xfer.nDeltaSent); | |
| 660 | + nMsg = 0; | |
| 661 | + xfer.nFileSent = 0; | |
| 662 | + xfer.nDeltaSent = 0; | |
| 663 | + xfer.nGimmeSent = 0; | |
| 659 | 664 | http_exchange(&send, &recv); |
| 660 | 665 | blob_reset(&send); |
| 661 | 666 | |
| 662 | 667 | /* Process the reply that came back from the server */ |
| 663 | 668 | while( blob_line(&recv, &xfer.line) ){ |
| 669 | + if( blob_buffer(&xfer.line)[0]=='#' ){ | |
| 670 | + continue; | |
| 671 | + } | |
| 664 | 672 | xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); |
| 665 | 673 | |
| 666 | 674 | /* file UUID SIZE \n CONTENT |
| 667 | 675 | ** file UUID DELTASRC SIZE \n CONTENT |
| 668 | 676 | ** |
| @@ -678,10 +686,11 @@ | ||
| 678 | 686 | */ |
| 679 | 687 | if( blob_eq(&xfer.aToken[0], "gimme") |
| 680 | 688 | && xfer.nToken==2 |
| 681 | 689 | && blob_is_uuid(&xfer.aToken[1]) |
| 682 | 690 | ){ |
| 691 | + nMsg++; | |
| 683 | 692 | if( pushFlag ){ |
| 684 | 693 | int rid = rid_from_uuid(&xfer.aToken[1], 0); |
| 685 | 694 | send_file(&xfer, rid, &xfer.aToken[1], 0); |
| 686 | 695 | } |
| 687 | 696 | }else |
| @@ -692,12 +701,16 @@ | ||
| 692 | 701 | */ |
| 693 | 702 | if( xfer.nToken==2 |
| 694 | 703 | && blob_eq(&xfer.aToken[0], "igot") |
| 695 | 704 | && blob_is_uuid(&xfer.aToken[1]) |
| 696 | 705 | ){ |
| 706 | + nMsg++; | |
| 697 | 707 | if( pullFlag ){ |
| 698 | - rid_from_uuid(&xfer.aToken[1], 1); | |
| 708 | + if( !db_exists("SELECT 1 FROM blob WHERE uuid='%b' AND size>=0", | |
| 709 | + &xfer.aToken[1]) ){ | |
| 710 | + content_put(0, blob_str(&xfer.aToken[1]), 0); | |
| 711 | + } | |
| 699 | 712 | } |
| 700 | 713 | }else |
| 701 | 714 | |
| 702 | 715 | |
| 703 | 716 | /* leaf UUID |
| @@ -706,10 +719,11 @@ | ||
| 706 | 719 | */ |
| 707 | 720 | if( xfer.nToken==2 |
| 708 | 721 | && blob_eq(&xfer.aToken[0], "leaf") |
| 709 | 722 | && blob_is_uuid(&xfer.aToken[1]) |
| 710 | 723 | ){ |
| 724 | + nMsg++; | |
| 711 | 725 | if( pushFlag ){ |
| 712 | 726 | int rid = rid_from_uuid(&xfer.aToken[1], 0); |
| 713 | 727 | leaf_response(&xfer, rid); |
| 714 | 728 | } |
| 715 | 729 | }else |
| @@ -754,26 +768,39 @@ | ||
| 754 | 768 | |
| 755 | 769 | if( blob_size(&xfer.err) ){ |
| 756 | 770 | fossil_fatal("%b", &xfer.err); |
| 757 | 771 | } |
| 758 | 772 | blobarray_reset(xfer.aToken, xfer.nToken); |
| 773 | + blob_reset(&xfer.line); | |
| 759 | 774 | } |
| 760 | - printf("Received: %3d files, %3d requests, %3d other msgs, %8d bytes\n", | |
| 761 | - xfer.nFile + xfer.nDelta + xfer.nDanglingFile, | |
| 762 | - nReq, nMsg, blob_size(&recv)); | |
| 775 | + printf("Received: %10d bytes, %3d messages, %3d files (%d+%d+%d)\n", | |
| 776 | + blob_size(&recv), nMsg, | |
| 777 | + xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile, | |
| 778 | + xfer.nFileRcvd, xfer.nDeltaRcvd, xfer.nDanglingFile); | |
| 779 | + | |
| 763 | 780 | blob_reset(&recv); |
| 764 | - if( nFileSend + xfer.nFile + xfer.nDelta + xfer.nDanglingFile==0 ){ | |
| 765 | - nNoFileCycle++; | |
| 766 | - if( nNoFileCycle>1 ){ | |
| 767 | - go = 0; | |
| 768 | - } | |
| 769 | - }else{ | |
| 770 | - nNoFileCycle = 0; | |
| 771 | - } | |
| 772 | - nReq = nMsg = 0; | |
| 773 | - xfer.nFile = 0; | |
| 774 | - xfer.nDelta = 0; | |
| 775 | - xfer.nDanglingFile = 0; | |
| 781 | + nMsg = 0; | |
| 782 | + xfer.nFileRcvd = 0; | |
| 783 | + xfer.nDeltaRcvd = 0; | |
| 784 | + xfer.nDanglingFile = 0; | |
| 785 | + nCycle++; | |
| 786 | + go = 0; | |
| 787 | + | |
| 788 | + /* If we have received one or more files on this cycle and | |
| 789 | + ** we have one or more phantoms, then go for another round | |
| 790 | + */ | |
| 791 | + if(xfer.nFileRcvd+xfer.nDeltaRcvd+xfer.nDanglingFile>0 | |
| 792 | + && db_exists("SELECT 1 FROM phantom") | |
| 793 | + ){ | |
| 794 | + go = 1; | |
| 795 | + } | |
| 796 | + | |
| 797 | + /* If we have one or more files queued to send, then go | |
| 798 | + ** another round | |
| 799 | + */ | |
| 800 | + if( xfer.nFileSent+xfer.nDeltaSent>0 ){ | |
| 801 | + go = 1; | |
| 802 | + } | |
| 776 | 803 | }; |
| 777 | 804 | http_close(); |
| 778 | 805 | db_end_transaction(0); |
| 779 | 806 | } |
| 780 | 807 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -36,13 +36,16 @@ | |
| 36 | Blob *pOut; /* Compose our reply here */ |
| 37 | Blob line; /* The current line of input */ |
| 38 | Blob aToken[5]; /* Tokenized version of line */ |
| 39 | Blob err; /* Error message text */ |
| 40 | int nToken; /* Number of tokens in line */ |
| 41 | int nIGot; /* Number of "igot" messages sent */ |
| 42 | int nFile; /* Number of files sent or received */ |
| 43 | int nDelta; /* Number of deltas sent or received */ |
| 44 | int nDanglingFile; /* Number of dangling deltas received */ |
| 45 | int mxSend; /* Stop sending "file" with pOut reaches this size */ |
| 46 | }; |
| 47 | |
| 48 | |
| @@ -100,20 +103,21 @@ | |
| 100 | blob_extract(pXfer->pIn, n, &content); |
| 101 | if( pXfer->nToken==4 ){ |
| 102 | Blob src; |
| 103 | int srcid = rid_from_uuid(&pXfer->aToken[2], 1); |
| 104 | if( content_get(srcid, &src)==0 ){ |
| 105 | content_put(&content, blob_str(&hash), srcid); |
| 106 | blob_appendf(pXfer->pOut, "gimme %b\n", &pXfer->aToken[2]); |
| 107 | pXfer->nDanglingFile++; |
| 108 | return; |
| 109 | } |
| 110 | pXfer->nDelta++; |
| 111 | blob_delta_apply(&src, &content, &content); |
| 112 | blob_reset(&src); |
| 113 | }else{ |
| 114 | pXfer->nFile++; |
| 115 | } |
| 116 | sha1sum_blob(&content, &hash); |
| 117 | if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){ |
| 118 | blob_appendf(&pXfer->err, "content does not match sha1 hash"); |
| 119 | } |
| @@ -139,51 +143,35 @@ | |
| 139 | Blob *pContent, /* The content of the file to send */ |
| 140 | Blob *pUuid, /* The UUID of the file to send */ |
| 141 | int srcId /* Send as a delta against this record */ |
| 142 | ){ |
| 143 | static const char *azQuery[] = { |
| 144 | "SELECT srcid FROM delta JOIN pending ON pending.rid=delta.srcid" |
| 145 | " WHERE delta.rid=%d" |
| 146 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=srcid)", |
| 147 | |
| 148 | "SELECT delta.rid FROM delta JOIN pending ON pending.rid=delta.rid" |
| 149 | " WHERE srcid=%d" |
| 150 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=delta.rid)", |
| 151 | |
| 152 | "SELECT pid FROM plink JOIN pending ON rid=pid" |
| 153 | " WHERE cid=%d" |
| 154 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)", |
| 155 | |
| 156 | "SELECT cid FROM plink JOIN pending ON rid=cid" |
| 157 | " WHERE pid=%d" |
| 158 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=cid)", |
| 159 | |
| 160 | "SELECT pid FROM mlink JOIN pending ON rid=pid" |
| 161 | " WHERE fid=%d" |
| 162 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)", |
| 163 | |
| 164 | "SELECT fid FROM mlink JOIN pending ON rid=fid" |
| 165 | " WHERE pid=%d" |
| 166 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=fid)", |
| 167 | }; |
| 168 | int i; |
| 169 | Blob src, delta; |
| 170 | int size = 0; |
| 171 | |
| 172 | for(i=0; srcId==0 && i<count(azQuery); i++){ |
| 173 | srcId = db_int(0, azQuery[i], rid); |
| 174 | } |
| 175 | if( srcId && content_get(srcId, &src) ){ |
| 176 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcId); |
| 177 | blob_delta_create(&src, pContent, &delta); |
| 178 | size = blob_size(&delta); |
| 179 | if( size>=blob_size(pContent)-50 ){ |
| 180 | size = 0; |
| 181 | }else{ |
| 182 | blob_appendf(pXfer->pOut, "file %b %s %d\n", pUuid, zUuid, size); |
| 183 | blob_append(pXfer->pOut, blob_buffer(&delta), size); |
| 184 | blob_appendf(pXfer->pOut, "\n", 1); |
| 185 | } |
| 186 | blob_reset(&delta); |
| 187 | free(zUuid); |
| 188 | blob_reset(&src); |
| 189 | } |
| @@ -190,10 +178,18 @@ | |
| 190 | return size; |
| 191 | } |
| 192 | |
| 193 | /* |
| 194 | ** Send the file identified by rid. |
| 195 | */ |
| 196 | static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int srcId){ |
| 197 | Blob content, uuid; |
| 198 | int size = 0; |
| 199 | |
| @@ -208,11 +204,11 @@ | |
| 208 | } |
| 209 | pUuid = &uuid; |
| 210 | } |
| 211 | if( pXfer->mxSend<=blob_size(pXfer->pOut) ){ |
| 212 | blob_appendf(pXfer->pOut, "igot %b\n", pUuid); |
| 213 | pXfer->nIGot++; |
| 214 | blob_reset(&uuid); |
| 215 | return; |
| 216 | } |
| 217 | content_get(rid, &content); |
| 218 | |
| @@ -221,13 +217,13 @@ | |
| 221 | } |
| 222 | if( size==0 ){ |
| 223 | int size = blob_size(&content); |
| 224 | blob_appendf(pXfer->pOut, "file %b %d\n", pUuid, size); |
| 225 | blob_append(pXfer->pOut, blob_buffer(&content), size); |
| 226 | pXfer->nFile++; |
| 227 | }else{ |
| 228 | pXfer->nDelta++; |
| 229 | } |
| 230 | db_multi_exec("INSERT INTO sent VALUES(%d)", rid); |
| 231 | blob_reset(&uuid); |
| 232 | } |
| 233 | |
| @@ -293,10 +289,11 @@ | |
| 293 | Stmt q; |
| 294 | db_prepare(&q, "SELECT uuid FROM phantom JOIN blob USING(rid)"); |
| 295 | while( db_step(&q)==SQLITE_ROW ){ |
| 296 | const char *zUuid = db_column_text(&q, 0); |
| 297 | blob_appendf(pXfer->pOut, "gimme %s\n", zUuid); |
| 298 | } |
| 299 | db_finalize(&q); |
| 300 | } |
| 301 | |
| 302 | |
| @@ -367,11 +364,10 @@ | |
| 367 | ** This is the transfer handler on the server side. The transfer |
| 368 | ** message has been uncompressed and placed in the g.cgiIn blob. |
| 369 | ** Process this message and form an appropriate reply. |
| 370 | */ |
| 371 | void page_xfer(void){ |
| 372 | int nToken; |
| 373 | int isPull = 0; |
| 374 | int isPush = 0; |
| 375 | int nErr = 0; |
| 376 | Xfer xfer; |
| 377 | |
| @@ -379,10 +375,11 @@ | |
| 379 | blobarray_zero(xfer.aToken, count(xfer.aToken)); |
| 380 | cgi_set_content_type(g.zContentType); |
| 381 | blob_zero(&xfer.err); |
| 382 | xfer.pIn = &g.cgiIn; |
| 383 | xfer.pOut = cgi_output_blob(); |
| 384 | |
| 385 | db_begin_transaction(); |
| 386 | db_multi_exec( |
| 387 | "CREATE TEMP TABLE sent(rid INTEGER PRIMARY KEY);" |
| 388 | ); |
| @@ -393,11 +390,11 @@ | |
| 393 | ** file UUID DELTASRC SIZE \n CONTENT |
| 394 | ** |
| 395 | ** Accept a file from the client. |
| 396 | */ |
| 397 | if( blob_eq(&xfer.aToken[0], "file") ){ |
| 398 | if( !isPush ){ |
| 399 | cgi_reset_content(); |
| 400 | @ error not\sauthorized\sto\swrite |
| 401 | nErr++; |
| 402 | break; |
| 403 | } |
| @@ -416,11 +413,11 @@ | |
| 416 | */ |
| 417 | if( blob_eq(&xfer.aToken[0], "gimme") |
| 418 | && xfer.nToken==2 |
| 419 | && blob_is_uuid(&xfer.aToken[1]) |
| 420 | ){ |
| 421 | if( isPull ){ |
| 422 | int rid = rid_from_uuid(&xfer.aToken[1], 0); |
| 423 | if( rid ){ |
| 424 | send_file(&xfer, rid, &xfer.aToken[1], 0); |
| 425 | } |
| 426 | } |
| @@ -432,11 +429,11 @@ | |
| 432 | */ |
| 433 | if( xfer.nToken==2 |
| 434 | && blob_eq(&xfer.aToken[0], "igot") |
| 435 | && blob_is_uuid(&xfer.aToken[1]) |
| 436 | ){ |
| 437 | if( isPush ){ |
| 438 | rid_from_uuid(&xfer.aToken[1], 1); |
| 439 | } |
| 440 | }else |
| 441 | |
| 442 | |
| @@ -446,11 +443,11 @@ | |
| 446 | */ |
| 447 | if( xfer.nToken==2 |
| 448 | && blob_eq(&xfer.aToken[0], "leaf") |
| 449 | && blob_is_uuid(&xfer.aToken[1]) |
| 450 | ){ |
| 451 | if( isPull ){ |
| 452 | int rid = rid_from_uuid(&xfer.aToken[1], 0); |
| 453 | leaf_response(&xfer, rid); |
| 454 | } |
| 455 | }else |
| 456 | |
| @@ -457,11 +454,11 @@ | |
| 457 | /* pull SERVERCODE PROJECTCODE |
| 458 | ** push SERVERCODE PROJECTCODE |
| 459 | ** |
| 460 | ** The client wants either send or receive |
| 461 | */ |
| 462 | if( nToken==3 |
| 463 | && (blob_eq(&xfer.aToken[0], "pull") || blob_eq(&xfer.aToken[0], "push")) |
| 464 | && blob_is_uuid(&xfer.aToken[1]) |
| 465 | && blob_is_uuid(&xfer.aToken[2]) |
| 466 | ){ |
| 467 | const char *zSCode; |
| @@ -511,28 +508,36 @@ | |
| 511 | /* clone |
| 512 | ** |
| 513 | ** The client knows nothing. Tell all. |
| 514 | */ |
| 515 | if( blob_eq(&xfer.aToken[0], "clone") ){ |
| 516 | login_check_credentials(); |
| 517 | if( !g.okRead || !g.okHistory ){ |
| 518 | cgi_reset_content(); |
| 519 | @ error not\sauthorized\sto\sclone |
| 520 | nErr++; |
| 521 | break; |
| 522 | } |
| 523 | isPull = 1; |
| 524 | @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x")) |
| 525 | send_leaves(&xfer); |
| 526 | }else |
| 527 | |
| 528 | /* login USER NONCE SIGNATURE |
| 529 | ** |
| 530 | ** Check for a valid login. This has to happen before anything else. |
| 531 | */ |
| 532 | if( blob_eq(&xfer.aToken[0], "login") |
| 533 | && nToken==4 |
| 534 | ){ |
| 535 | if( disableLogin ){ |
| 536 | g.okRead = g.okWrite = 1; |
| 537 | }else{ |
| 538 | check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3]); |
| @@ -597,21 +602,21 @@ | |
| 597 | */ |
| 598 | void client_sync(int pushFlag, int pullFlag, int cloneFlag){ |
| 599 | int go = 1; /* Loop until zero */ |
| 600 | const char *zSCode = db_get("server-code", "x"); |
| 601 | const char *zPCode = db_get("project-code", 0); |
| 602 | int nMsg = 0; |
| 603 | int nReq = 0; |
| 604 | int nFileSend = 0; |
| 605 | int nNoFileCycle = 0; |
| 606 | Blob send; /* Text we are sending to the server */ |
| 607 | Blob recv; /* Reply we got back from the server */ |
| 608 | Xfer xfer; /* Transfer data */ |
| 609 | |
| 610 | memset(&xfer, 0, sizeof(xfer)); |
| 611 | xfer.pIn = &recv; |
| 612 | xfer.pOut = &send; |
| 613 | |
| 614 | assert( pushFlag || pullFlag || cloneFlag ); |
| 615 | assert( !g.urlIsFile ); /* This only works for networking */ |
| 616 | |
| 617 | db_begin_transaction(); |
| @@ -620,15 +625,14 @@ | |
| 620 | ); |
| 621 | blobarray_zero(xfer.aToken, count(xfer.aToken)); |
| 622 | blob_zero(&send); |
| 623 | blob_zero(&recv); |
| 624 | blob_zero(&xfer.err); |
| 625 | |
| 626 | |
| 627 | while( go ){ |
| 628 | go = 0; |
| 629 | nReq = nMsg = 0; |
| 630 | |
| 631 | /* Generate a request to be sent to the server. |
| 632 | ** Always begin with a clone, pull, or push message |
| 633 | */ |
| 634 | |
| @@ -637,32 +641,36 @@ | |
| 637 | pushFlag = 0; |
| 638 | pullFlag = 0; |
| 639 | nMsg++; |
| 640 | }else if( pullFlag ){ |
| 641 | blob_appendf(&send, "pull %s %s\n", zSCode, zPCode); |
| 642 | send_leaves(&xfer); |
| 643 | request_phantoms(&xfer); |
| 644 | nMsg++; |
| 645 | } |
| 646 | if( pushFlag ){ |
| 647 | blob_appendf(&send, "push %s %s\n", zSCode, zPCode); |
| 648 | nMsg++; |
| 649 | } |
| 650 | |
| 651 | /* Exchange messages with the server */ |
| 652 | nFileSend = xfer.nFile + xfer.nDelta + xfer.nDanglingFile; |
| 653 | printf("Send: %3d files, %3d requests, %3d other msgs, %8d bytes\n", |
| 654 | nFileSend, nReq, nMsg, blob_size(&send)); |
| 655 | xfer.nFile = 0; |
| 656 | xfer.nDelta = 0; |
| 657 | xfer.nDanglingFile = 0; |
| 658 | nReq = nMsg = 0; |
| 659 | http_exchange(&send, &recv); |
| 660 | blob_reset(&send); |
| 661 | |
| 662 | /* Process the reply that came back from the server */ |
| 663 | while( blob_line(&recv, &xfer.line) ){ |
| 664 | xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); |
| 665 | |
| 666 | /* file UUID SIZE \n CONTENT |
| 667 | ** file UUID DELTASRC SIZE \n CONTENT |
| 668 | ** |
| @@ -678,10 +686,11 @@ | |
| 678 | */ |
| 679 | if( blob_eq(&xfer.aToken[0], "gimme") |
| 680 | && xfer.nToken==2 |
| 681 | && blob_is_uuid(&xfer.aToken[1]) |
| 682 | ){ |
| 683 | if( pushFlag ){ |
| 684 | int rid = rid_from_uuid(&xfer.aToken[1], 0); |
| 685 | send_file(&xfer, rid, &xfer.aToken[1], 0); |
| 686 | } |
| 687 | }else |
| @@ -692,12 +701,16 @@ | |
| 692 | */ |
| 693 | if( xfer.nToken==2 |
| 694 | && blob_eq(&xfer.aToken[0], "igot") |
| 695 | && blob_is_uuid(&xfer.aToken[1]) |
| 696 | ){ |
| 697 | if( pullFlag ){ |
| 698 | rid_from_uuid(&xfer.aToken[1], 1); |
| 699 | } |
| 700 | }else |
| 701 | |
| 702 | |
| 703 | /* leaf UUID |
| @@ -706,10 +719,11 @@ | |
| 706 | */ |
| 707 | if( xfer.nToken==2 |
| 708 | && blob_eq(&xfer.aToken[0], "leaf") |
| 709 | && blob_is_uuid(&xfer.aToken[1]) |
| 710 | ){ |
| 711 | if( pushFlag ){ |
| 712 | int rid = rid_from_uuid(&xfer.aToken[1], 0); |
| 713 | leaf_response(&xfer, rid); |
| 714 | } |
| 715 | }else |
| @@ -754,26 +768,39 @@ | |
| 754 | |
| 755 | if( blob_size(&xfer.err) ){ |
| 756 | fossil_fatal("%b", &xfer.err); |
| 757 | } |
| 758 | blobarray_reset(xfer.aToken, xfer.nToken); |
| 759 | } |
| 760 | printf("Received: %3d files, %3d requests, %3d other msgs, %8d bytes\n", |
| 761 | xfer.nFile + xfer.nDelta + xfer.nDanglingFile, |
| 762 | nReq, nMsg, blob_size(&recv)); |
| 763 | blob_reset(&recv); |
| 764 | if( nFileSend + xfer.nFile + xfer.nDelta + xfer.nDanglingFile==0 ){ |
| 765 | nNoFileCycle++; |
| 766 | if( nNoFileCycle>1 ){ |
| 767 | go = 0; |
| 768 | } |
| 769 | }else{ |
| 770 | nNoFileCycle = 0; |
| 771 | } |
| 772 | nReq = nMsg = 0; |
| 773 | xfer.nFile = 0; |
| 774 | xfer.nDelta = 0; |
| 775 | xfer.nDanglingFile = 0; |
| 776 | }; |
| 777 | http_close(); |
| 778 | db_end_transaction(0); |
| 779 | } |
| 780 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -36,13 +36,16 @@ | |
| 36 | Blob *pOut; /* Compose our reply here */ |
| 37 | Blob line; /* The current line of input */ |
| 38 | Blob aToken[5]; /* Tokenized version of line */ |
| 39 | Blob err; /* Error message text */ |
| 40 | int nToken; /* Number of tokens in line */ |
| 41 | int nIGotSent; /* Number of "igot" messages sent */ |
| 42 | int nGimmeSent; /* Number of gimme messages sent */ |
| 43 | int nFileSent; /* Number of files sent */ |
| 44 | int nDeltaSent; /* Number of deltas sent */ |
| 45 | int nFileRcvd; /* Number of files received */ |
| 46 | int nDeltaRcvd; /* Number of deltas received */ |
| 47 | int nDanglingFile; /* Number of dangling deltas received */ |
| 48 | int mxSend; /* Stop sending "file" with pOut reaches this size */ |
| 49 | }; |
| 50 | |
| 51 | |
| @@ -100,20 +103,21 @@ | |
| 103 | blob_extract(pXfer->pIn, n, &content); |
| 104 | if( pXfer->nToken==4 ){ |
| 105 | Blob src; |
| 106 | int srcid = rid_from_uuid(&pXfer->aToken[2], 1); |
| 107 | if( content_get(srcid, &src)==0 ){ |
| 108 | content_put(&content, blob_str(&pXfer->aToken[1]), srcid); |
| 109 | blob_appendf(pXfer->pOut, "gimme %b\n", &pXfer->aToken[2]); |
| 110 | pXfer->nGimmeSent++; |
| 111 | pXfer->nDanglingFile++; |
| 112 | return; |
| 113 | } |
| 114 | pXfer->nDeltaRcvd++; |
| 115 | blob_delta_apply(&src, &content, &content); |
| 116 | blob_reset(&src); |
| 117 | }else{ |
| 118 | pXfer->nFileRcvd++; |
| 119 | } |
| 120 | sha1sum_blob(&content, &hash); |
| 121 | if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){ |
| 122 | blob_appendf(&pXfer->err, "content does not match sha1 hash"); |
| 123 | } |
| @@ -139,51 +143,35 @@ | |
| 143 | Blob *pContent, /* The content of the file to send */ |
| 144 | Blob *pUuid, /* The UUID of the file to send */ |
| 145 | int srcId /* Send as a delta against this record */ |
| 146 | ){ |
| 147 | static const char *azQuery[] = { |
| 148 | "SELECT pid FROM plink" |
| 149 | " WHERE cid=%d" |
| 150 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)", |
| 151 | |
| 152 | "SELECT pid FROM mlink" |
| 153 | " WHERE fid=%d" |
| 154 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)", |
| 155 | }; |
| 156 | int i; |
| 157 | Blob src, delta; |
| 158 | int size = 0; |
| 159 | |
| 160 | for(i=0; srcId==0 && i<count(azQuery); i++){ |
| 161 | srcId = db_int(0, azQuery[i], rid); |
| 162 | } |
| 163 | if( srcId>0 && content_get(srcId, &src) ){ |
| 164 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcId); |
| 165 | blob_delta_create(&src, pContent, &delta); |
| 166 | size = blob_size(&delta); |
| 167 | if( size>=blob_size(pContent)-50 ){ |
| 168 | size = 0; |
| 169 | }else{ |
| 170 | blob_appendf(pXfer->pOut, "file %b %s %d\n", pUuid, zUuid, size); |
| 171 | blob_append(pXfer->pOut, blob_buffer(&delta), size); |
| 172 | /* blob_appendf(pXfer->pOut, "\n", 1); */ |
| 173 | } |
| 174 | blob_reset(&delta); |
| 175 | free(zUuid); |
| 176 | blob_reset(&src); |
| 177 | } |
| @@ -190,10 +178,18 @@ | |
| 178 | return size; |
| 179 | } |
| 180 | |
| 181 | /* |
| 182 | ** Send the file identified by rid. |
| 183 | ** |
| 184 | ** The pUuid can be NULL in which case the correct UUID is computed |
| 185 | ** from the rid. |
| 186 | ** |
| 187 | ** If srcId is positive, then a delta is sent against that srcId. |
| 188 | ** If srcId is zero, then an attempt is made to find an appropriate |
| 189 | ** file to delta against. If srcId is negative, the file is sent |
| 190 | ** without deltaing. |
| 191 | */ |
| 192 | static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int srcId){ |
| 193 | Blob content, uuid; |
| 194 | int size = 0; |
| 195 | |
| @@ -208,11 +204,11 @@ | |
| 204 | } |
| 205 | pUuid = &uuid; |
| 206 | } |
| 207 | if( pXfer->mxSend<=blob_size(pXfer->pOut) ){ |
| 208 | blob_appendf(pXfer->pOut, "igot %b\n", pUuid); |
| 209 | pXfer->nIGotSent++; |
| 210 | blob_reset(&uuid); |
| 211 | return; |
| 212 | } |
| 213 | content_get(rid, &content); |
| 214 | |
| @@ -221,13 +217,13 @@ | |
| 217 | } |
| 218 | if( size==0 ){ |
| 219 | int size = blob_size(&content); |
| 220 | blob_appendf(pXfer->pOut, "file %b %d\n", pUuid, size); |
| 221 | blob_append(pXfer->pOut, blob_buffer(&content), size); |
| 222 | pXfer->nFileSent++; |
| 223 | }else{ |
| 224 | pXfer->nDeltaSent++; |
| 225 | } |
| 226 | db_multi_exec("INSERT INTO sent VALUES(%d)", rid); |
| 227 | blob_reset(&uuid); |
| 228 | } |
| 229 | |
| @@ -293,10 +289,11 @@ | |
| 289 | Stmt q; |
| 290 | db_prepare(&q, "SELECT uuid FROM phantom JOIN blob USING(rid)"); |
| 291 | while( db_step(&q)==SQLITE_ROW ){ |
| 292 | const char *zUuid = db_column_text(&q, 0); |
| 293 | blob_appendf(pXfer->pOut, "gimme %s\n", zUuid); |
| 294 | pXfer->nGimmeSent++; |
| 295 | } |
| 296 | db_finalize(&q); |
| 297 | } |
| 298 | |
| 299 | |
| @@ -367,11 +364,10 @@ | |
| 364 | ** This is the transfer handler on the server side. The transfer |
| 365 | ** message has been uncompressed and placed in the g.cgiIn blob. |
| 366 | ** Process this message and form an appropriate reply. |
| 367 | */ |
| 368 | void page_xfer(void){ |
| 369 | int isPull = 0; |
| 370 | int isPush = 0; |
| 371 | int nErr = 0; |
| 372 | Xfer xfer; |
| 373 | |
| @@ -379,10 +375,11 @@ | |
| 375 | blobarray_zero(xfer.aToken, count(xfer.aToken)); |
| 376 | cgi_set_content_type(g.zContentType); |
| 377 | blob_zero(&xfer.err); |
| 378 | xfer.pIn = &g.cgiIn; |
| 379 | xfer.pOut = cgi_output_blob(); |
| 380 | xfer.mxSend = db_get_int("max-download", 1000000); |
| 381 | |
| 382 | db_begin_transaction(); |
| 383 | db_multi_exec( |
| 384 | "CREATE TEMP TABLE sent(rid INTEGER PRIMARY KEY);" |
| 385 | ); |
| @@ -393,11 +390,11 @@ | |
| 390 | ** file UUID DELTASRC SIZE \n CONTENT |
| 391 | ** |
| 392 | ** Accept a file from the client. |
| 393 | */ |
| 394 | if( blob_eq(&xfer.aToken[0], "file") ){ |
| 395 | if( !g.okWrite ){ |
| 396 | cgi_reset_content(); |
| 397 | @ error not\sauthorized\sto\swrite |
| 398 | nErr++; |
| 399 | break; |
| 400 | } |
| @@ -416,11 +413,11 @@ | |
| 413 | */ |
| 414 | if( blob_eq(&xfer.aToken[0], "gimme") |
| 415 | && xfer.nToken==2 |
| 416 | && blob_is_uuid(&xfer.aToken[1]) |
| 417 | ){ |
| 418 | if( g.okRead ){ |
| 419 | int rid = rid_from_uuid(&xfer.aToken[1], 0); |
| 420 | if( rid ){ |
| 421 | send_file(&xfer, rid, &xfer.aToken[1], 0); |
| 422 | } |
| 423 | } |
| @@ -432,11 +429,11 @@ | |
| 429 | */ |
| 430 | if( xfer.nToken==2 |
| 431 | && blob_eq(&xfer.aToken[0], "igot") |
| 432 | && blob_is_uuid(&xfer.aToken[1]) |
| 433 | ){ |
| 434 | if( g.okWrite ){ |
| 435 | rid_from_uuid(&xfer.aToken[1], 1); |
| 436 | } |
| 437 | }else |
| 438 | |
| 439 | |
| @@ -446,11 +443,11 @@ | |
| 443 | */ |
| 444 | if( xfer.nToken==2 |
| 445 | && blob_eq(&xfer.aToken[0], "leaf") |
| 446 | && blob_is_uuid(&xfer.aToken[1]) |
| 447 | ){ |
| 448 | if( g.okRead ){ |
| 449 | int rid = rid_from_uuid(&xfer.aToken[1], 0); |
| 450 | leaf_response(&xfer, rid); |
| 451 | } |
| 452 | }else |
| 453 | |
| @@ -457,11 +454,11 @@ | |
| 454 | /* pull SERVERCODE PROJECTCODE |
| 455 | ** push SERVERCODE PROJECTCODE |
| 456 | ** |
| 457 | ** The client wants either send or receive |
| 458 | */ |
| 459 | if( xfer.nToken==3 |
| 460 | && (blob_eq(&xfer.aToken[0], "pull") || blob_eq(&xfer.aToken[0], "push")) |
| 461 | && blob_is_uuid(&xfer.aToken[1]) |
| 462 | && blob_is_uuid(&xfer.aToken[2]) |
| 463 | ){ |
| 464 | const char *zSCode; |
| @@ -511,28 +508,36 @@ | |
| 508 | /* clone |
| 509 | ** |
| 510 | ** The client knows nothing. Tell all. |
| 511 | */ |
| 512 | if( blob_eq(&xfer.aToken[0], "clone") ){ |
| 513 | int rootid; |
| 514 | login_check_credentials(); |
| 515 | if( !g.okRead || !g.okHistory ){ |
| 516 | cgi_reset_content(); |
| 517 | @ error not\sauthorized\sto\sclone |
| 518 | nErr++; |
| 519 | break; |
| 520 | } |
| 521 | isPull = 1; |
| 522 | @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x")) |
| 523 | rootid = db_int(0, |
| 524 | "SELECT pid FROM plink AS a" |
| 525 | " WHERE NOT EXISTS(SELECT 1 FROM plink WHERE cid=a.pid)" |
| 526 | ); |
| 527 | if( rootid ){ |
| 528 | send_file(&xfer, rootid, 0, -1); |
| 529 | leaf_response(&xfer, rootid); |
| 530 | } |
| 531 | }else |
| 532 | |
| 533 | /* login USER NONCE SIGNATURE |
| 534 | ** |
| 535 | ** Check for a valid login. This has to happen before anything else. |
| 536 | */ |
| 537 | if( blob_eq(&xfer.aToken[0], "login") |
| 538 | && xfer.nToken==4 |
| 539 | ){ |
| 540 | if( disableLogin ){ |
| 541 | g.okRead = g.okWrite = 1; |
| 542 | }else{ |
| 543 | check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3]); |
| @@ -597,21 +602,21 @@ | |
| 602 | */ |
| 603 | void client_sync(int pushFlag, int pullFlag, int cloneFlag){ |
| 604 | int go = 1; /* Loop until zero */ |
| 605 | const char *zSCode = db_get("server-code", "x"); |
| 606 | const char *zPCode = db_get("project-code", 0); |
| 607 | int nMsg = 0; /* Number of messages sent or received */ |
| 608 | int nCycle = 0; /* Number of round trips to the server */ |
| 609 | int nFileSend = 0; |
| 610 | Blob send; /* Text we are sending to the server */ |
| 611 | Blob recv; /* Reply we got back from the server */ |
| 612 | Xfer xfer; /* Transfer data */ |
| 613 | |
| 614 | memset(&xfer, 0, sizeof(xfer)); |
| 615 | xfer.pIn = &recv; |
| 616 | xfer.pOut = &send; |
| 617 | xfer.mxSend = db_get_int("max-upload", 250000); |
| 618 | |
| 619 | assert( pushFlag || pullFlag || cloneFlag ); |
| 620 | assert( !g.urlIsFile ); /* This only works for networking */ |
| 621 | |
| 622 | db_begin_transaction(); |
| @@ -620,15 +625,14 @@ | |
| 625 | ); |
| 626 | blobarray_zero(xfer.aToken, count(xfer.aToken)); |
| 627 | blob_zero(&send); |
| 628 | blob_zero(&recv); |
| 629 | blob_zero(&xfer.err); |
| 630 | blob_zero(&xfer.line); |
| 631 | |
| 632 | |
| 633 | while( go ){ |
| 634 | |
| 635 | /* Generate a request to be sent to the server. |
| 636 | ** Always begin with a clone, pull, or push message |
| 637 | */ |
| 638 | |
| @@ -637,32 +641,36 @@ | |
| 641 | pushFlag = 0; |
| 642 | pullFlag = 0; |
| 643 | nMsg++; |
| 644 | }else if( pullFlag ){ |
| 645 | blob_appendf(&send, "pull %s %s\n", zSCode, zPCode); |
| 646 | nMsg++; |
| 647 | request_phantoms(&xfer); |
| 648 | send_leaves(&xfer); |
| 649 | } |
| 650 | if( pushFlag ){ |
| 651 | blob_appendf(&send, "push %s %s\n", zSCode, zPCode); |
| 652 | nMsg++; |
| 653 | } |
| 654 | |
| 655 | /* Exchange messages with the server */ |
| 656 | nFileSend = xfer.nFileSent + xfer.nDeltaSent; |
| 657 | printf("Send: %10d bytes, %3d messages, %3d files (%d+%d)\n", |
| 658 | blob_size(&send), nMsg+xfer.nGimmeSent+xfer.nIGotSent, |
| 659 | nFileSend, xfer.nFileSent, xfer.nDeltaSent); |
| 660 | nMsg = 0; |
| 661 | xfer.nFileSent = 0; |
| 662 | xfer.nDeltaSent = 0; |
| 663 | xfer.nGimmeSent = 0; |
| 664 | http_exchange(&send, &recv); |
| 665 | blob_reset(&send); |
| 666 | |
| 667 | /* Process the reply that came back from the server */ |
| 668 | while( blob_line(&recv, &xfer.line) ){ |
| 669 | if( blob_buffer(&xfer.line)[0]=='#' ){ |
| 670 | continue; |
| 671 | } |
| 672 | xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); |
| 673 | |
| 674 | /* file UUID SIZE \n CONTENT |
| 675 | ** file UUID DELTASRC SIZE \n CONTENT |
| 676 | ** |
| @@ -678,10 +686,11 @@ | |
| 686 | */ |
| 687 | if( blob_eq(&xfer.aToken[0], "gimme") |
| 688 | && xfer.nToken==2 |
| 689 | && blob_is_uuid(&xfer.aToken[1]) |
| 690 | ){ |
| 691 | nMsg++; |
| 692 | if( pushFlag ){ |
| 693 | int rid = rid_from_uuid(&xfer.aToken[1], 0); |
| 694 | send_file(&xfer, rid, &xfer.aToken[1], 0); |
| 695 | } |
| 696 | }else |
| @@ -692,12 +701,16 @@ | |
| 701 | */ |
| 702 | if( xfer.nToken==2 |
| 703 | && blob_eq(&xfer.aToken[0], "igot") |
| 704 | && blob_is_uuid(&xfer.aToken[1]) |
| 705 | ){ |
| 706 | nMsg++; |
| 707 | if( pullFlag ){ |
| 708 | if( !db_exists("SELECT 1 FROM blob WHERE uuid='%b' AND size>=0", |
| 709 | &xfer.aToken[1]) ){ |
| 710 | content_put(0, blob_str(&xfer.aToken[1]), 0); |
| 711 | } |
| 712 | } |
| 713 | }else |
| 714 | |
| 715 | |
| 716 | /* leaf UUID |
| @@ -706,10 +719,11 @@ | |
| 719 | */ |
| 720 | if( xfer.nToken==2 |
| 721 | && blob_eq(&xfer.aToken[0], "leaf") |
| 722 | && blob_is_uuid(&xfer.aToken[1]) |
| 723 | ){ |
| 724 | nMsg++; |
| 725 | if( pushFlag ){ |
| 726 | int rid = rid_from_uuid(&xfer.aToken[1], 0); |
| 727 | leaf_response(&xfer, rid); |
| 728 | } |
| 729 | }else |
| @@ -754,26 +768,39 @@ | |
| 768 | |
| 769 | if( blob_size(&xfer.err) ){ |
| 770 | fossil_fatal("%b", &xfer.err); |
| 771 | } |
| 772 | blobarray_reset(xfer.aToken, xfer.nToken); |
| 773 | blob_reset(&xfer.line); |
| 774 | } |
| 775 | printf("Received: %10d bytes, %3d messages, %3d files (%d+%d+%d)\n", |
| 776 | blob_size(&recv), nMsg, |
| 777 | xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile, |
| 778 | xfer.nFileRcvd, xfer.nDeltaRcvd, xfer.nDanglingFile); |
| 779 | |
| 780 | blob_reset(&recv); |
| 781 | nMsg = 0; |
| 782 | xfer.nFileRcvd = 0; |
| 783 | xfer.nDeltaRcvd = 0; |
| 784 | xfer.nDanglingFile = 0; |
| 785 | nCycle++; |
| 786 | go = 0; |
| 787 | |
| 788 | /* If we have received one or more files on this cycle and |
| 789 | ** we have one or more phantoms, then go for another round |
| 790 | */ |
| 791 | if(xfer.nFileRcvd+xfer.nDeltaRcvd+xfer.nDanglingFile>0 |
| 792 | && db_exists("SELECT 1 FROM phantom") |
| 793 | ){ |
| 794 | go = 1; |
| 795 | } |
| 796 | |
| 797 | /* If we have one or more files queued to send, then go |
| 798 | ** another round |
| 799 | */ |
| 800 | if( xfer.nFileSent+xfer.nDeltaSent>0 ){ |
| 801 | go = 1; |
| 802 | } |
| 803 | }; |
| 804 | http_close(); |
| 805 | db_end_transaction(0); |
| 806 | } |
| 807 |