Proxy

Proxy Controllers

Base

class swift.proxy.controllers.base.Controller(app)

Bases: object

Base WSGI controller class for the proxy

GET(req)

Handler for HTTP GET requests.

Parameters:req – The client request
Returns:the response to the client
GETorHEAD_base(req, server_type, node_iter, partition, path, concurrency=1, client_chunk_size=None)

Base handler for HTTP GET or HEAD requests.

Parameters:
  • req – swob.Request object
  • server_type – server type used in logging
  • node_iter – an iterator to obtain nodes from
  • partition – partition
  • path – path for the request
  • concurrency – number of requests to run concurrently
  • client_chunk_size – chunk size for response body iterator
Returns:

swob.Response object

HEAD(req)

Handler for HTTP HEAD requests.

Parameters:req – The client request
Returns:the response to the client
OPTIONS(req)

Base handler for OPTIONS requests

Parameters:req – swob.Request object
Returns:swob.Response object
account_info(account, req=None)

Get account information, and also verify that the account exists.

Parameters:
  • account – name of the account to get the info for
  • req – caller’s HTTP request context object (optional)
Returns:

tuple of (account partition, account nodes, container_count) or (None, None, None) if it does not exist

allowed_methods
autocreate_account(req, account)

Autocreate an account

Parameters:
  • req – request leading to this autocreate
  • account – the unquoted account name
best_response(req, statuses, reasons, bodies, server_type, etag=None, headers=None, overrides=None, quorum_size=None)

Given a list of responses from several servers, choose the best to return to the API.

Parameters:
  • req – swob.Request object
  • statuses – list of statuses returned
  • reasons – list of reasons for each status
  • bodies – bodies of each response
  • server_type – type of server the responses came from
  • etag – etag
  • headers – headers of each response
  • overrides – overrides to apply when lacking quorum
  • quorum_size – quorum size to use
Returns:

swob.Response object with the correct status, body, etc. set

container_info(account, container, req=None)

Get container information and thusly verify container existence. This will also verify account existence.

Parameters:
  • account – account name for the container
  • container – container name to look up
  • req – caller’s HTTP request context object (optional)
Returns:

dict containing at least container partition (‘partition’), container nodes (‘containers’), container read acl (‘read_acl’), container write acl (‘write_acl’), and container sync key (‘sync_key’). Values are set to None if the container does not exist.

generate_request_headers(orig_req=None, additional=None, transfer=False)

Create a list of headers to be used in backend requests

Parameters:
  • orig_req – the original request sent by the client to the proxy
  • additional – additional headers to send to the backend
  • transfer – If True, transfer headers from original client request
Returns:

a dictionary of headers

have_quorum(statuses, node_count, quorum=None)

Given a list of statuses from several requests, determine if a quorum response can already be decided.

Parameters:
  • statuses – list of statuses returned
  • node_count – number of nodes being queried (basically ring count)
  • quorum – number of statuses required for quorum
Returns:

True or False, depending on if quorum is established

is_origin_allowed(cors_info, origin)

Is the given Origin allowed to make requests to this resource

Parameters:
  • cors_info – the resource’s CORS related metadata headers
  • origin – the origin making the request
Returns:

True or False

make_requests(req, ring, part, method, path, headers, query_string='', overrides=None)

Sends an HTTP request to multiple nodes and aggregates the results. It attempts the primary nodes concurrently, then iterates over the handoff nodes as needed.

Parameters:
  • req – a request sent by the client
  • ring – the ring used for finding backend servers
  • part – the partition number
  • method – the method to send to the backend
  • path – the path to send to the backend (full path ends up being /<$device>/<$part>/<$path>)
  • headers – a list of dicts, where each dict represents one backend request that should be made.
  • query_string – optional query string to send to the backend
  • overrides – optional return status override map used to override the returned status of a request.
Returns:

a swob.Response object

pass_through_headers = []
server_type = 'Base'
transfer_headers(src_headers, dst_headers)

Transfer legal headers from an original client request to dictionary that will be used as headers by the backend request

Parameters:
  • src_headers – A dictionary of the original client request headers
  • dst_headers – A dictionary of the backend request headers
class swift.proxy.controllers.base.GetOrHeadHandler(app, req, server_type, node_iter, partition, path, backend_headers, concurrency=1, client_chunk_size=None, newest=None, header_provider=None)

Bases: swift.proxy.controllers.base.ResumingGetter

get_working_response(req)
class swift.proxy.controllers.base.NodeIter(app, ring, partition, node_iter=None)

Bases: object

Yields nodes for a ring partition, skipping over error limited nodes and stopping at the configurable number of nodes. If a node yielded subsequently gets error limited, an extra node will be yielded to take its place.

Note that if you’re going to iterate over this concurrently from multiple greenthreads, you’ll want to use a swift.common.utils.GreenthreadSafeIterator to serialize access. Otherwise, you may get ValueErrors from concurrent access. (You also may not, depending on how logging is configured, the vagaries of socket IO and eventlet, and the phase of the moon.)

Parameters:
  • app – a proxy app
  • ring – ring to get yield nodes from
  • partition – ring partition to yield nodes for
  • node_iter – optional iterable of nodes to try. Useful if you want to filter or reorder the nodes.
log_handoffs(handoffs)

Log handoff requests if handoff logging is enabled and the handoff was not expected.

We only log handoffs when we’ve pushed the handoff count further than we would normally have expected under normal circumstances, that is (request_node_count - num_primaries), when handoffs goes higher than that it means one of the primaries must have been skipped because of error limiting before we consumed all of our nodes_left.

next()
set_node_provider(callback)

Install a callback function that will be used during a call to next() to get an alternate node instead of returning the next node from the iterator.

Parameters:callback – A no argument function that should return a node dict or None.
class swift.proxy.controllers.base.ResumingGetter(app, req, server_type, node_iter, partition, path, backend_headers, concurrency=1, client_chunk_size=None, newest=None, header_provider=None)

Bases: object

fast_forward(num_bytes)

Will skip num_bytes into the current ranges.

Params num_bytes:
 

the number of bytes that have already been read on this request. This will change the Range header so that the next req will start where it left off.

Raises:
  • ValueError – if invalid range header
  • HTTPRequestedRangeNotSatisfiable – if begin + num_bytes > end of range + 1
  • RangeAlreadyComplete – if begin + num_bytes == end of range + 1
is_good_source(src)

Indicates whether or not the request made to the backend found what it was looking for.

Parameters:src – the response from the backend
Returns:True if found, False if not
last_headers
last_status
learn_size_from_content_range(start, end, length)

If client_chunk_size is set, makes sure we yield things starting on chunk boundaries based on the Content-Range header in the response.

Sets our Range header’s first byterange to the value learned from the Content-Range header in the response; if we were given a fully-specified range (e.g. “bytes=123-456”), this is a no-op.

If we were given a half-specified range (e.g. “bytes=123-” or “bytes=-456”), then this changes the Range header to a semantically-equivalent one and it lets us resume on a proper boundary instead of just in the middle of a piece somewhere.

pop_range()

Remove the first byterange from our Range header.

This is used after a byterange has been completely sent to the client; this way, should we need to resume the download from another object server, we do not re-fetch byteranges that the client already has.

If we have no Range header, this is a no-op.

response_parts_iter(req)
swift.proxy.controllers.base.bytes_to_skip(record_size, range_start)

Assume an object is composed of N records, where the first N-1 are all the same size and the last is at most that large, but may be smaller.

When a range request is made, it might start with a partial record. This must be discarded, lest the consumer get bad data. This is particularly true of suffix-byte-range requests, e.g. “Range: bytes=-12345” where the size of the object is unknown at the time the request is made.

This function computes the number of bytes that must be discarded to ensure only whole records are yielded. Erasure-code decoding needs this.

This function could have been inlined, but it took enough tries to get right that some targeted unit tests were desirable, hence its extraction.

swift.proxy.controllers.base.clear_info_cache(app, env, account, container=None)

Clear the cached info in both memcache and env

Parameters:
  • app – the application object
  • env – the WSGI environment
  • account – the account name
  • container – the containr name or None if setting info for containers
swift.proxy.controllers.base.close_swift_conn(src)

Force close the http connection to the backend.

Parameters:src – the response from the backend
swift.proxy.controllers.base.cors_validation(func)

Decorator to check if the request is a CORS request and if so, if it’s valid.

Parameters:func – function to check
swift.proxy.controllers.base.delay_denial(func)

Decorator to declare which methods should have any swift.authorize call delayed. This is so the method can load the Request object up with additional information that may be needed by the authorization system.

Parameters:func – function for which authorization will be delayed
swift.proxy.controllers.base.get_account_info(env, app, swift_source=None)

Get the info structure for an account, based on env and app. This is useful to middlewares.

Note

This call bypasses auth. Success does not imply that the request has authorization to the account.

Raises:ValueError – when path doesn’t contain an account
swift.proxy.controllers.base.get_cache_key(account, container=None, obj=None)

Get the keys for both memcache and env[‘swift.infocache’] (cache_key) where info about accounts, containers, and objects is cached

Parameters:
  • account – The name of the account
  • container – The name of the container (or None if account)
  • obj – The name of the object (or None if account or container)
Returns:

a string cache_key

swift.proxy.controllers.base.get_container_info(env, app, swift_source=None)

Get the info structure for a container, based on env and app. This is useful to middlewares.

Note

This call bypasses auth. Success does not imply that the request has authorization to the container.

swift.proxy.controllers.base.get_info(app, env, account, container=None, swift_source=None)

Get info about accounts or containers

Note: This call bypasses auth. Success does not imply that the
request has authorization to the info.
Parameters:
  • app – the application object
  • env – the environment used by the current request
  • account – The unquoted name of the account
  • container – The unquoted name of the container (or None if account)
  • swift_source – swift source logged for any subrequests made while retrieving the account or container info
Returns:

information about the specified entity in a dictionary. See get_account_info and get_container_info for details on what’s in the dictionary.

swift.proxy.controllers.base.get_object_info(env, app, path=None, swift_source=None)

Get the info structure for an object, based on env and app. This is useful to middlewares.

Note

This call bypasses auth. Success does not imply that the request has authorization to the object.

swift.proxy.controllers.base.headers_to_account_info(headers, status_int=200)

Construct a cacheable dict of account info based on response headers.

swift.proxy.controllers.base.headers_to_container_info(headers, status_int=200)

Construct a cacheable dict of container info based on response headers.

swift.proxy.controllers.base.headers_to_object_info(headers, status_int=200)

Construct a cacheable dict of object info based on response headers.

swift.proxy.controllers.base.set_info_cache(app, env, account, container, resp)

Cache info in both memcache and env.

Parameters:
  • app – the application object
  • account – the unquoted account name
  • container – the unquoted container name or None
  • resp – the response received or None if info cache should be cleared
Returns:

the info that was placed into the cache, or None if the request status was not in (404, 410, 2xx).

swift.proxy.controllers.base.set_object_info_cache(app, env, account, container, obj, resp)

Cache object info in the WSGI environment, but not in memcache. Caching in memcache would lead to cache pressure and mass evictions due to the large number of objects in a typical Swift cluster. This is a per-request cache only.

Parameters:
  • app – the application object
  • account – the unquoted account name
  • container – the unquoted container name
  • object – the unquoted object name
  • resp – a GET or HEAD response received from an object server, or None if info cache should be cleared
Returns:

the object info

swift.proxy.controllers.base.source_key(resp)

Provide the timestamp of the swift http response as a floating point value. Used as a sort key.

Parameters:resp – bufferedhttp response object
swift.proxy.controllers.base.update_headers(response, headers)

Helper function to update headers in the response.

Parameters:
  • response – swob.Response object
  • headers – dictionary headers

Account

class swift.proxy.controllers.account.AccountController(app, account_name, **kwargs)

Bases: swift.proxy.controllers.base.Controller

WSGI controller for account requests

DELETE(req)

HTTP DELETE request handler.

GETorHEAD(req)

Handler for HTTP GET/HEAD requests.

POST(req)

HTTP POST request handler.

PUT(req)

HTTP PUT request handler.

add_acls_from_sys_metadata(resp)
server_type = 'Account'

Container

class swift.proxy.controllers.container.ContainerController(app, account_name, container_name, **kwargs)

Bases: swift.proxy.controllers.base.Controller

WSGI controller for container requests

DELETE(*a, **kw)

HTTP DELETE request handler.

GET(*a, **kw)

Handler for HTTP GET requests.

GETorHEAD(req)

Handler for HTTP GET/HEAD requests.

HEAD(*a, **kw)

Handler for HTTP HEAD requests.

POST(*a, **kw)

HTTP POST request handler.

PUT(*a, **kw)

HTTP PUT request handler.

clean_acls(req)
pass_through_headers = ['x-container-read', 'x-container-write', 'x-container-sync-key', 'x-container-sync-to', 'x-versions-location']
server_type = 'Container'

Object

class swift.proxy.controllers.obj.BaseObjectController(app, account_name, container_name, object_name, **kwargs)

Bases: swift.proxy.controllers.base.Controller

Base WSGI controller for object requests.

DELETE(*a, **kw)

HTTP DELETE request handler.

GET(*a, **kw)

Handler for HTTP GET requests.

GETorHEAD(req)

Handle HTTP GET or HEAD requests.

HEAD(*a, **kw)

Handler for HTTP HEAD requests.

POST(*a, **kw)

HTTP POST request handler.

PUT(*a, **kw)

HTTP PUT request handler.

iter_nodes_local_first(ring, partition)

Yields nodes for a ring partition.

If the ‘write_affinity’ setting is non-empty, then this will yield N local nodes (as defined by the write_affinity setting) first, then the rest of the nodes as normal. It is a re-ordering of the nodes such that the local ones come first; no node is omitted. The effect is that the request will be serviced by local object servers first, but nonlocal ones will be employed if not enough local ones are available.

Parameters:
  • ring – ring to get nodes from
  • partition – ring partition to yield nodes for
server_type = 'Object'
class swift.proxy.controllers.obj.ECAppIter(path, policy, internal_parts_iters, range_specs, fa_length, obj_length, logger)

Bases: object

WSGI iterable that decodes EC fragment archives (or portions thereof) into the original object (or portions thereof).

Parameters:
  • path – object’s path, sans v1 (e.g. /a/c/o)
  • policy – storage policy for this object
  • internal_parts_iters – list of the response-document-parts iterators for the backend GET responses. For an M+K erasure code, the caller must supply M such iterables.
  • range_specs – list of dictionaries describing the ranges requested by the client. Each dictionary contains the start and end of the client’s requested byte range as well as the start and end of the EC segments containing that byte range.
  • fa_length – length of the fragment archive, in bytes, if the response is a 200. If it’s a 206, then this is ignored.
  • obj_length – length of the object, in bytes. Learned from the headers in the GET response from the object server.
  • logger – a logger
app_iter_range(start, end)
app_iter_ranges(ranges, content_type, boundary, content_size)
close()
kickoff(req, resp)

Start pulling data from the backends so that we can learn things like the real Content-Type that might only be in the multipart/byteranges response body. Update our response accordingly.

Also, this is the first point at which we can learn the MIME boundary that our response has in the headers. We grab that so we can also use it in the body.

Returns:None
Raises:HTTPException on error
class swift.proxy.controllers.obj.ECGetResponseBucket(policy, timestamp_str)

Bases: object

A helper class to encapsulate the properties of buckets in which fragment getters and alternate nodes are collected.

add_alternate_nodes(node, frag_indexes)
add_response(getter, parts_iter)
get_responses()

Return a list of all useful sources. Where there are multiple sources associated with the same frag_index then only one is included.

Returns:a list of sources, each source being a tuple of form (ResumingGetter, iter)
set_durable()
shortfall
shortfall_with_alts
class swift.proxy.controllers.obj.ECGetResponseCollection(policy)

Bases: object

Manages all successful EC GET responses gathered by ResumingGetters.

A response comprises a tuple of (<getter instance>, <parts iterator>). All responses having the same data timestamp are placed in an ECGetResponseBucket for that timestamp. The buckets are stored in the ‘buckets’ dict which maps timestamp-> bucket.

This class encapsulates logic for selecting the best bucket from the collection, and for choosing alternate nodes.

add_response(get, parts_iter)

Add a response to the collection.

Parameters:
  • get – An instance of ResumingGetter
  • parts_iter – An iterator over response body parts
Raises:

ValueError – if the response etag or status code values do not match any values previously received for the same timestamp

best_bucket

Return the best bucket in the collection.

The “best” bucket is the newest timestamp with sufficient getters, or the closest to having a sufficient getters, unless it is bettered by a bucket with potential alternate nodes.

Returns:An instance of ECGetResponseBucket or None if there are no buckets in the collection.
get_extra_headers()
has_alternate_node()
provide_alternate_node()

Callback function that is installed in a NodeIter. Called on every call to NodeIter.next(), which means we can track the number of nodes to which GET requests have been made and selectively inject an alternate node, if we have one.

Returns:A dict describing a node to which the next GET request should be made.
class swift.proxy.controllers.obj.ECObjectController(app, account_name, container_name, object_name, **kwargs)

Bases: swift.proxy.controllers.obj.BaseObjectController

policy_type = 'erasure_coding'
class swift.proxy.controllers.obj.MIMEPutter(conn, node, resp, req, connect_duration, logger, mime_boundary, multiphase=False)

Bases: swift.proxy.controllers.obj.Putter

Putter for backend PUT requests that use MIME.

This is here mostly to wrap up the fact that all multipart PUTs are chunked because of the mime boundary footer trick and the first half of the two-phase PUT conversation handling.

An HTTP PUT request that supports streaming.

classmethod connect(node, part, req, headers, conn_timeout, node_timeout, logger=None, need_multiphase=True, **kwargs)

Connect to a backend node and send the headers.

Override superclass method to notify object of need for support for multipart body with footers and optionally multiphase commit, and verify object server’s capabilities.

Parameters:need_multiphase – if True then multiphase support is required of the object server
Raises:FooterNotSupported if need_metadata_footer is set but backend node can’t process footers
Raises:MultiphasePUTNotSupported if need_multiphase is set but backend node can’t handle multiphase PUT
end_of_object_data(footer_metadata=None)

Call when there is no more data to send.

Overrides superclass implementation to send any footer metadata after object data.

Parameters:footer_metadata – dictionary of metadata items to be sent as footers.
send_commit_confirmation()

Call when there are > quorum 2XX responses received. Send commit confirmations to all object nodes to finalize the PUT.

class swift.proxy.controllers.obj.ObjectControllerRouter

Bases: object

policy_type_to_controller_map = {'erasure_coding': <class 'swift.proxy.controllers.obj.ECObjectController'>, 'replication': <class 'swift.proxy.controllers.obj.ReplicatedObjectController'>}
classmethod register(policy_type)

Decorator for Storage Policy implementations to register their ObjectController implementations.

This also fills in a policy_type attribute on the class.

class swift.proxy.controllers.obj.Putter(conn, node, resp, path, connect_duration, logger, chunked=False)

Bases: object

Putter for backend PUT requests.

Encapsulates all the actions required to establish a connection with a storage node and stream data to that node.

Parameters:
  • conn – an HTTPConnection instance
  • node – dict describing storage node
  • resp – an HTTPResponse instance if connect() received final response
  • path – the object path to send to the storage node
  • connect_duration – time taken to initiate the HTTPConnection
  • logger – a Logger instance
  • chunked – boolean indicating if the request encoding is chunked
await_response(timeout, informational=False)

Get 100-continue response indicating the end of 1st phase of a 2-phase commit or the final response, i.e. the one with status >= 200.

Might or might not actually wait for anything. If we said Expect: 100-continue but got back a non-100 response, that’ll be the thing returned, and we won’t do any network IO to get it. OTOH, if we got a 100 Continue response and sent up the PUT request’s body, then we’ll actually read the 2xx-5xx response off the network here.

Parameters:
  • timeout – time to wait for a response
  • informational – if True then try to get a 100-continue response, otherwise try to get a final response.
Returns:

HTTPResponse

Raises:

Timeout if the response took too long

close()
classmethod connect(node, part, path, headers, conn_timeout, node_timeout, logger=None, chunked=False, **kwargs)

Connect to a backend node and send the headers.

Returns:Putter instance
Raises:ConnectionTimeout if initial connection timed out
Raises:ResponseTimeout if header retrieval timed out
Raises:InsufficientStorage on 507 response from node
Raises:PutterConnectError on non-507 server error response from node
end_of_object_data(**kwargs)

Call when there is no more data to send.

send_chunk(chunk)
spawn_sender_greenthread(pool, queue_depth, write_timeout, exception_handler)

Call before sending the first chunk of request body

wait()
class swift.proxy.controllers.obj.ReplicatedObjectController(app, account_name, container_name, object_name, **kwargs)

Bases: swift.proxy.controllers.obj.BaseObjectController

policy_type = 'replication'
swift.proxy.controllers.obj.check_content_type(req)
swift.proxy.controllers.obj.chunk_transformer(policy, nstreams)
swift.proxy.controllers.obj.client_range_to_segment_range(client_start, client_end, segment_size)

Takes a byterange from the client and converts it into a byterange spanning the necessary segments.

Handles prefix, suffix, and fully-specified byte ranges.

Examples:
client_range_to_segment_range(100, 700, 512) = (0, 1023) client_range_to_segment_range(100, 700, 256) = (0, 767) client_range_to_segment_range(300, None, 256) = (256, None)
Parameters:
  • client_start – first byte of the range requested by the client
  • client_end – last byte of the range requested by the client
  • segment_size – size of an EC segment, in bytes
Returns:

a 2-tuple (seg_start, seg_end) where

  • seg_start is the first byte of the first segment, or None if this is a suffix byte range
  • seg_end is the last byte of the last segment, or None if this is a prefix byte range

swift.proxy.controllers.obj.segment_range_to_fragment_range(segment_start, segment_end, segment_size, fragment_size)

Takes a byterange spanning some segments and converts that into a byterange spanning the corresponding fragments within their fragment archives.

Handles prefix, suffix, and fully-specified byte ranges.

Parameters:
  • segment_start – first byte of the first segment
  • segment_end – last byte of the last segment
  • segment_size – size of an EC segment, in bytes
  • fragment_size – size of an EC fragment, in bytes
Returns:

a 2-tuple (frag_start, frag_end) where

  • frag_start is the first byte of the first fragment, or None if this is a suffix byte range
  • frag_end is the last byte of the last fragment, or None if this is a prefix byte range

swift.proxy.controllers.obj.trailing_metadata(policy, client_obj_hasher, bytes_transferred_from_client, fragment_archive_index)

Proxy Server

class swift.proxy.server.Application(conf, memcache=None, logger=None, account_ring=None, container_ring=None)

Bases: object

WSGI application for the proxy server.

check_config()

Check the configuration for possible errors

error_limit(node, msg)

Mark a node as error limited. This immediately pretends the node received enough errors to trigger error suppression. Use this for errors like Insufficient Storage. For other errors use error_occurred().

Parameters:
  • node – dictionary of node to error limit
  • msg – error message
error_limited(node)

Check if the node is currently error limited.

Parameters:node – dictionary of node to check
Returns:True if error limited, False otherwise
error_occurred(node, msg)

Handle logging, and handling of errors.

Parameters:
  • node – dictionary of node to handle errors for
  • msg – error message
exception_occurred(node, typ, additional_info, **kwargs)

Handle logging of generic exceptions.

Parameters:
  • node – dictionary of node to log the error for
  • typ – server type
  • additional_info – additional information to log
get_controller(req)

Get the controller to handle a request.

Parameters:req – the request
Returns:tuple of (controller class, path dictionary)
Raises:ValueError (thrown by split_path) if given invalid path
get_object_ring(policy_idx)

Get the ring object to use to handle a request based on its policy.

Parameters:policy_idx – policy index as defined in swift.conf
Returns:appropriate ring object
handle_request(req)

Entry point for proxy server. Should return a WSGI-style callable (such as swob.Response).

Parameters:req – swob.Request object
iter_nodes(ring, partition, node_iter=None)
modify_wsgi_pipeline(pipe)

Called during WSGI pipeline creation. Modifies the WSGI pipeline context to ensure that mandatory middleware is present in the pipeline.

Parameters:pipe – A PipelineWrapper object
set_node_timing(node, timing)
sort_nodes(nodes)

Sorts nodes in-place (and returns the sorted list) according to the configured strategy. The default “sorting” is to randomly shuffle the nodes. If the “timing” strategy is chosen, the nodes are sorted according to the stored timing data.

update_request(req)
swift.proxy.server.app_factory(global_conf, **local_conf)

paste.deploy app factory for creating WSGI proxy apps.