checkpointing new webapi: not all tests pass yet

This commit is contained in:
Brian Warner 2007-07-06 19:43:55 -07:00
parent 21e12f383d
commit 6570253d6b
2 changed files with 985 additions and 103 deletions

View File

@ -0,0 +1,525 @@
import re, os.path
from zope.interface import implements
from twisted.application import service
from twisted.trial import unittest
from twisted.internet import defer
from twisted.web import client, error
from twisted.python import failure
from allmydata import webish, interfaces, dirnode, uri
from allmydata.encode import NotEnoughPeersError
import itertools
# create a fake uploader/downloader, and a couple of fake dirnodes, then
# create a webserver that works against them
class MyClient(service.MultiService):
nodeid = "fake_nodeid"
def get_versions(self):
return {'allmydata': "fake",
'foolscap': "fake",
'twisted': "fake",
'zfec': "fake",
}
introducer_furl = "None"
def connected_to_introducer(self):
return False
def get_all_peerids(self):
return []
class MyDownloader(service.Service):
implements(interfaces.IDownloader)
name = "downloader"
def __init__(self, files):
self.files = files
def download(self, uri, target):
print "DOWNLOADING", uri
if uri not in self.files:
e = NotEnoughPeersError()
f = failure.Failure(e)
target.fail(f)
return defer.fail(f)
data = self.files[uri]
target.open(len(data))
target.write(data)
target.close()
return defer.maybeDeferred(target.finish)
uri_counter = itertools.count()
class MyUploader(service.Service):
implements(interfaces.IUploader)
name = "uploader"
def __init__(self, files):
self.files = files
def upload(self, uploadable):
f = uploadable.get_filehandle()
data = f.read()
uri = str(uri_counter.next())
self.files[uri] = data
uploadable.close_filehandle(f)
return defer.succeed(uri)
class MyDirectoryNode(dirnode.MutableDirectoryNode):
def __init__(self, nodes, uri=None):
self._nodes = nodes
if uri is None:
uri = str(uri_counter.next())
self._uri = str(uri)
self._nodes[self._uri] = self
self.children = {}
self._mutable = True
def get_immutable_uri(self):
return self.get_uri() + "RO"
def get(self, name):
def _try():
uri = self.children[name]
if uri not in self._nodes:
raise IndexError("this isn't supposed to happen")
return self._nodes[uri]
return defer.maybeDeferred(_try)
def set_uri(self, name, child_uri):
self.children[name] = child_uri
return defer.succeed(None)
def create_empty_directory(self, name):
node = MyDirectoryNode(self._nodes)
self.children[name] = node.get_uri()
return defer.succeed(node)
def list(self):
kids = dict([(name, self._nodes[uri])
for name,uri in self.children.iteritems()])
return defer.succeed(kids)
class MyFileNode(dirnode.FileNode):
pass
class MyVirtualDrive(service.Service):
name = "vdrive"
public_root = None
private_root = None
def have_public_root(self):
return bool(self.public_root)
def have_private_root(self):
return bool(self.private_root)
def get_public_root(self):
return defer.succeed(self.public_root)
def get_private_root(self):
return defer.succeed(self.private_root)
class Web(unittest.TestCase):
def setUp(self):
self.s = MyClient()
self.s.startService()
s = webish.WebishServer("0")
s.setServiceParent(self.s)
port = s.listener._port.getHost().port
self.webish_url = "http://localhost:%d" % port
v = MyVirtualDrive()
v.setServiceParent(self.s)
self.nodes = {} # maps URI to node
self.files = {} # maps file URI to contents
dl = MyDownloader(self.files)
dl.setServiceParent(self.s)
ul = MyUploader(self.files)
ul.setServiceParent(self.s)
v.public_root = MyDirectoryNode(self.nodes)
v.private_root = MyDirectoryNode(self.nodes)
foo = MyDirectoryNode(self.nodes)
self._foo_node = foo
self._foo_uri = foo.get_uri()
self._foo_readonly_uri = foo.get_immutable_uri()
v.public_root.children["foo"] = foo.get_uri()
self.BAR_CONTENTS = "bar.txt contents"
bar_uri = uri.pack_uri("SI"+"0"*30,
"K"+"0"*15,
"EH"+"0"*30,
25, 100, 123)
bar_txt = MyFileNode(bar_uri, self.s)
self._bar_txt_uri = bar_txt.get_uri()
self.nodes[bar_uri] = bar_txt
self.files[bar_txt.get_uri()] = self.BAR_CONTENTS
foo.children["bar.txt"] = bar_txt.get_uri()
foo.children["sub"] = MyDirectoryNode(self.nodes).get_uri()
blocking_uri = uri.pack_uri("SI"+"1"*30,
"K"+"1"*15,
"EH"+"1"*30,
25, 100, 124)
blocking_file = MyFileNode(blocking_uri, self.s)
self.nodes[blocking_uri] = blocking_file
self.files[blocking_uri] = "blocking contents"
foo.children["blockingfile"] = blocking_file.get_uri()
# public/
# public/foo/
# public/foo/bar.txt
# public/foo/sub/
# public/foo/blockingfile
self.NEWFILE_CONTENTS = "newfile contents\n"
def tearDown(self):
return self.s.stopService()
def failUnlessIsBarDotTxt(self, res):
self.failUnlessEqual(res, self.BAR_CONTENTS)
def GET(self, urlpath):
url = self.webish_url + urlpath
return client.getPage(url, method="GET")
def PUT(self, urlpath, data):
url = self.webish_url + urlpath
return client.getPage(url, method="PUT", postdata=data)
def DELETE(self, urlpath):
url = self.webish_url + urlpath
return client.getPage(url, method="DELETE")
def POST(self, urlpath, data):
url = self.webish_url + urlpath
return client.getPage(url, method="POST", postdata=data)
def shouldFail(self, res, expected_failure, which, substring=None):
print "SHOULDFAIL", res
if isinstance(res, failure.Failure):
res.trap(expected_failure)
if substring:
self.failUnless(substring in str(res),
"substring '%s' not in '%s'"
% (substring, str(res)))
else:
self.fail("%s was supposed to raise %s, not get '%s'" %
(which, expected_failure, res))
def should404(self, res, which):
if isinstance(res, failure.Failure):
res.trap(error.Error)
self.failUnlessEqual(res.value.status, "404")
else:
self.fail("%s was supposed to raise %s, not get '%s'" %
(which, expected_failure, res))
def test_create(self): # YES
pass
def test_welcome(self): # YES
d = self.GET("")
return d
def test_GET_FILEURL(self): # YES
d = self.GET("/vdrive/global/foo/bar.txt")
d.addCallback(self.failUnlessIsBarDotTxt)
return d
def test_GET_FILEURL_missing(self): # YES
d = self.GET("/vdrive/global/foo/missing")
def _oops(f):
print f
print dir(f)
print f.value
print dir(f.value)
print f.value.args
print f.value.response
print f.value.status
return f
#d.addBoth(_oops)
d.addBoth(self.should404, "test_GET_FILEURL_missing")
return d
def test_PUT_NEWFILEURL(self): # YES
d = self.PUT("/vdrive/global/foo/new.txt", self.NEWFILE_CONTENTS)
def _check(res):
self.failUnless("new.txt" in self._foo_node.children)
new_uri = self._foo_node.children["new.txt"]
new_contents = self.files[new_uri]
self.failUnlessEqual(new_contents, self.NEWFILE_CONTENTS)
self.failUnlessEqual(res.strip(), new_uri)
d.addCallback(_check)
return d
def test_PUT_NEWFILEURL_mkdirs(self): # YES
d = self.PUT("/vdrive/global/foo/newdir/new.txt", self.NEWFILE_CONTENTS)
def _check(res):
self.failIf("new.txt" in self._foo_node.children)
self.failUnless("newdir" in self._foo_node.children)
newdir_uri = self._foo_node.children["newdir"]
newdir_node = self.nodes[newdir_uri]
self.failUnless("new.txt" in newdir_node.children)
new_uri = newdir_node.children["new.txt"]
new_contents = self.files[new_uri]
self.failUnlessEqual(new_contents, self.NEWFILE_CONTENTS)
self.failUnlessEqual(res.strip(), new_uri)
d.addCallback(_check)
return d
def test_PUT_NEWFILEURL_blocked(self): # YES
d = self.PUT("/vdrive/global/foo/blockingfile/new.txt",
self.NEWFILE_CONTENTS)
d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_blocked",
"403 Forbidden")
return d
def test_DELETE_FILEURL(self):
d = self.DELETE("/vdrive/global/foo/bar.txt")
return d
def test_DELETE_FILEURL_missing(self):
d = self.DELETE("/vdrive/global/foo/missing")
return d
def test_GET_FILEURL_json(self): # YES
# twisted.web.http.parse_qs ignores any query args without an '=', so
# I can't do "GET /path?json", I have to do "GET /path/t=json"
# instead. This may make it tricky to emulate the S3 interface
# completely.
d = self.GET("/vdrive/global/foo/bar.txt?t=json")
def _got(json):
# TODO
self.failUnless("JSON" in json, json)
d.addCallback(_got)
return d
def test_GET_FILEURL_json_missing(self): # YES
d = self.GET("/vdrive/global/foo/missing?json")
d.addBoth(self.should404, "test_GET_FILEURL_json_missing")
return d
def test_GET_FILEURL_localfile(self): # YES
localfile = os.path.abspath("web/GET_FILEURL_localfile")
os.makedirs("web")
d = self.GET("/vdrive/global/foo/bar.txt?localfile=%s" % localfile)
def _done(res):
self.failUnless(os.path.exists(localfile))
data = open(localfile, "rb").read()
self.failUnlessEqual(data, self.BAR_CONTENTS)
d.addCallback(_done)
return d
def test_GET_FILEURL_localfile_nonlocal(self): # YES
# TODO: somehow pretend that we aren't local, and verify that the
# server refuses to write to local files, probably by changing the
# server's idea of what counts as "local".
old_LOCALHOST = webish.LOCALHOST
webish.LOCALHOST = "127.0.0.2"
localfile = os.path.abspath("web/GET_FILEURL_localfile_nonlocal")
os.makedirs("web")
d = self.GET("/vdrive/global/foo/bar.txt?localfile=%s" % localfile)
d.addBoth(self.shouldFail, error.Error, "localfile non-local",
"403 Forbidden")
def _check(res):
self.failIf(os.path.exists(localfile))
d.addCallback(_check)
def _reset(res):
print "RESETTING", res
webish.LOCALHOST = old_LOCALHOST
return res
d.addBoth(_reset)
return d
def test_PUT_NEWFILEURL_localfile(self): # YES
localfile = os.path.abspath("web/PUT_NEWFILEURL_localfile")
os.makedirs("web")
f = open(localfile, "wb")
f.write(self.NEWFILE_CONTENTS)
f.close()
d = self.PUT("/vdrive/global/foo/new.txt?localfile=%s" % localfile, "")
def _check(res):
self.failUnless("new.txt" in self._foo_node.children)
new_uri = self._foo_node.children["new.txt"]
new_contents = self.files[new_uri]
self.failUnlessEqual(new_contents, self.NEWFILE_CONTENTS)
self.failUnlessEqual(res.strip(), new_uri)
d.addCallback(_check)
return d
def test_PUT_NEWFILEURL_localfile_mkdirs(self): # YES
localfile = os.path.abspath("web/PUT_NEWFILEURL_localfile_mkdirs")
os.makedirs("web")
f = open(localfile, "wb")
f.write(self.NEWFILE_CONTENTS)
f.close()
d = self.PUT("/vdrive/global/foo/newdir/new.txt?localfile=%s" %
localfile, "")
def _check(res):
self.failIf("new.txt" in self._foo_node.children)
self.failUnless("newdir" in self._foo_node.children)
newdir_uri = self._foo_node.children["newdir"]
newdir_node = self.nodes[newdir_uri]
self.failUnless("new.txt" in newdir_node.children)
new_uri = newdir_node.children["new.txt"]
new_contents = self.files[new_uri]
self.failUnlessEqual(new_contents, self.NEWFILE_CONTENTS)
self.failUnlessEqual(res.strip(), new_uri)
d.addCallback(_check)
return d
def test_GET_FILEURL_uri(self): # YES
d = self.GET("/vdrive/global/foo/bar.txt?t=uri")
def _check(res):
self.failUnlessEqual(res, self._bar_txt_uri)
d.addCallback(_check)
return d
def test_GET_FILEURL_uri_missing(self): # YES
d = self.GET("/vdrive/global/foo/missing?t=uri")
d.addBoth(self.should404, "test_GET_FILEURL_uri_missing")
return d
def test_GET_DIRURL(self): # YES
d = self.GET("/vdrive/global/foo")
def _check(res):
self.failUnless(re.search(r'<td><a href="bar.txt">bar.txt</a></td>'
'\s+<td>FILE</td>'
'\s+<td>123</td>'
, res))
self.failUnless(re.search(r'<td><a href="sub">sub</a></td>'
'\s+<td>DIR</td>', res))
d.addCallback(_check)
return d
def test_GET_DIRURL_json(self): # YES
d = self.GET("/vdrive/global/foo?t=json")
def _got(json):
# TODO
self.failUnless("JSON" in json, json)
d.addCallback(_got)
return d
def test_GET_DIRURL_uri(self): # YES
d = self.GET("/vdrive/global/foo?t=uri")
def _check(res):
self.failUnlessEqual(res, self._foo_uri)
d.addCallback(_check)
return d
def test_GET_DIRURL_readonly_uri(self): # YES
d = self.GET("/vdrive/global/foo?t=readonly-uri")
def _check(res):
self.failUnlessEqual(res, self._foo_readonly_uri)
d.addCallback(_check)
return d
def test_PUT_NEWDIRURL(self): # YES
d = self.PUT("/vdrive/global/foo/newdir?t=mkdir", "")
def _check(res):
self.failUnless("newdir" in self._foo_node.children)
newdir_uri = self._foo_node.children["newdir"]
newdir_node = self.nodes[newdir_uri]
self.failIf(newdir_node.children)
d.addCallback(_check)
return d
def test_PUT_NEWDIRURL_mkdirs(self): # YES
d = self.PUT("/vdrive/global/foo/subdir/newdir?t=mkdir", "")
def _check(res):
self.failIf("newdir" in self._foo_node.children)
self.failUnless("subdir" in self._foo_node.children)
subdir_node = self.nodes[self._foo_node.children["subdir"]]
self.failUnless("newdir" in subdir_node.children)
newdir_node = self.nodes[subdir_node.children["newdir"]]
self.failIf(newdir_node.children)
d.addCallback(_check)
return d
def test_DELETE_DIRURL(self):
d = self.DELETE("/vdrive/global/foo")
return d
def test_DELETE_DIRURL_missing(self):
d = self.DELETE("/vdrive/global/missing")
return d
def test_GET_DIRURL_localdir(self):
localdir = os.path.abspath("web/GET_DIRURL_localdir")
os.makedirs("web")
d = self.GET("/vdrive/global/foo?localdir=%s" % localdir)
return d
def test_PUT_NEWDIRURL_localdir(self):
localdir = os.path.abspath("web/PUT_NEWDIRURL_localdir")
os.makedirs("web")
# create some files there
d = self.GET("/vdrive/global/foo/newdir?localdir=%s" % localdir)
return d
def test_PUT_NEWDIRURL_localdir_mkdirs(self):
localdir = os.path.abspath("web/PUT_NEWDIRURL_localdir_mkdirs")
os.makedirs("web")
# create some files there
d = self.GET("/vdrive/global/foo/subdir/newdir?localdir=%s" % localdir)
return d
def test_POST_upload(self):
form = "TODO"
d = self.POST("/vdrive/global/foo", form)
return d
def test_POST_mkdir(self):
form = "TODO"
d = self.POST("/vdrive/global/foo", form)
return d
def test_POST_put_uri(self):
form = "TODO"
d = self.POST("/vdrive/global/foo", form)
return d
def test_POST_delete(self):
form = "TODO, bar.txt"
d = self.POST("/vdrive/global/foo", form)
return d
def test_URI_GET(self):
d = self.GET("/uri/%s/bar.txt" % foo_uri)
return d
def test_PUT_NEWFILEURL_uri(self):
d = self.PUT("/vdrive/global/foo/new.txt?uri", new_uri)
return d
def test_XMLRPC(self):
pass
"""
# GET / (welcome)
# GET FILEURL
# PUT NEWFILEURL
# DELETE FILEURL
# GET FILEURL?t=json
# GET FILEURL?localfile=$FILENAME
# PUT NEWFILEURL?localfile=$FILENAME
# GET FILEURL?t=uri
# GET DIRURL
# GET DIRURL?t=json
# GET DIRURL?t=uri
# GET DIRURL?t=readonly-uri
# PUT NEWDIRURL?t=mkdir
# DELETE DIRURL
# GET DIRURL?localdir=$DIRNAME
# PUT NEWDIRURL?localdir=$DIRNAME
# POST DIRURL?t=upload-form
# POST DIRURL?t=mkdir-form
# POST DIRURL?t=put-uri-form
# POST DIRURL?t=delete-form
# GET .../url/$URI
# and a few others
# PUT NEWFILEURL?t=uri
# /xmlrpc
"""

View File

@ -2,13 +2,14 @@
from twisted.application import service, strports
from twisted.web import static, resource, server, html, http
from twisted.python import util, log
from twisted.internet import defer
from nevow import inevow, rend, loaders, appserver, url, tags as T
from nevow.static import File as nevow_File # TODO: merge with static.File?
from allmydata.util import idlib
from allmydata.uri import unpack_uri
from allmydata.interfaces import IDownloadTarget, IDirectoryNode, IFileNode
from allmydata.dirnode import FileNode
from allmydata import upload
from allmydata import upload, download
from zope.interface import implements, Interface
import urllib
from formless import annotate, webform
@ -24,84 +25,6 @@ def get_downloader_service(ctx):
def get_uploader_service(ctx):
return IClient(ctx).getServiceNamed("uploader")
class Welcome(rend.Page):
addSlash = True
docFactory = getxmlfile("welcome.xhtml")
def data_version(self, ctx, data):
v = IClient(ctx).get_versions()
return "tahoe: %s, zfec: %s, foolscap: %s, twisted: %s" % \
(v['allmydata'], v['zfec'], v['foolscap'], v['twisted'])
def data_my_nodeid(self, ctx, data):
return idlib.b2a(IClient(ctx).nodeid)
def data_introducer_furl(self, ctx, data):
return IClient(ctx).introducer_furl
def data_connected_to_introducer(self, ctx, data):
if IClient(ctx).connected_to_introducer():
return "yes"
return "no"
def data_connected_to_vdrive(self, ctx, data):
if IClient(ctx).getServiceNamed("vdrive").have_public_root():
return "yes"
return "no"
def data_num_peers(self, ctx, data):
#client = inevow.ISite(ctx)._client
client = IClient(ctx)
return len(list(client.get_all_peerids()))
def data_peers(self, ctx, data):
d = []
client = IClient(ctx)
for nodeid in sorted(client.get_all_peerids()):
row = (idlib.b2a(nodeid),)
d.append(row)
return d
def render_row(self, ctx, data):
(nodeid_a,) = data
ctx.fillSlots("peerid", nodeid_a)
return ctx.tag
def render_global_vdrive(self, ctx, data):
if IClient(ctx).getServiceNamed("vdrive").have_public_root():
return T.p["To view the global shared filestore, ",
T.a(href="../global_vdrive")["Click Here!"],
]
return T.p["vdrive.furl not specified (or vdrive server not "
"responding), no vdrive available."]
def render_private_vdrive(self, ctx, data):
if IClient(ctx).getServiceNamed("vdrive").have_private_root():
return T.p["To view your personal private non-shared filestore, ",
T.a(href="../private_vdrive")["Click Here!"],
]
return T.p["personal vdrive not available."]
# this is a form where users can download files by URI
def bind_download(self, ctx):
uriarg = annotate.Argument("uri",
annotate.String("URI of file to download: "))
namearg = annotate.Argument("filename",
annotate.String("Filename to download as: "))
ctxarg = annotate.Argument("ctx", annotate.Context())
meth = annotate.Method(arguments=[uriarg, namearg, ctxarg],
label="Download File by URI")
# buttons always use value=data.label
# MethodBindingRenderer uses value=(data.action or data.label)
return annotate.MethodBinding("download", meth, action="Download")
def download(self, uri, filename, ctx):
log.msg("webish downloading URI")
target = url.here.sibling("download_uri").add("uri", uri)
if filename:
target = target.add("filename", filename)
return target
def render_forms(self, ctx, data):
return webform.renderForms()
class Directory(rend.Page):
addSlash = True
@ -112,27 +35,15 @@ class Directory(rend.Page):
self._dirname = dirname
def childFactory(self, ctx, name):
print "Directory.childFactory", name
if name.startswith("freeform"): # ick
return None
if name == "@manifest": # ick, this time it's my fault
return Manifest(self._dirnode, self._dirname)
if self._dirname == "/":
dirname = "/" + name
else:
dirname = self._dirname + "/" + name
d = self._dirnode.get(name)
def _got_child(res):
if IFileNode.providedBy(res):
dl = get_downloader_service(ctx)
return Downloader(dl, name, res)
elif IDirectoryNode.providedBy(res):
return Directory(res, dirname)
else:
raise RuntimeError("what is this %s" % res)
d.addCallback(_got_child)
return d
return rend.NotFound
def render_title(self, ctx, data):
print "DIRECTORY.render_title"
return ctx.tag["Directory '%s':" % self._dirname]
def render_header(self, ctx, data):
@ -380,15 +291,13 @@ class TypedFile(static.File):
self.contentEncodings,
self.defaultType)
class Downloader(resource.Resource):
def __init__(self, downloader, name, filenode):
self._downloader = downloader
class FileDownloader(resource.Resource):
def __init__(self, name, filenode):
self._name = name
IFileNode(filenode)
self._filenode = filenode
def render(self, ctx):
req = inevow.IRequest(ctx)
def render(self, req):
gte = static.getTypeAndEncoding
type, encoding = gte(self._name,
static.File.contentTypes,
@ -400,6 +309,242 @@ class Downloader(resource.Resource):
d.addErrback(lambda why: None)
return server.NOT_DONE_YET
class BlockingFileError(Exception):
"""We cannot auto-create a parent directory, because there is a file in
the way"""
LOCALHOST = "127.0.0.1"
class NeedLocalhostError:
implements(inevow.IResource)
def locateChild(self, ctx, segments):
return rend.NotFound
def renderHTTP(self, ctx):
req = inevow.IRequest(ctx)
req.setResponseCode(http.FORBIDDEN)
req.setHeader("content-type", "text/plain")
return "localfile= or localdir= requires a local connection"
class LocalFileDownloader(resource.Resource):
def __init__(self, filenode, local_filename):
self._local_filename = local_filename
IFileNode(filenode)
self._filenode = filenode
def render(self, req):
print "LOCALFILEDOWNLOADER", self._local_filename
target = download.FileName(self._local_filename)
d = self._filenode.download(target)
def _done(res):
req.write(self._filenode.get_uri())
req.finish()
d.addCallback(_done)
return server.NOT_DONE_YET
class FileJSONMetadata(rend.Page):
def __init__(self, filenode):
self._filenode = filenode
def renderHTTP(self, ctx):
file_uri = self._filenode.get_uri()
pieces = unpack_uri(file_uri)
data = "filenode\n"
data += "JSONny stuff here\n"
data += "uri=%s, size=%s" % (file_uri, pieces['size'])
return data
class FileXMLMetadata(FileJSONMetadata):
def renderHTTP(self, ctx):
file_uri = self._filenode.get_uri()
pieces = unpack_uri(file_uri)
data = "<xmlish>\n"
data += "filenode\n"
data += "stuff here\n"
data += "uri=%s, size=%s" % (file_uri, pieces['size'])
return data
class FileURI(FileJSONMetadata):
def renderHTTP(self, ctx):
file_uri = self._filenode.get_uri()
return file_uri
class LocalDirectoryDownloader(resource.Resource):
def __init__(self, dirnode):
self._dirnode = dirnode
def renderHTTP(self, ctx):
dl = get_downloader_service(ctx)
pass # TODO
class DirectoryJSONMetadata(rend.Page):
def __init__(self, dirnode):
self._dirnode = dirnode
def renderHTTP(self, ctx):
file_uri = self._dirnode.get_uri()
data = "dirnode\n"
data += "JSONny stuff here\n"
d = self._dirnode.list()
def _got(children, data):
for name, childnode in children.iteritems():
data += "name=%s, child_uri=%s" % (name, childnode.get_uri())
return data
d.addCallback(_got, data)
def _done(data):
data += "done\n"
return data
d.addCallback(_done)
return d
class DirectoryXMLMetadata(DirectoryJSONMetadata):
def renderHTTP(self, ctx):
file_uri = self._dirnode.get_uri()
pieces = unpack_uri(file_uri)
data = "<xmlish>\n"
data += "dirnode\n"
data += "stuff here\n"
d = self._dirnode.list()
def _got(children, data):
for name, childnode in children:
data += "name=%s, child_uri=%s" % (name, childnode.get_uri())
return data
d.addCallback(_got)
def _done(data):
data += "</done>\n"
return data
d.addCallback(_done)
return d
class DirectoryURI(DirectoryJSONMetadata):
def renderHTTP(self, ctx):
dir_uri = self._dirnode.get_uri()
return dir_uri
class DirectoryReadonlyURI(DirectoryJSONMetadata):
def renderHTTP(self, ctx):
dir_uri = self._dirnode.get_immutable_uri()
return dir_uri
class POSTHandler(rend.Page):
def __init__(self, node):
self._node = node
# TODO: handler methods
class DELETEHandler(rend.Page):
def __init__(self, node, name):
self._node = node
self._name = name
def renderHTTP(self, ctx):
d = self._node.delete(self._name)
def _done(res):
# what should this return??
return "%s deleted" % self._name
d.addCallback(_done)
return d
class PUTHandler(rend.Page):
def __init__(self, node, path, t, localfile, localdir):
self._node = node
self._path = path
self._t = t
self._localfile = localfile
self._localdir = localdir
def renderHTTP(self, ctx):
req = inevow.IRequest(ctx)
t = self._t
localfile = self._localfile
localdir = self._localdir
self._uploader = get_uploader_service(ctx)
# we must traverse the path, creating new directories as necessary
d = self._get_or_create_directories(self._node, self._path[:-1])
name = self._path[-1]
if localfile:
d.addCallback(self._upload_localfile, localfile, name)
elif localdir:
d.addCallback(self._upload_localdir, localdir)
elif t == "uri":
d.addCallback(self._attach_uri, req.content, name)
elif t == "mkdir":
d.addCallback(self._mkdir, name)
else:
d.addCallback(self._upload_file, req.content, name)
def _check_blocking(f):
f.trap(BlockingFileError)
req.setResponseCode(http.FORBIDDEN)
req.setHeader("content-type", "text/plain")
return str(f)
d.addErrback(_check_blocking)
return d
def _get_or_create_directories(self, node, path):
if not IDirectoryNode.providedBy(node):
raise BlockingFileError
if not path:
return node
d = node.get(path[0])
def _maybe_create(f):
f.trap(KeyError)
print "CREATING", path[0]
return node.create_empty_directory(path[0])
d.addErrback(_maybe_create)
d.addCallback(self._get_or_create_directories, path[1:])
return d
def _mkdir(self, node, name):
d = node.create_empty_directory(name)
def _done(newnode):
return newnode.get_uri()
d.addCallback(_done)
return d
def _upload_file(self, node, contents, name):
uploadable = upload.FileHandle(contents)
d = self._uploader.upload(uploadable)
def _uploaded(uri):
d1 = node.set_uri(name, uri)
d1.addCallback(lambda res: uri)
return d1
d.addCallback(_uploaded)
def _done(uri):
log.msg("webish upload complete")
return uri
d.addCallback(_done)
return d
def _upload_localfile(self, node, localfile, name):
uploadable = upload.FileName(localfile)
d = self._uploader.upload(uploadable)
def _uploaded(uri):
print "SETTING URI", name, uri
d1 = node.set_uri(name, uri)
d1.addCallback(lambda res: uri)
return d1
d.addCallback(_uploaded)
def _done(uri):
log.msg("webish upload complete")
return uri
d.addCallback(_done)
return d
def _attach_uri(self, parentnode, contents, name):
newuri = contents.read().strip()
d = parentnode.set_uri(name, newuri)
def _done(res):
return newuri
d.addCallback(_done)
return d
def _upload_localdir(self, node, localdir):
pass # TODO
class Manifest(rend.Page):
docFactory = getxmlfile("manifest.xhtml")
def __init__(self, dirnode, dirname):
@ -419,10 +564,148 @@ class Manifest(rend.Page):
ctx.fillSlots("refresh_capability", refresh_cap)
return ctx.tag
class VDrive(rend.Page):
def __init__(self, node, name):
self.node = node
self.name = name
def get_child_at_path(self, path):
if path:
return self.node.get_child_at_path(path)
return defer.succeed(self.node)
def locateChild(self, ctx, segments):
req = inevow.IRequest(ctx)
method = req.method
path = segments
# when we're pointing at a directory (like /vdrive/public/my_pix),
# Directory.addSlash causes a redirect to /vdrive/public/my_pix/,
# which appears here as ['my_pix', '']. This is supposed to hit the
# same Directory as ['my_pix'].
if path and path[-1] == '':
path = path[:-1]
print "VDrive.locateChild", method, segments, req.args
t = ""
if "t" in req.args:
t = req.args["t"][0]
localfile = None
if "localfile" in req.args:
localfile = req.args["localfile"][0]
localdir = None
if "localdir" in req.args:
localdir = req.args["localdir"][0]
if (localfile or localdir) and req.getHost().host != LOCALHOST:
return NeedLocalhostError(), ()
# TODO: think about clobbering/revealing config files and node secrets
if method == "GET":
# the node must exist, and our operation will be performed on the
# node itself.
name = path[-1]
d = self.get_child_at_path(path)
def file_or_dir(node):
if IFileNode.providedBy(node):
if localfile:
# write contents to a local file
return LocalFileDownloader(node, localfile), ()
elif t == "":
# send contents as the result
print "FileDownloader"
return FileDownloader(name, node), ()
elif t == "json":
print "Localfilejsonmetadata"
return FileJSONMetadata(node), ()
elif t == "xml":
return FileXMLMetadata(node), ()
elif t == "uri":
return FileURI(node), ()
else:
raise RuntimeError("bad t=%s" % t)
elif IDirectoryNode.providedBy(node):
print "GOT DIR"
if localdir:
# recursive download to a local directory
return LocalDirectoryDownloader(node, localdir), ()
elif t == "":
# send an HTML representation of the directory
print "GOT HTML DIR"
return Directory(node, name), ()
elif t == "json":
return DirectoryJSONMetadata(node), ()
elif t == "xml":
return DirectoryXMLMetadata(node), ()
elif t == "uri":
return DirectoryURI(node), ()
elif t == "readonly-uri":
return DirectoryReadonlyURI(node), ()
else:
raise RuntimeError("bad t=%s" % t)
else:
raise RuntimeError("unknown node type")
d.addCallback(file_or_dir)
elif method == "POST":
# the node must exist, and our operation will be performed on the
# node itself.
d = self.get_child_at_path(path)
d.addCallback(lambda node: POSTHandler(node), ())
elif method == "DELETE":
# the node must exist, and our operation will be performed on its
# parent node.
assert path # you can't delete the root
d = self.get_child_at_path(path[:-1])
d.addCallback(lambda node: DELETEHandler(node, path[-1]), )
elif method in ("PUT",):
# the node may or may not exist, and our operation may involve
# all the ancestors of the node.
return PUTHandler(self.node, path, t, localfile, localdir), ()
else:
return rend.NotFound
def _trap_KeyError(f):
f.trap(KeyError)
return rend.FourOhFour(), ()
d.addErrback(_trap_KeyError)
return d
class Root(rend.Page):
addSlash = True
docFactory = getxmlfile("welcome.xhtml")
def locateChild(self, ctx, segments):
if segments[0] == "download_uri":
client = IClient(ctx)
vdrive = client.getServiceNamed("vdrive")
print "Root.locateChild", segments
if segments[0] == "vdrive":
if len(segments) < 2:
return rend.NotFound
if segments[1] == "global":
d = vdrive.get_public_root()
name = "public vdrive"
elif segments[1] == "private":
d = vdrive.get_private_root()
name = "private vdrive"
else:
return rend.NotFound
d.addCallback(lambda dirnode: VDrive(dirnode, name))
d.addCallback(lambda vd: vd.locateChild(ctx, segments[2:]))
return d
elif segments[0] == "uri":
if len(segments) < 2:
return rend.NotFound
uri = segments[1]
d = vdrive.get_node(uri)
d.addCallback(lambda node: VDrive(node), uri)
d.addCallback(lambda vd: vd.locateChild(ctx, segments[2:]))
return d
elif segments[0] == "xmlrpc":
pass # TODO
elif segments[0] == "download_uri":
req = inevow.IRequest(ctx)
dl = get_downloader_service(ctx)
filename = "unknown_filename"
@ -436,14 +719,14 @@ class Root(rend.Page):
uri = req.args["uri"][0]
else:
return rend.NotFound
child = Downloader(dl, filename, FileNode(uri, IClient(ctx)))
child = FileDownloader(filename, FileNode(uri, IClient(ctx)))
return child, ()
return rend.Page.locateChild(self, ctx, segments)
child_webform_css = webform.defaultCSS
child_tahoe_css = nevow_File(util.sibpath(__file__, "web/tahoe.css"))
child_welcome = Welcome()
#child_welcome = Welcome()
def child_global_vdrive(self, ctx):
client = IClient(ctx)
@ -465,6 +748,80 @@ class Root(rend.Page):
else:
return static.Data("sorry, still initializing", "text/plain")
def data_version(self, ctx, data):
v = IClient(ctx).get_versions()
return "tahoe: %s, zfec: %s, foolscap: %s, twisted: %s" % \
(v['allmydata'], v['zfec'], v['foolscap'], v['twisted'])
def data_my_nodeid(self, ctx, data):
return idlib.b2a(IClient(ctx).nodeid)
def data_introducer_furl(self, ctx, data):
return IClient(ctx).introducer_furl
def data_connected_to_introducer(self, ctx, data):
if IClient(ctx).connected_to_introducer():
return "yes"
return "no"
def data_connected_to_vdrive(self, ctx, data):
if IClient(ctx).getServiceNamed("vdrive").have_public_root():
return "yes"
return "no"
def data_num_peers(self, ctx, data):
#client = inevow.ISite(ctx)._client
client = IClient(ctx)
return len(list(client.get_all_peerids()))
def data_peers(self, ctx, data):
d = []
client = IClient(ctx)
for nodeid in sorted(client.get_all_peerids()):
row = (idlib.b2a(nodeid),)
d.append(row)
return d
def render_row(self, ctx, data):
(nodeid_a,) = data
ctx.fillSlots("peerid", nodeid_a)
return ctx.tag
def render_global_vdrive(self, ctx, data):
if IClient(ctx).getServiceNamed("vdrive").have_public_root():
return T.p["To view the global shared filestore, ",
T.a(href="../global_vdrive")["Click Here!"],
]
return T.p["vdrive.furl not specified (or vdrive server not "
"responding), no vdrive available."]
def render_private_vdrive(self, ctx, data):
if IClient(ctx).getServiceNamed("vdrive").have_private_root():
return T.p["To view your personal private non-shared filestore, ",
T.a(href="../private_vdrive")["Click Here!"],
]
return T.p["personal vdrive not available."]
# this is a form where users can download files by URI
def bind_download(self, ctx):
uriarg = annotate.Argument("uri",
annotate.String("URI of file to download: "))
namearg = annotate.Argument("filename",
annotate.String("Filename to download as: "))
ctxarg = annotate.Argument("ctx", annotate.Context())
meth = annotate.Method(arguments=[uriarg, namearg, ctxarg],
label="Download File by URI")
# buttons always use value=data.label
# MethodBindingRenderer uses value=(data.action or data.label)
return annotate.MethodBinding("download", meth, action="Download")
def download(self, uri, filename, ctx):
log.msg("webish downloading URI")
target = url.here.sibling("download_uri").add("uri", uri)
if filename:
target = target.add("filename", filename)
return target
def render_forms(self, ctx, data):
return webform.renderForms()
class WebishServer(service.MultiService):
name = "webish"
@ -472,7 +829,7 @@ class WebishServer(service.MultiService):
def __init__(self, webport):
service.MultiService.__init__(self)
self.root = Root()
self.root.putChild("", url.here.child("welcome"))#Welcome())
#self.root.putChild("", url.here.child("welcome"))#Welcome())
self.site = site = appserver.NevowSite(self.root)
s = strports.service(webport, site)