Ticket #1526: no-extensions.diff
File no-extensions.diff, 42.0 KB (added by warner, at 2011-09-26T02:30:12Z) |
---|
-
src/allmydata/mutable/filenode.py
diff --git a/src/allmydata/mutable/filenode.py b/src/allmydata/mutable/filenode.py index 02f99b6..cafe20f 100644
a b class MutableFileNode: 118 118 self._privkey = None 119 119 self._encprivkey = None 120 120 121 # Starting with MDMF caps, we allowed arbitrary extensions in122 # caps. If we were initialized with a cap that had extensions,123 # we want to remember them so we can tell MutableFileVersions124 # about them.125 extensions = self._uri.get_extension_params()126 if extensions:127 extensions = map(int, extensions)128 suspected_k, suspected_segsize = extensions129 self._downloader_hints['k'] = suspected_k130 self._downloader_hints['segsize'] = suspected_segsize131 132 121 return self 133 122 134 123 def create_with_keys(self, (pubkey, privkey), contents, … … class MutableFileNode: 701 690 702 691 def set_downloader_hints(self, hints): 703 692 self._downloader_hints = hints 704 extensions = [ hints["k"], hints["segsize"] ]705 self._uri.set_extension_params(extensions)706 707 693 708 694 def _did_upload(self, res, size): 709 695 self._most_recent_size = size -
src/allmydata/scripts/debug.py
diff --git a/src/allmydata/scripts/debug.py b/src/allmydata/scripts/debug.py index 16e57b3..b482fb9 100644
a b def dump_MDMF_share(m, length, options): 369 369 if base32.could_be_base32_encoded(piece): 370 370 storage_index = base32.a2b(piece) 371 371 fingerprint = hashutil.ssk_pubkey_fingerprint_hash(pubkey) 372 hints = [str(k), str(segsize)] 373 u = MDMFVerifierURI(storage_index, fingerprint, hints) 372 u = MDMFVerifierURI(storage_index, fingerprint) 374 373 verify_cap = u.to_string() 375 374 print >>out, " verify-cap:", quote_output(verify_cap, quotemarks=False) 376 375 -
src/allmydata/test/common.py
diff --git a/src/allmydata/test/common.py b/src/allmydata/test/common.py index 1e29f0d..42890d8 100644
a b class FakeMutableFileNode: 208 208 data = initial_contents.read(initial_contents.get_size()) 209 209 data = "".join(data) 210 210 self.all_contents[self.storage_index] = data 211 self.my_uri.set_extension_params([self._k, self._segsize])212 211 return defer.succeed(self) 213 212 def _get_initial_contents(self, contents): 214 213 if contents is None: … … class FakeMutableFileNode: 358 357 new_data = new_contents.read(new_contents.get_size()) 359 358 new_data = "".join(new_data) 360 359 self.all_contents[self.storage_index] = new_data 361 self.my_uri.set_extension_params([self._k, self._segsize])362 360 return defer.succeed(None) 363 361 def modify(self, modifier): 364 362 # this does not implement FileTooLargeError, but the real one does … … class FakeMutableFileNode: 368 366 old_contents = self.all_contents[self.storage_index] 369 367 new_data = modifier(old_contents, None, True) 370 368 self.all_contents[self.storage_index] = new_data 371 self.my_uri.set_extension_params([self._k, self._segsize])372 369 return None 373 370 374 371 # As actually implemented, MutableFilenode and MutableFileVersion -
src/allmydata/test/test_cli.py
diff --git a/src/allmydata/test/test_cli.py b/src/allmydata/test/test_cli.py index 5d6b728..fb6f143 100644
a b class Put(GridTestMixin, CLITestMixin, unittest.TestCase): 1229 1229 d = self.do_cli("put", "--mutable", "--mutable-type=mdmf", fn1) 1230 1230 def _got_cap((rc, out, err)): 1231 1231 self.failUnlessEqual(rc, 0) 1232 self.cap = out 1232 self.cap = out.strip() 1233 1233 d.addCallback(_got_cap) 1234 1234 # Now try to write something to the cap using put. 1235 1235 data2 = "data2" * 100000 … … class Put(GridTestMixin, CLITestMixin, unittest.TestCase): 1248 1248 self.failUnlessEqual(rc, 0) 1249 1249 self.failUnlessEqual(out, data2) 1250 1250 d.addCallback(_got_data) 1251 # Now strip the extension information off of the cap and try 1252 # to put something to it. 1253 def _make_bare_cap(ignored): 1254 cap = self.cap.split(":") 1255 cap = ":".join(cap[:len(cap) - 2]) 1256 self.cap = cap 1257 d.addCallback(_make_bare_cap) 1251 # add some extension information to the cap and try to put something 1252 # to it. 1253 def _make_extended_cap(ignored): 1254 self.cap = self.cap + ":Extension-Stuff" 1255 d.addCallback(_make_extended_cap) 1258 1256 data3 = "data3" * 100000 1259 1257 fn3 = os.path.join(self.basedir, "data3") 1260 1258 fileutil.write(fn3, data3) … … class Put(GridTestMixin, CLITestMixin, unittest.TestCase): 1277 1275 d = self.do_cli("put", "--mutable", "--mutable-type=sdmf", fn1) 1278 1276 def _got_cap((rc, out, err)): 1279 1277 self.failUnlessEqual(rc, 0) 1280 self.cap = out 1278 self.cap = out.strip() 1281 1279 d.addCallback(_got_cap) 1282 1280 # Now try to write something to the cap using put. 1283 1281 data2 = "data2" * 100000 -
src/allmydata/test/test_dirnode.py
diff --git a/src/allmydata/test/test_dirnode.py b/src/allmydata/test/test_dirnode.py index 9f9a5ad..145419b 100644
a b class MemAccum: 41 41 setup_py_uri = "URI:CHK:n7r3m6wmomelk4sep3kw5cvduq:os7ijw5c3maek7pg65e5254k2fzjflavtpejjyhshpsxuqzhcwwq:3:20:14861" 42 42 one_uri = "URI:LIT:n5xgk" # LIT for "one" 43 43 mut_write_uri = "URI:SSK:vfvcbdfbszyrsaxchgevhmmlii:euw4iw7bbnkrrwpzuburbhppuxhc3gwxv26f6imekhz7zyw2ojnq" 44 mdmf_write_uri = "URI:MDMF:x533rhbm6kiehzl5kj3s44n5ie:4gif5rhneyd763ouo5qjrgnsoa3bg43xycy4robj2rf3tvmhdl3a :1:131072"44 mdmf_write_uri = "URI:MDMF:x533rhbm6kiehzl5kj3s44n5ie:4gif5rhneyd763ouo5qjrgnsoa3bg43xycy4robj2rf3tvmhdl3a" 45 45 empty_litdir_uri = "URI:DIR2-LIT:" 46 46 tiny_litdir_uri = "URI:DIR2-LIT:gqytunj2onug64tufqzdcosvkjetutcjkq5gw4tvm5vwszdgnz5hgyzufqydulbshj5x2lbm" # contains one child which is itself also LIT 47 47 mut_read_uri = "URI:SSK-RO:jf6wkflosyvntwxqcdo7a54jvm:euw4iw7bbnkrrwpzuburbhppuxhc3gwxv26f6imekhz7zyw2ojnq" 48 mdmf_read_uri = "URI:MDMF-RO:d4cydxselputycfzkw6qgz4zv4:4gif5rhneyd763ouo5qjrgnsoa3bg43xycy4robj2rf3tvmhdl3a :1:131072"48 mdmf_read_uri = "URI:MDMF-RO:d4cydxselputycfzkw6qgz4zv4:4gif5rhneyd763ouo5qjrgnsoa3bg43xycy4robj2rf3tvmhdl3a" 49 49 future_write_uri = "x-tahoe-crazy://I_am_from_the_future." 50 50 future_read_uri = "x-tahoe-crazy-readonly://I_am_from_the_future." 51 51 future_nonascii_write_uri = u"x-tahoe-even-more-crazy://I_am_from_the_future_rw_\u263A".encode('utf-8') -
src/allmydata/test/test_mutable.py
diff --git a/src/allmydata/test/test_mutable.py b/src/allmydata/test/test_mutable.py index f88cb90..d42a9ab 100644
a b class Filenode(unittest.TestCase, testutil.ShouldFailMixin): 375 375 return d 376 376 377 377 378 def test_create_from_mdmf_writecap_with_extensions(self):379 # Test that the nodemaker is capable of creating an MDMF380 # filenode when given a writecap with extension parameters in381 # them.382 d = self.nodemaker.create_mutable_file(version=MDMF_VERSION)383 def _created(n):384 self.failUnless(isinstance(n, MutableFileNode))385 s = n.get_uri()386 # We need to cheat a little and delete the nodemaker's387 # cache, otherwise we'll get the same node instance back.388 self.failUnlessIn(":3:131073", s)389 n2 = self.nodemaker.create_from_cap(s)390 391 self.failUnlessEqual(n2.get_storage_index(), n.get_storage_index())392 self.failUnlessEqual(n.get_writekey(), n2.get_writekey())393 hints = n2._downloader_hints394 self.failUnlessEqual(hints['k'], 3)395 self.failUnlessEqual(hints['segsize'], 131073)396 d.addCallback(_created)397 return d398 399 400 378 def test_create_from_mdmf_readcap(self): 401 379 d = self.nodemaker.create_mutable_file(version=MDMF_VERSION) 402 380 def _created(n): … … class Filenode(unittest.TestCase, testutil.ShouldFailMixin): 411 389 return d 412 390 413 391 414 def test_create_from_mdmf_readcap_with_extensions(self):415 # We should be able to create an MDMF filenode with the416 # extension parameters without it breaking.417 d = self.nodemaker.create_mutable_file(version=MDMF_VERSION)418 def _created(n):419 self.failUnless(isinstance(n, MutableFileNode))420 s = n.get_readonly_uri()421 self.failUnlessIn(":3:131073", s)422 423 n2 = self.nodemaker.create_from_cap(s)424 self.failUnless(isinstance(n2, MutableFileNode))425 self.failUnless(n2.is_readonly())426 self.failUnlessEqual(n.get_storage_index(), n2.get_storage_index())427 hints = n2._downloader_hints428 self.failUnlessEqual(hints["k"], 3)429 self.failUnlessEqual(hints["segsize"], 131073)430 d.addCallback(_created)431 return d432 433 434 392 def test_internal_version_from_cap(self): 435 393 # MutableFileNodes and MutableFileVersions have an internal 436 394 # switch that tells them whether they're dealing with an SDMF or … … class Filenode(unittest.TestCase, testutil.ShouldFailMixin): 606 564 d = self.nodemaker.create_mutable_file(version=MDMF_VERSION) 607 565 def _created(node): 608 566 self.uri = node.get_uri() 567 # also confirm that the cap has no extension fields 568 pieces = self.uri.split(":") 569 self.failUnlessEqual(len(pieces), 4) 609 570 610 571 return node.overwrite(MutableData("contents1" * 100000)) 611 572 def _then(ignored): … … class Filenode(unittest.TestCase, testutil.ShouldFailMixin): 619 580 return d 620 581 621 582 622 def test_create_and_download_from_bare_mdmf_cap(self):623 # MDMF caps have extension parameters on them by default. We624 # need to make sure that they work without extension parameters.625 contents = MutableData("contents" * 100000)626 d = self.nodemaker.create_mutable_file(version=MDMF_VERSION,627 contents=contents)628 def _created(node):629 uri = node.get_uri()630 self._created = node631 self.failUnlessIn(":3:131073", uri)632 # Now strip that off the end of the uri, then try creating633 # and downloading the node again.634 bare_uri = uri.replace(":3:131073", "")635 assert ":3:131073" not in bare_uri636 637 return self.nodemaker.create_from_cap(bare_uri)638 d.addCallback(_created)639 def _created_bare(node):640 self.failUnlessEqual(node.get_writekey(),641 self._created.get_writekey())642 self.failUnlessEqual(node.get_readkey(),643 self._created.get_readkey())644 self.failUnlessEqual(node.get_storage_index(),645 self._created.get_storage_index())646 return node.download_best_version()647 d.addCallback(_created_bare)648 d.addCallback(lambda data:649 self.failUnlessEqual(data, "contents" * 100000))650 return d651 652 653 583 def test_mdmf_write_count(self): 654 584 # Publishing an MDMF file should only cause one write for each 655 585 # share that is to be published. Otherwise, we introduce … … class Version(GridTestMixin, unittest.TestCase, testutil.ShouldFailMixin, \ 3068 2998 return d 3069 2999 3070 3000 3071 def test_version_extension_api(self):3072 # We need to define an API by which an uploader can set the3073 # extension parameters, and by which a downloader can retrieve3074 # extensions.3075 d = self.do_upload_mdmf()3076 d.addCallback(lambda ign: self.mdmf_node.get_best_mutable_version())3077 def _got_version(version):3078 hints = version.get_downloader_hints()3079 # Should be empty at this point.3080 self.failUnlessIn("k", hints)3081 self.failUnlessEqual(hints['k'], 3)3082 self.failUnlessIn('segsize', hints)3083 self.failUnlessEqual(hints['segsize'], 131073)3084 d.addCallback(_got_version)3085 return d3086 3087 3088 def test_extensions_from_cap(self):3089 # If we initialize a mutable file with a cap that has extension3090 # parameters in it and then grab the extension parameters using3091 # our API, we should see that they're set correctly.3092 d = self.do_upload_mdmf()3093 def _then(ign):3094 mdmf_uri = self.mdmf_node.get_uri()3095 new_node = self.nm.create_from_cap(mdmf_uri)3096 return new_node.get_best_mutable_version()3097 d.addCallback(_then)3098 def _got_version(version):3099 hints = version.get_downloader_hints()3100 self.failUnlessIn("k", hints)3101 self.failUnlessEqual(hints["k"], 3)3102 self.failUnlessIn("segsize", hints)3103 self.failUnlessEqual(hints["segsize"], 131073)3104 d.addCallback(_got_version)3105 return d3106 3107 3108 def test_extensions_from_upload(self):3109 # If we create a new mutable file with some contents, we should3110 # get back an MDMF cap with the right hints in place.3111 contents = "foo bar baz" * 1000003112 d = self.nm.create_mutable_file(contents, version=MDMF_VERSION)3113 def _got_mutable_file(n):3114 rw_uri = n.get_uri()3115 expected_k = str(self.c.DEFAULT_ENCODING_PARAMETERS['k'])3116 self.failUnlessIn(expected_k, rw_uri)3117 # XXX: Get this more intelligently.3118 self.failUnlessIn("131073", rw_uri)3119 3120 ro_uri = n.get_readonly_uri()3121 self.failUnlessIn(expected_k, ro_uri)3122 self.failUnlessIn("131073", ro_uri)3123 d.addCallback(_got_mutable_file)3124 return d3125 3126 3127 3001 def test_cap_after_upload(self): 3128 3002 # If we create a new mutable file and upload things to it, and 3129 3003 # it's an MDMF file, we should get an MDMF cap back from that -
src/allmydata/test/test_uri.py
diff --git a/src/allmydata/test/test_uri.py b/src/allmydata/test/test_uri.py index 7662c7f..c6fb5bc 100644
a b 1 1 2 import re2 import os, re 3 3 from twisted.trial import unittest 4 4 from allmydata import uri 5 5 from allmydata.util import hashutil, base32 … … class Mutable(testutil.ReallyEqualMixin, unittest.TestCase): 430 430 u4 = u2.get_verify_cap() 431 431 self.failUnlessReallyEqual(u4, u2) 432 432 433 def test_mdmf_cap_extra_information(self): 434 # MDMF caps can be arbitrarily extended after the fingerprint 435 # and key/storage index fields. 433 def test_mdmf_cap_ignore_extensions(self): 434 # MDMF caps can be arbitrarily extended after the fingerprint and 435 # key/storage index fields. tahoe-1.9 is supposed to ignore any 436 # extensions, and not add any itself. 436 437 u1 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint) 437 self.failUnlessEqual([], u1.get_extension_params())438 439 cap = u1.to_string()440 # Now let's append some fields. Say, 131073 (the segment size)441 # and 3 (the "k" encoding parameter).442 expected_extensions = []443 for e in ('131073', '3'):444 cap += (":%s" % e)445 expected_extensions.append(e)446 447 u2 = uri.WriteableMDMFFileURI.init_from_string(cap)448 self.failUnlessReallyEqual(self.writekey, u2.writekey)449 self.failUnlessReallyEqual(self.fingerprint, u2.fingerprint)450 self.failIf(u2.is_readonly())451 self.failUnless(u2.is_mutable())452 453 c2 = u2.to_string()454 u2n = uri.WriteableMDMFFileURI.init_from_string(c2)455 self.failUnlessReallyEqual(u2, u2n)456 457 # We should get the extra back when we ask for it.458 self.failUnlessEqual(expected_extensions, u2.get_extension_params())459 460 # These should be preserved through cap attenuation, too.461 u3 = u2.get_readonly()462 self.failUnlessReallyEqual(self.readkey, u3.readkey)463 self.failUnlessReallyEqual(self.fingerprint, u3.fingerprint)464 self.failUnless(u3.is_readonly())465 self.failUnless(u3.is_mutable())466 self.failUnlessEqual(expected_extensions, u3.get_extension_params())467 468 c3 = u3.to_string()469 u3n = uri.ReadonlyMDMFFileURI.init_from_string(c3)470 self.failUnlessReallyEqual(u3, u3n)471 472 u4 = u3.get_verify_cap()473 self.failUnlessReallyEqual(self.storage_index, u4.storage_index)474 self.failUnlessReallyEqual(self.fingerprint, u4.fingerprint)475 self.failUnless(u4.is_readonly())476 self.failIf(u4.is_mutable())477 478 c4 = u4.to_string()479 u4n = uri.MDMFVerifierURI.init_from_string(c4)480 self.failUnlessReallyEqual(u4n, u4)481 482 self.failUnlessEqual(expected_extensions, u4.get_extension_params())483 484 485 def test_sdmf_cap_extra_information(self):486 # For interface consistency, we define a method to get487 # extensions for SDMF files as well. This method must always488 # return no extensions, since SDMF files were not created with489 # extensions and cannot be modified to include extensions490 # without breaking older clients.491 u1 = uri.WriteableSSKFileURI(self.writekey, self.fingerprint)492 438 cap = u1.to_string() 493 u2 = uri.WriteableSSKFileURI.init_from_string(cap) 494 self.failUnlessEqual([], u2.get_extension_params()) 495 496 def test_extension_character_range(self): 497 # As written now, we shouldn't put things other than numbers in 498 # the extension fields. 499 writecap = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint).to_string() 500 readcap = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint).to_string() 501 vcap = uri.MDMFVerifierURI(self.storage_index, self.fingerprint).to_string() 502 self.failUnlessRaises(uri.BadURIError, 503 uri.WriteableMDMFFileURI.init_from_string, 504 ("%s:invalid" % writecap)) 505 self.failUnlessRaises(uri.BadURIError, 506 uri.ReadonlyMDMFFileURI.init_from_string, 507 ("%s:invalid" % readcap)) 508 self.failUnlessRaises(uri.BadURIError, 509 uri.MDMFVerifierURI.init_from_string, 510 ("%s:invalid" % vcap)) 439 440 cap2 = cap+":I COME FROM THE FUTURE" 441 u2 = uri.WriteableMDMFFileURI.init_from_string(cap2) 442 self.failUnlessReallyEqual(self.writekey, u2.writekey) 443 self.failUnlessReallyEqual(self.fingerprint, u2.fingerprint) 444 self.failIf(u2.is_readonly()) 445 self.failUnless(u2.is_mutable()) 446 447 cap3 = cap+":"+os.urandom(40) # parse *that*! 448 u3 = uri.WriteableMDMFFileURI.init_from_string(cap3) 449 self.failUnlessReallyEqual(self.writekey, u3.writekey) 450 self.failUnlessReallyEqual(self.fingerprint, u3.fingerprint) 451 self.failIf(u3.is_readonly()) 452 self.failUnless(u3.is_mutable()) 453 454 cap4 = u1.get_readonly().to_string()+":ooh scary future stuff" 455 u4 = uri.from_string_mutable_filenode(cap4) 456 self.failUnlessReallyEqual(self.readkey, u4.readkey) 457 self.failUnlessReallyEqual(self.fingerprint, u4.fingerprint) 458 self.failUnless(u4.is_readonly()) 459 self.failUnless(u4.is_mutable()) 460 461 cap5 = u1.get_verify_cap().to_string()+":spoilers!" 462 u5 = uri.from_string(cap5) 463 self.failUnlessReallyEqual(self.storage_index, u5.storage_index) 464 self.failUnlessReallyEqual(self.fingerprint, u5.fingerprint) 465 self.failUnless(u5.is_readonly()) 466 self.failIf(u5.is_mutable()) 511 467 512 468 513 469 def test_mdmf_valid_human_encoding(self): … … class Mutable(testutil.ReallyEqualMixin, unittest.TestCase): 517 473 # test that a valid cap (with and without the traditional 518 474 # separators) is recognized and accepted by the classes. 519 475 w1 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint) 520 w2 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint,521 ['131073', '3'])522 476 r1 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint) 523 r2 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint,524 ['131073', '3'])525 477 v1 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint) 526 v2 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint,527 ['131073', '3'])528 478 529 # These will yield sixdifferent caps.530 for o in (w1, w2, r1 , r2, v1, v2):479 # These will yield three different caps. 480 for o in (w1, r1, v1): 531 481 url = base + o.to_string() 532 482 o1 = o.__class__.init_from_human_encoding(url) 533 483 self.failUnlessReallyEqual(o1, o) 534 484 535 # Note that our cap will, by default, have : as separators. 485 # Note that our cap will, by default, have : as separators. 536 486 # But it's expected that users from, e.g., the WUI, will 537 487 # have %3A as a separator. We need to make sure that the 538 488 # initialization routine handles that, too. … … class Mutable(testutil.ReallyEqualMixin, unittest.TestCase): 550 500 # test that a valid cap (with and without the traditional 551 501 # separators) is recognized and accepted by the classes. 552 502 w1 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint) 553 w2 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint,554 ['131073', '3'])555 503 r1 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint) 556 r2 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint,557 ['131073', '3'])558 504 v1 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint) 559 v2 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint,560 ['131073', '3'])561 505 562 # These will yield sixdifferent caps.563 for o in (w1, w2, r1 , r2, v1, v2):506 # These will yield three different caps. 507 for o in (w1, r1, v1): 564 508 url = base + o.to_string() 565 509 self.failUnlessRaises(uri.BadURIError, 566 510 o.__class__.init_from_human_encoding, … … class Mutable(testutil.ReallyEqualMixin, unittest.TestCase): 572 516 # test that a valid cap (with and without the traditional 573 517 # separators) is recognized and accepted by the classes. 574 518 w1 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint) 575 w2 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint,576 ['131073', '3'])577 519 r1 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint) 578 r2 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint,579 ['131073', '3'])580 520 v1 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint) 581 v2 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint,582 ['131073', '3'])583 521 584 # These will yield sixdifferent caps.585 for o in (w1, w2, r1 , r2, v1, v2):522 # These will yield three different caps. 523 for o in (w1, r1, v1): 586 524 # not exhaustive, obviously... 587 525 url = base + o.to_string() + "foobarbaz" 588 526 url2 = base + "foobarbaz" + o.to_string() … … class Mutable(testutil.ReallyEqualMixin, unittest.TestCase): 603 541 u3 = uri.from_string_mutable_filenode(cap) 604 542 self.failUnlessEqual(u3, u1) 605 543 606 # XXX: We should refactor the extension field into setUp607 u1 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint,608 ['131073', '3'])609 cap = u1.to_string()610 self.failUnless(uri.is_uri(cap))611 u2 = uri.from_string(cap)612 self.failUnlessReallyEqual(u1, u2)613 u3 = uri.from_string_mutable_filenode(cap)614 self.failUnlessEqual(u3, u1)615 616 544 u1 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint) 617 545 cap = u1.to_string() 618 546 self.failUnless(uri.is_uri(cap)) … … class Mutable(testutil.ReallyEqualMixin, unittest.TestCase): 621 549 u3 = uri.from_string_mutable_filenode(cap) 622 550 self.failUnlessEqual(u3, u1) 623 551 624 u1 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint,625 ['131073', '3'])626 cap = u1.to_string()627 self.failUnless(uri.is_uri(cap))628 u2 = uri.from_string(cap)629 self.failUnlessReallyEqual(u1, u2)630 u3 = uri.from_string_mutable_filenode(cap)631 self.failUnlessEqual(u3, u1)632 633 552 u1 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint) 634 553 cap = u1.to_string() 635 554 self.failUnless(uri.is_uri(cap)) … … class Mutable(testutil.ReallyEqualMixin, unittest.TestCase): 638 557 u3 = uri.from_string_verifier(cap) 639 558 self.failUnlessEqual(u3, u1) 640 559 641 u1 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint,642 ['131073', '3'])643 cap = u1.to_string()644 self.failUnless(uri.is_uri(cap))645 u2 = uri.from_string(cap)646 self.failUnlessReallyEqual(u1, u2)647 u3 = uri.from_string_verifier(cap)648 self.failUnlessEqual(u3, u1)649 650 560 651 561 class Dirnode(testutil.ReallyEqualMixin, unittest.TestCase): 652 562 def test_pack(self): … … class Dirnode(testutil.ReallyEqualMixin, unittest.TestCase): 816 726 d3 = uri.from_string(d2.to_string(), deep_immutable=True) 817 727 self.failUnlessIsInstance(d3, uri.UnknownURI) 818 728 819 def test_mdmf_with_extensions(self):820 writekey = "\x01" * 16821 fingerprint = "\x02" * 32822 uri1 = uri.WriteableMDMFFileURI(writekey, fingerprint)823 d1 = uri.MDMFDirectoryURI(uri1)824 d1_uri = d1.to_string()825 # Add some extensions, verify that the URI is interpreted826 # correctly.827 d1_uri += ":3:131073"828 uri2 = uri.from_string(d1_uri)829 self.failUnlessIsInstance(uri2, uri.MDMFDirectoryURI)830 self.failUnless(IURI.providedBy(uri2))831 self.failUnless(IDirnodeURI.providedBy(uri2))832 self.failUnless(uri1.is_mutable())833 self.failIf(uri1.is_readonly())834 835 d2_uri = uri2.to_string()836 self.failUnlessIn(":3:131073", d2_uri)837 838 # Now attenuate, verify that the extensions persist839 ro_uri = uri2.get_readonly()840 self.failUnlessIsInstance(ro_uri, uri.ReadonlyMDMFDirectoryURI)841 self.failUnless(ro_uri.is_mutable())842 self.failUnless(ro_uri.is_readonly())843 self.failUnless(IURI.providedBy(ro_uri))844 self.failUnless(IDirnodeURI.providedBy(ro_uri))845 ro_uri_str = ro_uri.to_string()846 self.failUnlessIn(":3:131073", ro_uri_str)847 848 729 def test_mdmf_attenuation(self): 849 730 writekey = "\x01" * 16 850 731 fingerprint = "\x02" * 32 -
src/allmydata/test/test_web.py
diff --git a/src/allmydata/test/test_web.py b/src/allmydata/test/test_web.py index 370f09a..adf4f71 100644
a b class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi 892 892 return d 893 893 894 894 def test_GET_FILE_URI_mdmf_extensions(self): 895 base = "/uri/%s" % urllib.quote("%s:3:131073" % self._quux_txt_uri) 896 d = self.GET(base) 897 d.addCallback(self.failUnlessIsQuuxDotTxt) 898 return d 899 900 def test_GET_FILE_URI_mdmf_bare_cap(self): 901 cap_elements = self._quux_txt_uri.split(":") 902 # 6 == expected cap length with two extensions. 903 self.failUnlessEqual(len(cap_elements), 6) 904 905 # Now lop off the extension parameters and stitch everything 906 # back together 907 quux_uri = ":".join(cap_elements[:len(cap_elements) - 2]) 908 909 # Now GET that. We should get back quux. 910 base = "/uri/%s" % urllib.quote(quux_uri) 895 base = "/uri/%s" % urllib.quote("%s:RANDOMSTUFF" % self._quux_txt_uri) 911 896 d = self.GET(base) 912 897 d.addCallback(self.failUnlessIsQuuxDotTxt) 913 898 return d … … class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi 949 934 return d 950 935 951 936 def test_PUT_FILE_URI_mdmf_extensions(self): 952 base = "/uri/%s" % urllib.quote("%s: 3:131073" % self._quux_txt_uri)937 base = "/uri/%s" % urllib.quote("%s:EXTENSIONSTUFF" % self._quux_txt_uri) 953 938 self._quux_new_contents = "new_contents" 954 939 d = self.GET(base) 955 940 d.addCallback(lambda res: self.failUnlessIsQuuxDotTxt(res)) … … class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi 959 944 res)) 960 945 return d 961 946 962 def test_PUT_FILE_URI_mdmf_bare_cap(self):963 elements = self._quux_txt_uri.split(":")964 self.failUnlessEqual(len(elements), 6)965 966 quux_uri = ":".join(elements[:len(elements) - 2])967 base = "/uri/%s" % urllib.quote(quux_uri)968 self._quux_new_contents = "new_contents" * 50000969 970 d = self.GET(base)971 d.addCallback(self.failUnlessIsQuuxDotTxt)972 d.addCallback(lambda ignored: self.PUT(base, self._quux_new_contents))973 d.addCallback(lambda ignored: self.GET(base))974 d.addCallback(lambda res:975 self.failUnlessEqual(res, self._quux_new_contents))976 return d977 978 947 def test_PUT_FILE_URI_mdmf_readonly(self): 979 948 # We're not allowed to PUT things to a readonly cap. 980 949 base = "/uri/%s" % self._quux_txt_readonly_uri … … class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi 1043 1012 return d 1044 1013 1045 1014 def test_GET_FILEURL_info_mdmf_extensions(self): 1046 d = self.GET("/uri/%s: 3:131073?t=info" % self._quux_txt_uri)1015 d = self.GET("/uri/%s:STUFF?t=info" % self._quux_txt_uri) 1047 1016 def _got(res): 1048 1017 self.failUnlessIn("mutable file (mdmf)", res) 1049 1018 self.failUnlessIn(self._quux_txt_uri, res) … … class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi 1051 1020 d.addCallback(_got) 1052 1021 return d 1053 1022 1054 def test_GET_FILEURL_info_mdmf_bare_cap(self):1055 elements = self._quux_txt_uri.split(":")1056 self.failUnlessEqual(len(elements), 6)1057 1058 quux_uri = ":".join(elements[:len(elements) - 2])1059 base = "/uri/%s?t=info" % urllib.quote(quux_uri)1060 d = self.GET(base)1061 def _got(res):1062 self.failUnlessIn("mutable file (mdmf)", res)1063 self.failUnlessIn(quux_uri, res)1064 d.addCallback(_got)1065 return d1066 1067 1023 def test_PUT_overwrite_only_files(self): 1068 1024 # create a directory, put a file in that directory. 1069 1025 contents, n, filecap = self.makefile(8) … … class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi 1288 1244 d.addCallback(_got_json, "sdmf") 1289 1245 return d 1290 1246 1291 def test_GET_FILEURL_json_mdmf_extensions(self):1292 # A GET invoked against a URL that includes an MDMF cap with1293 # extensions should fetch the same JSON information as a GET1294 # invoked against a bare cap.1295 self._quux_txt_uri = "%s:3:131073" % self._quux_txt_uri1296 self._quux_txt_readonly_uri = "%s:3:131073" % self._quux_txt_readonly_uri1297 d = self.GET("/uri/%s?t=json" % urllib.quote(self._quux_txt_uri))1298 d.addCallback(self.failUnlessIsQuuxJSON)1299 return d1300 1301 def test_GET_FILEURL_json_mdmf_bare_cap(self):1302 elements = self._quux_txt_uri.split(":")1303 self.failUnlessEqual(len(elements), 6)1304 1305 quux_uri = ":".join(elements[:len(elements) - 2])1306 # so failUnlessIsQuuxJSON will work.1307 self._quux_txt_uri = quux_uri1308 1309 # we need to alter the readonly URI in the same way, again so1310 # failUnlessIsQuuxJSON will work1311 elements = self._quux_txt_readonly_uri.split(":")1312 self.failUnlessEqual(len(elements), 6)1313 quux_ro_uri = ":".join(elements[:len(elements) - 2])1314 self._quux_txt_readonly_uri = quux_ro_uri1315 1316 base = "/uri/%s?t=json" % urllib.quote(quux_uri)1317 d = self.GET(base)1318 d.addCallback(self.failUnlessIsQuuxJSON)1319 return d1320 1321 def test_GET_FILEURL_json_mdmf_bare_readonly_cap(self):1322 elements = self._quux_txt_readonly_uri.split(":")1323 self.failUnlessEqual(len(elements), 6)1324 1325 quux_readonly_uri = ":".join(elements[:len(elements) - 2])1326 # so failUnlessIsQuuxJSON will work1327 self._quux_txt_readonly_uri = quux_readonly_uri1328 base = "/uri/%s?t=json" % quux_readonly_uri1329 d = self.GET(base)1330 # XXX: We may need to make a method that knows how to check for1331 # readonly JSON, or else alter that one so that it knows how to1332 # do that.1333 d.addCallback(self.failUnlessIsQuuxJSON, readonly=True)1334 return d1335 1336 1247 def test_GET_FILEURL_json_mdmf(self): 1337 1248 d = self.GET("/uri/%s?t=json" % urllib.quote(self._quux_txt_uri)) 1338 1249 d.addCallback(self.failUnlessIsQuuxJSON) -
src/allmydata/uri.py
diff --git a/src/allmydata/uri.py b/src/allmydata/uri.py index 62bf9b9..c500c7b 100644
a b BASE32STR_256bits = '(%s{51}%s)' % (base32.BASE32CHAR, base32.BASE32CHAR_1bits) 28 28 SEP='(?::|%3A)' 29 29 NUMBER='([0-9]+)' 30 30 NUMBER_IGNORE='(?:[0-9]+)' 31 OPTIONAL_EXTENSION_FIELD = '(' + SEP + '[0-9' + SEP + ']+|)'32 31 33 32 # "human-encoded" URIs are allowed to come with a leading 34 33 # 'http://127.0.0.1:(8123|3456)/uri/' that will be ignored. … … class WriteableSSKFileURI(_BaseURI): 294 293 def get_verify_cap(self): 295 294 return SSKVerifierURI(self.storage_index, self.fingerprint) 296 295 297 def get_extension_params(self):298 return []299 300 def set_extension_params(self, params):301 pass302 303 296 class ReadonlySSKFileURI(_BaseURI): 304 297 implements(IURI, IMutableFileURI) 305 298 … … class ReadonlySSKFileURI(_BaseURI): 354 347 def get_verify_cap(self): 355 348 return SSKVerifierURI(self.storage_index, self.fingerprint) 356 349 357 def get_extension_params(self):358 return []359 360 def set_extension_params(self, params):361 pass362 363 350 class SSKVerifierURI(_BaseURI): 364 351 implements(IVerifierURI) 365 352 … … class SSKVerifierURI(_BaseURI): 404 391 def get_verify_cap(self): 405 392 return self 406 393 407 def get_extension_params(self):408 return []409 410 def set_extension_params(self, params):411 pass412 413 394 class WriteableMDMFFileURI(_BaseURI): 414 395 implements(IURI, IMutableFileURI) 415 396 416 397 BASE_STRING='URI:MDMF:' 417 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+ OPTIONAL_EXTENSION_FIELD+'$')418 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+ OPTIONAL_EXTENSION_FIELD+'$')398 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'(:|$)') 399 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'(:|$)') 419 400 420 def __init__(self, writekey, fingerprint , params=[]):401 def __init__(self, writekey, fingerprint): 421 402 self.writekey = writekey 422 403 self.readkey = hashutil.ssk_readkey_hash(writekey) 423 404 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey) 424 405 assert len(self.storage_index) == 16 425 406 self.fingerprint = fingerprint 426 self.extension = params427 407 428 408 @classmethod 429 409 def init_from_human_encoding(cls, uri): 430 410 mo = cls.HUMAN_RE.search(uri) 431 411 if not mo: 432 412 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls)) 433 params = filter(lambda x: x != '', re.split(SEP, mo.group(3))) 434 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)), params) 413 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2))) 435 414 436 415 @classmethod 437 416 def init_from_string(cls, uri): 438 417 mo = cls.STRING_RE.search(uri) 439 418 if not mo: 440 419 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls)) 441 params = mo.group(3) 442 params = filter(lambda x: x != '', params.split(":")) 443 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)), params) 420 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2))) 444 421 445 422 def to_string(self): 446 423 assert isinstance(self.writekey, str) 447 424 assert isinstance(self.fingerprint, str) 448 425 ret = 'URI:MDMF:%s:%s' % (base32.b2a(self.writekey), 449 426 base32.b2a(self.fingerprint)) 450 if self.extension:451 ret += ":"452 ret += ":".join(self.extension)453 454 427 return ret 455 428 456 429 def __repr__(self): … … class WriteableMDMFFileURI(_BaseURI): 469 442 return True 470 443 471 444 def get_readonly(self): 472 return ReadonlyMDMFFileURI(self.readkey, self.fingerprint , self.extension)445 return ReadonlyMDMFFileURI(self.readkey, self.fingerprint) 473 446 474 447 def get_verify_cap(self): 475 return MDMFVerifierURI(self.storage_index, self.fingerprint, self.extension) 476 477 def get_extension_params(self): 478 return self.extension 479 480 def set_extension_params(self, params): 481 params = map(str, params) 482 self.extension = params 448 return MDMFVerifierURI(self.storage_index, self.fingerprint) 483 449 484 450 class ReadonlyMDMFFileURI(_BaseURI): 485 451 implements(IURI, IMutableFileURI) 486 452 487 453 BASE_STRING='URI:MDMF-RO:' 488 STRING_RE=re.compile('^' +BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+ OPTIONAL_EXTENSION_FIELD+'$')489 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF-RO'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+ OPTIONAL_EXTENSION_FIELD+'$')454 STRING_RE=re.compile('^' +BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'(:|$)') 455 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF-RO'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'(:|$)') 490 456 491 def __init__(self, readkey, fingerprint , params=[]):457 def __init__(self, readkey, fingerprint): 492 458 self.readkey = readkey 493 459 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey) 494 460 assert len(self.storage_index) == 16 495 461 self.fingerprint = fingerprint 496 self.extension = params497 462 498 463 @classmethod 499 464 def init_from_human_encoding(cls, uri): 500 465 mo = cls.HUMAN_RE.search(uri) 501 466 if not mo: 502 467 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls)) 503 params = mo.group(3) 504 params = filter(lambda x: x!= '', re.split(SEP, params)) 505 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)), params) 468 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2))) 506 469 507 470 @classmethod 508 471 def init_from_string(cls, uri): … … class ReadonlyMDMFFileURI(_BaseURI): 510 473 if not mo: 511 474 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls)) 512 475 513 params = mo.group(3) 514 params = filter(lambda x: x != '', params.split(":")) 515 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)), params) 476 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2))) 516 477 517 478 def to_string(self): 518 479 assert isinstance(self.readkey, str) 519 480 assert isinstance(self.fingerprint, str) 520 481 ret = 'URI:MDMF-RO:%s:%s' % (base32.b2a(self.readkey), 521 482 base32.b2a(self.fingerprint)) 522 if self.extension:523 ret += ":"524 ret += ":".join(self.extension)525 526 483 return ret 527 484 528 485 def __repr__(self): … … class ReadonlyMDMFFileURI(_BaseURI): 544 501 return self 545 502 546 503 def get_verify_cap(self): 547 return MDMFVerifierURI(self.storage_index, self.fingerprint, self.extension) 548 549 def get_extension_params(self): 550 return self.extension 551 552 def set_extension_params(self, params): 553 params = map(str, params) 554 self.extension = params 504 return MDMFVerifierURI(self.storage_index, self.fingerprint) 555 505 556 506 class MDMFVerifierURI(_BaseURI): 557 507 implements(IVerifierURI) 558 508 559 509 BASE_STRING='URI:MDMF-Verifier:' 560 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+ OPTIONAL_EXTENSION_FIELD+'$')561 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF-Verifier'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+ OPTIONAL_EXTENSION_FIELD+'$')510 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'(:|$)') 511 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF-Verifier'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'(:|$)') 562 512 563 def __init__(self, storage_index, fingerprint , params=[]):513 def __init__(self, storage_index, fingerprint): 564 514 assert len(storage_index) == 16 565 515 self.storage_index = storage_index 566 516 self.fingerprint = fingerprint 567 self.extension = params568 517 569 518 @classmethod 570 519 def init_from_human_encoding(cls, uri): 571 520 mo = cls.HUMAN_RE.search(uri) 572 521 if not mo: 573 522 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls)) 574 params = mo.group(3) 575 params = filter(lambda x: x != '', re.split(SEP, params)) 576 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)), params) 523 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2))) 577 524 578 525 @classmethod 579 526 def init_from_string(cls, uri): 580 527 mo = cls.STRING_RE.search(uri) 581 528 if not mo: 582 529 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls)) 583 params = mo.group(3) 584 params = filter(lambda x: x != '', params.split(":")) 585 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)), params) 530 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2))) 586 531 587 532 def to_string(self): 588 533 assert isinstance(self.storage_index, str) 589 534 assert isinstance(self.fingerprint, str) 590 535 ret = 'URI:MDMF-Verifier:%s:%s' % (si_b2a(self.storage_index), 591 536 base32.b2a(self.fingerprint)) 592 if self.extension:593 ret += ':'594 ret += ":".join(self.extension)595 596 537 return ret 597 538 598 539 def is_readonly(self): … … class MDMFVerifierURI(_BaseURI): 607 548 def get_verify_cap(self): 608 549 return self 609 550 610 def get_extension_params(self):611 return self.extension612 613 551 class _DirectoryBaseURI(_BaseURI): 614 552 implements(IURI, IDirnodeURI) 615 553 def __init__(self, filenode_uri=None):