### Description

When a file is uploaded to GCS, [by default](https://cloud.google.com/storage/docs/metadata#cache-control), public files will get `Cache-Control: public, max-age=3600`.

I've tried setting `cors` for the whole bucket (didn't work) and setting `Cache-Control` on individual file (disappears on file re-upload from airflow)

Setting `metadata` for GCSHook is for different field (can't be used to set cache-control)

### Use case/motivation

Allow GCSHook to set cache control rather than overriding the `upload` function

### Related issues

_No response_

### Are you willing to submit a PR?

- [X] Yes I am willing to submit a PR!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)

"""
    Return True if given Google Cloud Storage URL (gs://<bucket>/<blob>)
    is a directory or an empty bucket. Otherwise return False.
    """
    _, blob = _parse_gcs_url(bucket)
    return len(blob) == 0 or blob.endswith("/")
def _parse_gcs_url(gsurl: str) -> tuple[str, str]:
    """
    Given a Google Cloud Storage URL (gs://<bucket>/<blob>), returns a
    tuple containing the corresponding bucket and blob.
    """
    parsed_url = urlsplit(gsurl)
    if not parsed_url.netloc:
        raise AirflowException("Please provide a bucket name")
    if parsed_url.scheme.lower() != "gs":
        raise AirflowException(f"Schema must be to 'gs://': Current schema: '{parsed_url.scheme}://'")
    bucket = parsed_url.netloc
    
    blob = parsed_url.path.lstrip("/")
    return bucket, blob
class GCSAsyncHook(GoogleBaseAsyncHook):
    """GCSAsyncHook run on the trigger worker, inherits from GoogleBaseHookAsync."""
    sync_hook_class = GCSHook
    async def get_storage_client(self, session: ClientSession) -> Storage:
        """Returns a Google Cloud Storage service object."""
        with await self.service_file_as_context() as file:
            return Storage(service_file=file, session=cast(Session, session))