**Base String URI**
    Per `section 3.4.1.2`_ of RFC 5849.

    For example, the HTTP request::

        GET /r%20v/X?id=123 HTTP/1.1
        Host: EXAMPLE.COM:80

    is represented by the base string URI: "http://example.com/r%20v/X".

    In another example, the HTTPS request::

        GET /?q=1 HTTP/1.1
        Host: www.example.net:8080

    is represented by the base string URI: "https://www.example.net:8080/".

    .. _`section 3.4.1.2`: https://tools.ietf.org/html/rfc5849#section-3.4.1.2

    The host argument overrides the netloc part of the uri argument.

def base_string_uri(uri, host=None):
    """**Base String URI**
    Per `section 3.4.1.2`_ of RFC 5849.

    For example, the HTTP request::

        GET /r%20v/X?id=123 HTTP/1.1
        Host: EXAMPLE.COM:80

    is represented by the base string URI: "http://example.com/r%20v/X".

    In another example, the HTTPS request::

        GET /?q=1 HTTP/1.1
        Host: www.example.net:8080

    is represented by the base string URI: "https://www.example.net:8080/".

    .. _`section 3.4.1.2`: https://tools.ietf.org/html/rfc5849#section-3.4.1.2

    The host argument overrides the netloc part of the uri argument.
    """
    if not isinstance(uri, unicode_type):
        raise ValueError('uri must be a unicode object.')

    # FIXME: urlparse does not support unicode
    scheme, netloc, path, params, query, fragment = urlparse.urlparse(uri)

    # The scheme, authority, and path of the request resource URI `RFC3986`
    # are included by constructing an "http" or "https" URI representing
    # the request resource (without the query or fragment) as follows:
    #
    # .. _`RFC3986`: https://tools.ietf.org/html/rfc3986

    if not scheme or not netloc:
        raise ValueError('uri must include a scheme and netloc')

    # Per `RFC 2616 section 5.1.2`_:
    #
    # Note that the absolute path cannot be empty; if none is present in
    # the original URI, it MUST be given as "/" (the server root).
    #
    # .. _`RFC 2616 section 5.1.2`: https://tools.ietf.org/html/rfc2616#section-5.1.2
    if not path:
        path = '/'

    # 1.  The scheme and host MUST be in lowercase.
    scheme = scheme.lower()
    netloc = netloc.lower()

    # 2.  The host and port values MUST match the content of the HTTP
    #     request "Host" header field.
    if host is not None:
        netloc = host.lower()

    # 3.  The port MUST be included if it is not the default port for the
    #     scheme, and MUST be excluded if it is the default.  Specifically,
    #     the port MUST be excluded when making an HTTP request `RFC2616`_
    #     to port 80 or when making an HTTPS request `RFC2818`_ to port 443.
    #     All other non-default port numbers MUST be included.
    #
    # .. _`RFC2616`: https://tools.ietf.org/html/rfc2616
    # .. _`RFC2818`: https://tools.ietf.org/html/rfc2818
    default_ports = (
        ('http', '80'),
        ('https', '443'),
    )
    if ':' in netloc:
        host, port = netloc.split(':', 1)
        if (scheme, port) in default_ports:
            netloc = host

    v = urlparse.urlunparse((scheme, netloc, path, params, '', ''))

    # RFC 5849 does not specify which characters are encoded in the
    # "base string URI", nor how they are encoded - which is very bad, since
    # the signatures won't match if there are any differences. Fortunately,
    # most URIs only use characters that are clearly not encoded (e.g. digits
    # and A-Z, a-z), so have avoided any differences between implementations.
    #
    # The example from its section 3.4.1.2 illustrates that spaces in
    # the path are percent encoded. But it provides no guidance as to what other
    # characters (if any) must be encoded (nor how); nor if characters in the
    # other components are to be encoded or not.
    #
    # This implementation **assumes** that **only** the space is percent-encoded
    # and it is done to the entire value (not just to spaces in the path).
    #
    # This code may need to be changed if it is discovered that other characters
    # are expected to be encoded.
    #
    # Note: the "base string URI" returned by this function will be encoded
    # again before being concatenated into the "signature base string". So any
    # spaces in the URI will actually appear in the "signature base string"
    # as "%2520" (the "%20" further encoded according to section 3.6).

    return v.replace(' ', '%20')