## Is your feature request related to a problem?

When sending incorrectly formatted payloads (e.g. a text-based string with a `gzip` compression header), the app will fail with a cryptic 500 error and register an unhandled exception.

## Describe the solution you'd like
The app should return a 400 bad request error, with details on why the request failed (i.e. it is incorrectly formatted or the content does not match the compression specified). In addition, this shouldn't be treated as an unhandled exception as it's a client error.


## Describe alternatives you've considered
Keep returning a cryptic 500 error.


## Additional context
Relevant context on #2701


#### *Thank you* for your feature request – we love each and every one!

posthog/utils.py
columns = [col[0] for col in cursor.description]
    return [dict(zip(columns, row)) for row in cursor.fetchall()]
def convert_property_value(input: Union[str, bool, dict, list, int]) -> str:
    if isinstance(input, bool):
        if input == True:
            return "true"
        return "false"
    if isinstance(input, dict) or isinstance(input, list):
        return json.dumps(input, sort_keys=True)
    return str(input)
def get_compare_period_dates(
    date_from: datetime.datetime, date_to: datetime.datetime
) -> Tuple[datetime.datetime, datetime.datetime]:
    new_date_to = date_from
    diff = date_to - date_from
    new_date_from = date_from - diff
    return new_date_from, new_date_to
def cors_response(request, response):
    if not request.META.get("HTTP_ORIGIN"):
        return response
    url = urlparse(request.META["HTTP_ORIGIN"])
    response["Access-Control-Allow-Origin"] = "%s://%s" % (url.scheme, url.netloc)
    response["Access-Control-Allow-Credentials"] = "true"
    response["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS"
    response["Access-Control-Allow-Headers"] = "X-Requested-With"
    return response
def generate_cache_key(stringified: str) -> str: