Stay organized with collections
Save and categorize content based on your preferences.
Source code for google.appengine.api.dispatchinfo
#!/usr/bin/env python
#
# Copyright 2007 Google 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.
#
"""Dispatch configuration tools.
Library for parsing dispatch.yaml files and working with these in memory.
"""
from __future__ import absolute_import
from __future__ import unicode_literals
import os
import re
from google.appengine._internal import six_subset
if os.environ.get('APPENGINE_RUNTIME') == 'python27':
from google.appengine.api import appinfo
from google.appengine.api import validation
from google.appengine.api import yaml_builder
from google.appengine.api import yaml_listener
from google.appengine.api import yaml_object
else:
from google.appengine.api import appinfo
from google.appengine.api import validation
from google.appengine.api import yaml_builder
from google.appengine.api import yaml_listener
from google.appengine.api import yaml_object
_URL_SPLITTER_RE = re.compile(r'^([^/]+)(/.*)$')
_URL_HOST_EXACT_PATTERN_RE = re.compile(r"""
# 0 or more . terminated hostname segments (may not start or end in -).
^([a-z0-9]([a-z0-9\-]*[a-z0-9])*\.)*
# followed by a host name segment.
([a-z0-9]([a-z0-9\-]*[a-z0-9])*)$""", re.VERBOSE)
_URL_IP_V4_ADDR_RE = re.compile(r"""
#4 1-3 digit numbers separated by .
^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$""", re.VERBOSE)
_URL_HOST_SUFFIX_PATTERN_RE = re.compile(r"""
# Single star or
^([*]|
# Host prefix with no ., Ex '*-a' or
[*][a-z0-9\-]*[a-z0-9]|
# Host prefix with ., Ex '*-a.b-c.d'
[*](\.|[a-z0-9\-]*[a-z0-9]\.)([a-z0-9]([a-z0-9\-]*[a-z0-9])*\.)*
([a-z0-9]([a-z0-9\-]*[a-z0-9])*))$
""", re.VERBOSE)
APPLICATION = 'application'
DISPATCH = 'dispatch'
URL = 'url'
MODULE = 'module'
SERVICE = 'service'
[docs]class Error(Exception):
"""Base class for errors in this module."""
[docs]class DispatchEntryURLValidator(validation.Validator):
"""Validater for URL patterns."""
[docs] def Validate(self, value, unused_key=None):
"""Validates an URL pattern."""
if value is None:
raise validation.MissingAttribute('url must be specified')
if not isinstance(value, six_subset.string_types):
raise validation.ValidationError('url must be a string, not \'%r\'' %
type(value))
url_holder = ParsedURL(value)
if url_holder.host_exact:
_ValidateMatch(_URL_HOST_EXACT_PATTERN_RE, url_holder.host,
'invalid host_pattern \'%s\'' % url_holder.host)
_ValidateNotIpV4Address(url_holder.host)
else:
_ValidateMatch(_URL_HOST_SUFFIX_PATTERN_RE, url_holder.host_pattern,
'invalid host_pattern \'%s\'' % url_holder.host_pattern)
return value
[docs]class ParsedURL(object):
"""Dispath Entry URL holder class.
Attributes:
host_pattern: The host pattern component of the URL pattern.
host_exact: True if the host pattern does not start with a *.
host: host_pattern with any leading * removed.
path_pattern: The path pattern component of the URL pattern.
path_exact: True if the path_pattern does not end with a *.
path: path_pattern with any trailing * removed.
"""
def __init__(self, url_pattern):
"""Initializes this ParsedURL with an URL pattern value.
Args:
url_pattern: An URL pattern that conforms to the regular expression
'^([^/]+)(/.*)$'.
Raises:
validation.ValidationError: When url_pattern does not match the required
regular expression.
"""
split_matcher = _ValidateMatch(_URL_SPLITTER_RE, url_pattern,
'invalid url \'%s\'' % url_pattern)
self.host_pattern, self.path_pattern = split_matcher.groups()
if self.host_pattern.startswith('*'):
self.host_exact = False
self.host = self.host_pattern[1:]
else:
self.host_exact = True
self.host = self.host_pattern
if self.path_pattern.endswith('*'):
self.path_exact = False
self.path = self.path_pattern[:-1]
else:
self.path_exact = True
self.path = self.path_pattern
def _ValidateMatch(regex, value, message):
"""Validate value matches regex."""
matcher = regex.match(value)
if not matcher:
raise validation.ValidationError(message)
return matcher
def _ValidateNotIpV4Address(host):
"""Validate host is not an IPV4 address."""
matcher = _URL_IP_V4_ADDR_RE.match(host)
if matcher and sum(1 for x in matcher.groups() if int(x) <= 255) == 4:
raise validation.ValidationError('Host may not match an ipv4 address \'%s\''
% host)
return matcher
[docs]class DispatchEntry(validation.Validated):
"""A Dispatch entry describes a mapping from a URL pattern to a module."""
ATTRIBUTES = {
URL: DispatchEntryURLValidator(),
MODULE: validation.Optional(appinfo.MODULE_ID_RE_STRING),
SERVICE: validation.Optional(appinfo.MODULE_ID_RE_STRING)
}
[docs]class DispatchInfoExternal(validation.Validated):
"""Describes the format of a dispatch.yaml file."""
ATTRIBUTES = {
APPLICATION: validation.Optional(appinfo.APPLICATION_RE_STRING),
DISPATCH: validation.Optional(validation.Repeated(DispatchEntry)),
}
[docs]def LoadSingleDispatch(dispatch_info, open_fn=None):
"""Load a dispatch.yaml file or string and return a DispatchInfoExternal.
Args:
dispatch_info: The contents of a dispatch.yaml file as a string, or an open
file object.
open_fn: Function for opening files. Unused here, needed to provide
a polymorphic API used by appcfg.py yaml parsing.
Returns:
A DispatchInfoExternal instance which represents the contents of the parsed
yaml file.
Raises:
MalformedDispatchConfigurationError: The yaml file contains multiple
dispatch sections or is missing a required value.
yaml_errors.EventError: An error occured while parsing the yaml file.
"""
builder = yaml_object.ObjectBuilder(DispatchInfoExternal)
handler = yaml_builder.BuilderHandler(builder)
listener = yaml_listener.EventListener(handler)
listener.Parse(dispatch_info)
parsed_yaml = handler.GetResults()
if not parsed_yaml:
return DispatchInfoExternal()
if len(parsed_yaml) > 1:
raise MalformedDispatchConfigurationError('Multiple dispatch: sections '
'in configuration.')
dispatch_info_external = parsed_yaml[0]
for dispatch in getattr(dispatch_info_external, DISPATCH) or []:
if dispatch.module and dispatch.service:
raise MalformedDispatchConfigurationError(
'Both module: and service: in dispatch entry. Please use only one.')
if not (dispatch.module or dispatch.service):
raise MalformedDispatchConfigurationError(
"Missing required value 'service'.")
dispatch.module = dispatch.module or dispatch.service
dispatch.service = None
return dispatch_info_external
Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2023-04-04 UTC.
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Hard to understand","hardToUnderstand","thumb-down"],["Incorrect information or sample code","incorrectInformationOrSampleCode","thumb-down"],["Missing the information/samples I need","missingTheInformationSamplesINeed","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2023-04-04 UTC."],[],[]]