X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=rwlaunchpad%2Fplugins%2Frwstagingmgr%2Frift%2Ftasklets%2Frwstagingmgr%2Fserver%2Fhandler.py;fp=rwlaunchpad%2Fplugins%2Frwstagingmgr%2Frift%2Ftasklets%2Frwstagingmgr%2Fserver%2Fhandler.py;h=ce26e0644fc1077ceafafe9aa00e7474d97457a2;hb=6364d016e7f819903ff29a2ce160cb4cea61bf8f;hp=0000000000000000000000000000000000000000;hpb=c33673480c15d6e64402b547563640b2025a3cd6;p=osm%2FSO.git diff --git a/rwlaunchpad/plugins/rwstagingmgr/rift/tasklets/rwstagingmgr/server/handler.py b/rwlaunchpad/plugins/rwstagingmgr/rift/tasklets/rwstagingmgr/server/handler.py new file mode 100644 index 00000000..ce26e064 --- /dev/null +++ b/rwlaunchpad/plugins/rwstagingmgr/rift/tasklets/rwstagingmgr/server/handler.py @@ -0,0 +1,148 @@ +# +# Copyright 2016 RIFT.IO Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author(s): Varun Prasad +# Creation Date: 09/28/2016 +# + +import tornado.httpclient +import tornado.web +import tornadostreamform.multipart_streamer as multipart_streamer +import logging +import os + +MB = 1024 * 1024 +GB = 1024 * MB + +MAX_STREAMED_SIZE = 5 * GB + +class HttpMessageError(Exception): + def __init__(self, code, msg): + self.code = code + self.msg = msg + + +class RequestHandler(tornado.web.RequestHandler): + def options(self, *args, **kargs): + pass + + def set_default_headers(self): + self.set_header('Access-Control-Allow-Origin', '*') + self.set_header('Access-Control-Allow-Headers', + 'Content-Type, Cache-Control, Accept, X-Requested-With, Authorization') + self.set_header('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE') + + +class StoreStreamerPart(multipart_streamer.MultiPartStreamer): + """ + Create a Part streamer with a custom temp directory. Using the default + tmp directory and trying to move the file to $RIFT_ARTIFACTS occasionally + causes link errors. So create a temp directory within the staging area. + """ + def __init__(self, store, *args, **kwargs): + super().__init__(*args, **kwargs) + self.store = store + + def create_part(self, headers): + return multipart_streamer.TemporaryFileStreamedPart(self, headers, tmp_dir=self.store.tmp_dir) + + +@tornado.web.stream_request_body +class UploadStagingHandler(RequestHandler): + def initialize(self, store): + """Initialize the handler + + Arguments: + log - the logger that this handler should use + loop - the tasklets ioloop + + """ + self.log = logging.getLogger() + self.store = store + + self.part_streamer = None + + @tornado.gen.coroutine + def prepare(self): + """Prepare the handler for a request + + The prepare function is the first part of a request transaction. It + creates a temporary file that uploaded data can be written to. + + """ + if self.request.method != "POST": + return + + self.request.connection.set_max_body_size(MAX_STREAMED_SIZE) + + # Retrieve the content type and parameters from the request + content_type = self.request.headers.get('content-type', None) + if content_type is None: + raise tornado.httpclient.HTTPError(400, "No content type set") + + content_type, params = tornado.httputil._parse_header(content_type) + + if "multipart/form-data" != content_type.lower(): + raise tornado.httpclient.HTTPError(415, "Invalid content type") + + # You can get the total request size from the headers. + try: + total = int(self.request.headers.get("Content-Length", "0")) + except KeyError: + self.log.warning("Content length header not found") + # For any well formed browser request, Content-Length should have a value. + total = 0 + + # And here you create a streamer that will accept incoming data + self.part_streamer = StoreStreamerPart(self.store, total) + + + @tornado.gen.coroutine + def data_received(self, chunk): + """When a chunk of data is received, we forward it to the multipart streamer.""" + self.part_streamer.data_received(chunk) + + def post(self, staging_id): + """Handle a post request + + The function is called after any data associated with the body of the + request has been received. + + """ + try: + # You MUST call this to close the incoming stream. + self.part_streamer.data_complete() + desc_parts = self.part_streamer.get_parts_by_name("file") + if len(desc_parts) != 1: + raise tornado.httpclient.HTTPError(400, "File option not found") + + binary_data = desc_parts[0] + staging_area = self.store.get_staging_area(staging_id) + filename = binary_data.get_filename() + staging_area.model.name = filename + staging_area.model.size = binary_data.get_size() + + dest_file = os.path.join(staging_area.model.path, filename) + binary_data.move(dest_file) + + self.set_status(200) + self.write(tornado.escape.json_encode({ + "path": "/api/download/{}/{}".format(staging_id, filename) + })) + + finally: + self.part_streamer.release_parts() + self.finish() +