def canonize_request(method, path, query, headers, body):
    """
    Generate a canonical representation of the given request.

    :param method: String representation of the request method.
    :param path: String representation of the request path.
    :param query: Optional dict of query parameters.
    :param headers: Optional dict of request headers.
    :param body: Optional request body.
    :returns: The canonical request form as bytes.
    """
    canonical = method.lower() + ' ' + path

    if query != None:
        keys = list(query.keys())
        if len(keys) > 0:
            keys.sort()
            qs = []
            for k in keys:
                qs.append(k + '=' + query[k])
            canonical += '?' + '&'.join(qs)

    canonical += '\n'

    if headers != None:
        signed_headers = headers['x-signed-headers'].split(' ')
        signed_headers.append('x-signed-headers')
        for hdr in signed_headers:
            canonical += hdr.lower() + ': ' + headers[hdr].strip() + '\n'

    canonical = bytes(canonical, 'utf-8')
    if body != None:
        if not isinstance(body, bytes):
            body = bytes(body, 'utf-8')
        canonical += body

    return canonical