¡@

Home 

\n' % \

                cgi.escape(env['PATH_INFO'])

        if prefix:

            body += '   \n' \

                    '    ../\n' \

                    '     \n' \

                    '     \n' \

                    '   \n'

        for item in listing:

            if 'subdir' in item:

                subdir = get_valid_utf8_str(item['subdir'])

                if prefix:

                    subdir = subdir[len(prefix):]

                body += '   \n' \

                        '    %s\n' \

                        '     \n' \

                        '     \n' \

                        '   \n' % \

                        (quote(subdir), cgi.escape(subdir))

        for item in listing:

            if 'name' in item:

                name = get_valid_utf8_str(item['name'])

                if prefix:

                    name = name[len(prefix):]

                content_type = get_valid_utf8_str(item['content_type'])

                bytes = get_valid_utf8_str(human_readable(item['bytes']))

                last_modified = (cgi.escape(item['last_modified']).

                                 split('.')[0].replace('T', ' '))

                body += '   \n' \

                        '    %s\n' \

                        '    %s\n' \

                        '    %s\n' \

                        '   \n' % \

                        (' '.join('type-' + cgi.escape(t.lower(), quote=True)

                                  for t in content_type.split('/')),

                         quote(name), cgi.escape(name),

                         bytes, get_valid_utf8_str(last_modified))

        body += '  

OpenStack Study: staticweb.py

OpenStack Index

**** CubicPower OpenStack Study ****

# Copyright (c) 2010-2012 OpenStack Foundation

#

# 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.

"""

This StaticWeb WSGI middleware will serve container data as a static web site

with index file and error file resolution and optional file listings. This mode

is normally only active for anonymous requests. When using keystone for

authentication set ``delay_auth_decision = true`` in the authtoken middleware

configuration in your ``/etc/swift/proxy-server.conf`` file. If you want to

use it with authenticated requests, set the ``X-Web-Mode: true`` header on the

request.

The ``staticweb`` filter should be added to the pipeline in your

``/etc/swift/proxy-server.conf`` file just after any auth middleware. Also, the

configuration section for the ``staticweb`` middleware itself needs to be

added. For example::

[DEFAULT]

...

[pipeline:main]

pipeline = catch_errors healthcheck proxy-logging cache ratelimit tempauth

staticweb proxy-logging proxy-server

...

[filter:staticweb]

use = egg:swift#staticweb

Any publicly readable containers (for example, ``X-Container-Read: .r:*``, see

:ref:`acls` for more information on this) will be checked for

X-Container-Meta-Web-Index and X-Container-Meta-Web-Error header values::

X-Container-Meta-Web-Index

X-Container-Meta-Web-Error

If X-Container-Meta-Web-Index is set, any files will be served

without having to specify the part. For instance, setting

``X-Container-Meta-Web-Index: index.html`` will be able to serve the object

.../pseudo/path/index.html with just .../pseudo/path or .../pseudo/path/

If X-Container-Meta-Web-Error is set, any errors (currently just 401

Unauthorized and 404 Not Found) will instead serve the

.../ object. For instance, setting

``X-Container-Meta-Web-Error: error.html`` will serve .../404error.html for

requests for paths not found.

For pseudo paths that have no , this middleware can serve HTML file

listings if you set the ``X-Container-Meta-Web-Listings: true`` metadata item

on the container.

If listings are enabled, the listings can have a custom style sheet by setting

the X-Container-Meta-Web-Listings-CSS header. For instance, setting

``X-Container-Meta-Web-Listings-CSS: listing.css`` will make listings link to

the .../listing.css style sheet. If you "view source" in your browser on a

listing page, you will see the well defined document structure that can be

styled.

The content-type of directory marker objects can be modified by setting

the ``X-Container-Meta-Web-Directory-Type`` header. If the header is not set,

application/directory is used by default. Directory marker objects are

0-byte objects that represent directories to create a simulated hierarchical

structure.

Example usage of this middleware via ``swift``:

Make the container publicly readable::

swift post -r '.r:*' container

You should be able to get objects directly, but no index.html resolution or

listings.

Set an index file directive::

swift post -m 'web-index:index.html' container

You should be able to hit paths that have an index.html without needing to

type the index.html part.

Turn on listings::

swift post -m 'web-listings: true' container

Now you should see object listings for paths and pseudo paths that have no

index.html.

Enable a custom listings style sheet::

swift post -m 'web-listings-css:listings.css' container

Set an error file::

swift post -m 'web-error:error.html' container

Now 401's should load 401error.html, 404's should load 404error.html, etc.

Set Content-Type of directory marker object::

swift post -m 'web-directory-type:text/directory' container

Now 0-byte objects with a content-type of text/directory will be treated

as directories rather than objects.

"""

import cgi

import time

from swift.common.utils import human_readable, split_path, config_true_value, \

json, quote, get_valid_utf8_str, register_swift_info

from swift.common.wsgi import make_pre_authed_env, WSGIContext

from swift.common.http import is_success, is_redirection, HTTP_NOT_FOUND

from swift.common.swob import Response, HTTPMovedPermanently, HTTPNotFound

from swift.proxy.controllers.base import get_container_info

**** CubicPower OpenStack Study ****

class _StaticWebContext(WSGIContext):

"""

The Static Web WSGI middleware filter; serves container data as a

static web site. See `staticweb`_ for an overview.

This _StaticWebContext is used by StaticWeb with each request

that might need to be handled to make keeping contextual

information about the request a bit simpler than storing it in

the WSGI env.

"""

**** CubicPower OpenStack Study ****

    def __init__(self, staticweb, version, account, container, obj):

        WSGIContext.__init__(self, staticweb.app)

        self.version = version

        self.account = account

        self.container = container

        self.obj = obj

        self.app = staticweb.app

        self.agent = '%(orig)s StaticWeb'

        # Results from the last call to self._get_container_info.

        self._index = self._error = self._listings = self._listings_css = \

            self._dir_type = None

**** CubicPower OpenStack Study ****

    def _error_response(self, response, env, start_response):

        """

        Sends the error response to the remote client, possibly resolving a

        custom error response body based on x-container-meta-web-error.

        :param response: The error response we should default to sending.

        :param env: The original request WSGI environment.

        :param start_response: The WSGI start_response hook.

        """

        if not self._error:

            start_response(self._response_status, self._response_headers,

                           self._response_exc_info)

            return response

        save_response_status = self._response_status

        save_response_headers = self._response_headers

        save_response_exc_info = self._response_exc_info

        resp = self._app_call(make_pre_authed_env(

            env, 'GET', '/%s/%s/%s/%s%s' % (

                self.version, self.account, self.container,

                self._get_status_int(), self._error),

            self.agent, swift_source='SW'))

        if is_success(self._get_status_int()):

            start_response(save_response_status, self._response_headers,

                           self._response_exc_info)

            return resp

        start_response(save_response_status, save_response_headers,

                       save_response_exc_info)

        return response

**** CubicPower OpenStack Study ****

    def _get_container_info(self, env):

        """

        Retrieves x-container-meta-web-index, x-container-meta-web-error,

        x-container-meta-web-listings, x-container-meta-web-listings-css,

        and x-container-meta-web-directory-type from memcache or from the

        cluster and stores the result in memcache and in self._index,

        self._error, self._listings, self._listings_css and self._dir_type.

        :param env: The WSGI environment dict.

        """

        self._index = self._error = self._listings = self._listings_css = \

            self._dir_type = None

        container_info = get_container_info(env, self.app, swift_source='SW')

        if is_success(container_info['status']):

            meta = container_info.get('meta', {})

            self._index = meta.get('web-index', '').strip()

            self._error = meta.get('web-error', '').strip()

            self._listings = meta.get('web-listings', '').strip()

            self._listings_css = meta.get('web-listings-css', '').strip()

            self._dir_type = meta.get('web-directory-type', '').strip()

**** CubicPower OpenStack Study ****

    def _listing(self, env, start_response, prefix=None):

        """

        Sends an HTML object listing to the remote client.

        :param env: The original WSGI environment dict.

        :param start_response: The original WSGI start_response hook.

        :param prefix: Any prefix desired for the container listing.

        """

        if not config_true_value(self._listings):

            body = '                'Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\n' \

                '\n' \

                '\n' \

                'Listing of %s\n' % cgi.escape(env['PATH_INFO'])

            if self._listings_css:

                body += '                      'href="%s" />\n' % self._build_css_path(prefix or '')

            else:

                body += '  \n' \

                    '   h1 {font-size: 1em; font-weight: bold;}\n' \

                    '   p {font-size: 2}\n' \

                    '  \n'

            body += '\n' \

                '  

Web Listing Disabled

' \

                '   

The owner of this web site has disabled web listing.' \

                '   

If you are the owner of this web site, you can enable' \

                '   web listing by setting X-Container-Meta-Web-Listings.

'

            if self._index:

                body += '

Index File Not Found

' \

                    ' 

The owner of this web site has set ' \

                    ' X-Container-Meta-Web-Index: %s. ' \

                    ' However, this file is not found.

' % self._index

            body += ' \n\n'

            resp = HTTPNotFound(body=body)(env, self._start_response)

            return self._error_response(resp, env, start_response)

        tmp_env = make_pre_authed_env(

            env, 'GET', '/%s/%s/%s' % (

                self.version, self.account, self.container),

            self.agent, swift_source='SW')

        tmp_env['QUERY_STRING'] = 'delimiter=/&format=json'

        if prefix:

            tmp_env['QUERY_STRING'] += '&prefix=%s' % quote(prefix)

        else:

            prefix = ''

        resp = self._app_call(tmp_env)

        if not is_success(self._get_status_int()):

            return self._error_response(resp, env, start_response)

        listing = None

        body = ''.join(resp)

        if body:

            listing = json.loads(body)

        if not listing:

            resp = HTTPNotFound()(env, self._start_response)

            return self._error_response(resp, env, start_response)

        headers = {'Content-Type': 'text/html; charset=UTF-8'}

        body = '               'Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\n' \

               '\n' \

               ' \n' \

               '  Listing of %s\n' % \

               cgi.escape(env['PATH_INFO'])

        if self._listings_css:

            body += '                      'href="%s" />\n' % (self._build_css_path(prefix))

        else:

            body += '  \n' \

                    '   h1 {font-size: 1em; font-weight: bold;}\n' \

                    '   th {text-align: left; padding: 0px 1em 0px 1em;}\n' \

                    '   td {padding: 0px 1em 0px 1em;}\n' \

                    '   a {text-decoration: none;}\n' \

                    '  \n'

        body += ' \n' \

                ' \n' \

                '  Listing of %s\n' \

                '  \n' \

                '   \n' \

                '    Name\n' \

                '    Size\n' \

                '    Date\n' \

                '   

\n' \

                ' \n' \

                '\n'

        resp = Response(headers=headers, body=body)

        return resp(env, start_response)

**** CubicPower OpenStack Study ****

    def _build_css_path(self, prefix=''):

        """

        Constructs a relative path from a given prefix within the container.

        URLs and paths starting with '/' are not modified.

        :param prefix: The prefix for the container listing.

        """

        if self._listings_css.startswith(('/', 'http://', 'https://')):

            css_path = quote(self._listings_css, ':/')

        else:

            css_path = '../' * prefix.count('/') + quote(self._listings_css)

        return css_path

**** CubicPower OpenStack Study ****

    def handle_container(self, env, start_response):

        """

        Handles a possible static web request for a container.

        :param env: The original WSGI environment dict.

        :param start_response: The original WSGI start_response hook.

        """

        self._get_container_info(env)

        if not self._listings and not self._index:

            if config_true_value(env.get('HTTP_X_WEB_MODE', 'f')):

                return HTTPNotFound()(env, start_response)

            return self.app(env, start_response)

        if env['PATH_INFO'][-1] != '/':

            resp = HTTPMovedPermanently(

                location=(env['PATH_INFO'] + '/'))

            return resp(env, start_response)

        if not self._index:

            return self._listing(env, start_response)

        tmp_env = dict(env)

        tmp_env['HTTP_USER_AGENT'] = \

            '%s StaticWeb' % env.get('HTTP_USER_AGENT')

        tmp_env['swift.source'] = 'SW'

        tmp_env['PATH_INFO'] += self._index

        resp = self._app_call(tmp_env)

        status_int = self._get_status_int()

        if status_int == HTTP_NOT_FOUND:

            return self._listing(env, start_response)

        elif not is_success(self._get_status_int()) and \

                not is_redirection(self._get_status_int()):

            return self._error_response(resp, env, start_response)

        start_response(self._response_status, self._response_headers,

                       self._response_exc_info)

        return resp

**** CubicPower OpenStack Study ****

    def handle_object(self, env, start_response):

        """

        Handles a possible static web request for an object. This object could

        resolve into an index or listing request.

        :param env: The original WSGI environment dict.

        :param start_response: The original WSGI start_response hook.

        """

        tmp_env = dict(env)

        tmp_env['HTTP_USER_AGENT'] = \

            '%s StaticWeb' % env.get('HTTP_USER_AGENT')

        tmp_env['swift.source'] = 'SW'

        resp = self._app_call(tmp_env)

        status_int = self._get_status_int()

        self._get_container_info(env)

        if is_success(status_int) or is_redirection(status_int):

            # Treat directory marker objects as not found

            if not self._dir_type:

                self._dir_type = 'application/directory'

            content_length = self._response_header_value('content-length')

            content_length = int(content_length) if content_length else 0

            if self._response_header_value('content-type') == self._dir_type \

                    and content_length <= 1:

                status_int = HTTP_NOT_FOUND

            else:

                start_response(self._response_status, self._response_headers,

                               self._response_exc_info)

                return resp

        if status_int != HTTP_NOT_FOUND:

            # Retaining the previous code's behavior of not using custom error

            # pages for non-404 errors.

            self._error = None

            return self._error_response(resp, env, start_response)

        if not self._listings and not self._index:

            start_response(self._response_status, self._response_headers,

                           self._response_exc_info)

            return resp

        status_int = HTTP_NOT_FOUND

        if self._index:

            tmp_env = dict(env)

            tmp_env['HTTP_USER_AGENT'] = \

                '%s StaticWeb' % env.get('HTTP_USER_AGENT')

            tmp_env['swift.source'] = 'SW'

            if tmp_env['PATH_INFO'][-1] != '/':

                tmp_env['PATH_INFO'] += '/'

            tmp_env['PATH_INFO'] += self._index

            resp = self._app_call(tmp_env)

            status_int = self._get_status_int()

            if is_success(status_int) or is_redirection(status_int):

                if env['PATH_INFO'][-1] != '/':

                    resp = HTTPMovedPermanently(

                        location=env['PATH_INFO'] + '/')

                    return resp(env, start_response)

                start_response(self._response_status, self._response_headers,

                               self._response_exc_info)

                return resp

        if status_int == HTTP_NOT_FOUND:

            if env['PATH_INFO'][-1] != '/':

                tmp_env = make_pre_authed_env(

                    env, 'GET', '/%s/%s/%s' % (

                        self.version, self.account, self.container),

                    self.agent, swift_source='SW')

                tmp_env['QUERY_STRING'] = 'limit=1&format=json&delimiter' \

                    '=/&limit=1&prefix=%s' % quote(self.obj + '/')

                resp = self._app_call(tmp_env)

                body = ''.join(resp)

                if not is_success(self._get_status_int()) or not body or \

                        not json.loads(body):

                    resp = HTTPNotFound()(env, self._start_response)

                    return self._error_response(resp, env, start_response)

                resp = HTTPMovedPermanently(location=env['PATH_INFO'] + '/')

                return resp(env, start_response)

            return self._listing(env, start_response, self.obj)

**** CubicPower OpenStack Study ****

class StaticWeb(object):

"""

The Static Web WSGI middleware filter; serves container data as a static

web site. See `staticweb`_ for an overview.

The proxy logs created for any subrequests made will have swift.source set

to "SW".

:param app: The next WSGI application/filter in the paste.deploy pipeline.

:param conf: The filter configuration dict.

"""

**** CubicPower OpenStack Study ****

    def __init__(self, app, conf):

        #: The next WSGI application/filter in the paste.deploy pipeline.

        self.app = app

        #: The filter configuration dict.

        self.conf = conf

**** CubicPower OpenStack Study ****

    def __call__(self, env, start_response):

        """

        Main hook into the WSGI paste.deploy filter/app pipeline.

        :param env: The WSGI environment dict.

        :param start_response: The WSGI start_response hook.

        """

        env['staticweb.start_time'] = time.time()

        try:

            (version, account, container, obj) = \

                split_path(env['PATH_INFO'], 2, 4, True)

        except ValueError:

            return self.app(env, start_response)

        if env['REQUEST_METHOD'] not in ('HEAD', 'GET'):

            return self.app(env, start_response)

        if env.get('REMOTE_USER') and \

                not config_true_value(env.get('HTTP_X_WEB_MODE', 'f')):

            return self.app(env, start_response)

        if not container:

            return self.app(env, start_response)

        context = _StaticWebContext(self, version, account, container, obj)

        if obj:

            return context.handle_object(env, start_response)

        return context.handle_container(env, start_response)

def filter_factory(global_conf, **local_conf):

    """Returns a Static Web WSGI filter for use with paste.deploy."""

    conf = global_conf.copy()

    conf.update(local_conf)

    register_swift_info('staticweb')

**** CubicPower OpenStack Study ****

def filter_factory(global_conf, **local_conf):

    """Returns a Static Web WSGI filter for use with paste.deploy."""

    conf = global_conf.copy()

    conf.update(local_conf)

    register_swift_info('staticweb')

**** CubicPower OpenStack Study ****

    def staticweb_filter(app):

        return StaticWeb(app, conf)

    return staticweb_filter