Implemented GETs for the ContentRepoResource. It all actually appears to be working.

This commit is contained in:
Kegan Dougal 2014-08-18 17:18:39 +01:00
parent 590ab24c85
commit 58548ab557
3 changed files with 65 additions and 15 deletions

View file

@ -24,7 +24,7 @@ from twisted.python.log import PythonLoggingObserver
from twisted.web.resource import Resource
from twisted.web.static import File
from twisted.web.server import Site
from synapse.http.server import JsonResource, RootRedirect, FileUploadResource
from synapse.http.server import JsonResource, RootRedirect, ContentRepoResource
from synapse.http.client import TwistedHttpClient
from synapse.api.urls import (
CLIENT_PREFIX, FEDERATION_PREFIX, WEB_CLIENT_PREFIX, CONTENT_REPO_PREFIX
@ -56,7 +56,7 @@ class SynapseHomeServer(HomeServer):
return File("webclient") # TODO configurable?
def build_resource_for_content_repo(self):
return FileUploadResource("uploads", self.auth)
return ContentRepoResource("uploads", self.auth)
def build_db_pool(self):
""" Set up all the dbs. Since all the *.sql have IF NOT EXISTS, so we

View file

@ -17,10 +17,13 @@
from syutil.jsonutil import (
encode_canonical_json, encode_pretty_printed_json
)
from synapse.api.errors import cs_exception, SynapseError, CodeMessageException
from synapse.api.errors import (
cs_exception, SynapseError, CodeMessageException, Codes, cs_error
)
from synapse.util.stringutils import random_string
from twisted.internet import defer, reactor
from twisted.protocols.basic import FileSender
from twisted.web import server, resource
from twisted.web.server import NOT_DONE_YET
from twisted.web.util import redirectTo
@ -30,7 +33,7 @@ import collections
import json
import logging
import os
import re
logger = logging.getLogger(__name__)
@ -180,16 +183,25 @@ class RootRedirect(resource.Resource):
return resource.Resource.getChild(self, name, request)
class FileUploadResource(resource.Resource):
class ContentRepoResource(resource.Resource):
"""Provides file uploading and downloading.
Uploads are POSTed to wherever this Resource is linked to. This resource
returns a "content token" which can be used to GET this content again. The
token is typically a path, but it may not be.
In this case, the token contains 3 sections:
- User ID base64d (for namespacing content to each user)
- random string
- Content type base64d (so we can return it when clients GET it)
"""
isLeaf = True
def __init__(self, directory, auth, file_map_func=None):
def __init__(self, directory, auth):
resource.Resource.__init__(self)
self.directory = directory
self.auth = auth
if not file_map_func:
file_map_func = self.map_request_to_name
self.get_name_for_request = file_map_func
if not os.path.isdir(self.directory):
os.mkdir(self.directory)
@ -210,14 +222,19 @@ class FileUploadResource(resource.Resource):
main_part = random_string(24)
# suffix with a file extension if we can make one. This is nice to
# provide a hint to clients on the file information.
# provide a hint to clients on the file information. We will also reuse
# this info to spit back the content type to the client.
suffix = ""
if request.requestHeaders.hasHeader("Content-Type"):
content_type = request.requestHeaders.getRawHeaders(
"Content-Type")[0]
suffix = "." + base64.urlsafe_b64encode(content_type)
if (content_type.split("/")[0].lower() in
["image", "video", "audio"]):
suffix = "." + content_type.split("/")[-1]
file_ext = content_type.split("/")[-1]
# be a little paranoid and only allow a-z
file_ext = re.sub("[^a-z]", "", file_ext)
suffix += "." + file_ext
file_path = os.path.join(self.directory, prefix + main_part + suffix)
logger.info("User %s is uploading a file to path %s",
@ -236,6 +253,37 @@ class FileUploadResource(resource.Resource):
defer.returnValue(file_path)
def render_GET(self, request):
# no auth here on purpose, to allow anyone to view, even across home
# servers.
# TODO: A little crude here, we could do this better.
filename = request.path.split(self.directory + "/")[1]
# be paranoid
filename = re.sub("[^0-9A-z.-_]", "", filename)
file_path = self.directory + "/" + filename
if os.path.isfile(file_path):
# filename has the content type
base64_contentype = filename.split(".")[1]
content_type = base64.urlsafe_b64decode(base64_contentype)
logger.info("Sending file %s", file_path)
f = open(file_path, 'rb')
request.setHeader('Content-Type', content_type)
d = FileSender().beginFileTransfer(f, request)
def cbFinished(ignored):
f.close()
request.finish()
d.addCallback(cbFinished)
else:
respond_with_json_bytes(
request,
404,
json.dumps(cs_error("Not found", code=Codes.NOT_FOUND)),
send_cors=True)
return server.NOT_DONE_YET
def render_POST(self, request):
self._async_render(request)
return server.NOT_DONE_YET
@ -243,13 +291,14 @@ class FileUploadResource(resource.Resource):
@defer.inlineCallbacks
def _async_render(self, request):
try:
fname = yield self.get_name_for_request(request)
fname = yield self.map_request_to_name(request)
# TODO I have a suspcious feeling this is just going to block
with open(fname, "wb") as f:
f.write(request.content.read())
respond_with_json_bytes(request, 200,
json.dumps({"path": fname}),
json.dumps({"content_token": fname}),
send_cors=True)
except CodeMessageException as e:

View file

@ -32,8 +32,9 @@ angular.module('mFileUpload', [])
console.log("Uploading " + file.name + "... to /matrix/content");
matrixService.uploadContent(file).then(
function(response) {
console.log(" -> Successfully uploaded! Available at " + location.origin + response.data.url);
deferred.resolve(location.origin + response.data.url);
var content_url = location.origin + "/matrix/content/" + response.data.content_token;
console.log(" -> Successfully uploaded! Available at " + content_url);
deferred.resolve(content_url);
},
function(error) {
console.log(" -> Failed to upload " + file.name);