diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/SignatureInfo.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/SignatureInfo.java index 7346390b3840..baf4989f1311 100644 --- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/SignatureInfo.java +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/SignatureInfo.java @@ -65,6 +65,7 @@ private SignatureInfo(Builder builder) { this.accountEmail = builder.accountEmail; this.timestamp = builder.timestamp; + // The "host" header only needs to be present and signed if using V4. if (Storage.SignUrlOption.SignatureVersion.V4.equals(signatureVersion) && (!builder.canonicalizedExtensionHeaders.containsKey("host"))) { canonicalizedExtensionHeaders = diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java index 8b3771d2c2cf..b0a3e9137a0b 100644 --- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java @@ -1035,7 +1035,9 @@ enum Option { EXT_HEADERS, SERVICE_ACCOUNT_CRED, SIGNATURE_VERSION, - HOST_NAME + HOST_NAME, + PATH_STYLE, + VIRTUAL_HOSTED_STYLE } enum SignatureVersion { @@ -1122,11 +1124,41 @@ public static SignUrlOption signWith(ServiceAccountSigner signer) { /** * Use a different host name than the default host name 'https://storage.googleapis.com'. This - * must also include the scheme component of the URI. + * must also include the scheme component of the URI. This option is particularly useful for + * developers to point requests to an alternate endpoint (e.g. a staging environment or sending + * requests through VPC). Note that if using this with the {@code withVirtualHostedStyle()} + * method, you should omit the bucket name from the hostname, as it automatically gets prepended + * to the hostname for virtual hosted-style URLs. */ public static SignUrlOption withHostName(String hostName) { return new SignUrlOption(Option.HOST_NAME, hostName); } + + /** + * Use a virtual hosted-style hostname, which adds the bucket into the host portion of the URI + * rather than the path, e.g. 'https://mybucket.storage.googleapis.com/...'. The bucket name + * will be obtained from the resource passed in. For V4 signing, this also sets the "host" + * header in the canonicalized extension headers to the virtual hosted-style host, unless that + * header is supplied via the {@code withExtHeaders()} method. + * + * @see Request Endpoints + */ + public static SignUrlOption withVirtualHostedStyle() { + return new SignUrlOption(Option.VIRTUAL_HOSTED_STYLE, ""); + } + + /** + * Generate a path-style URL, which places the bucket name in the path portion of the URL + * instead of in the hostname, e.g 'https://storage.googleapis.com/mybucket/...'. Note that this + * cannot be used alongside {@code withVirtualHostedStyle()}. Virtual hosted-style URLs, which + * can be used via the {@code withVirtualHostedStyle()} method, should generally be preferred + * instead of path-style URLs. + * + * @see Request Endpoints + */ + public static SignUrlOption withPathStyle() { + return new SignUrlOption(Option.PATH_STYLE, ""); + } } /** @@ -1556,14 +1588,14 @@ public static Builder newBuilder() { *

Example of creating a bucket. * *

{@code
-   * String bucketName = "my_unique_bucket";
+   * String bucketName = "my-unique-bucket";
    * Bucket bucket = storage.create(BucketInfo.of(bucketName));
    * }
* *

Example of creating a bucket with storage class and location. * *

{@code
-   * String bucketName = "my_unique_bucket";
+   * String bucketName = "my-unique-bucket";
    * Bucket bucket = storage.create(BucketInfo.newBuilder(bucketName)
    *     // See here for possible values: http://g.co/cloud/storage/docs/storage-classes
    *     .setStorageClass(StorageClass.COLDLINE)
@@ -1583,8 +1615,8 @@ public static Builder newBuilder() {
    * 

Example of creating a blob with no content. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * BlobId blobId = BlobId.of(bucketName, blobName);
    * BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType("text/plain").build();
    * Blob blob = storage.create(blobInfo);
@@ -1604,8 +1636,8 @@ public static Builder newBuilder() {
    * 

Example of creating a blob from a byte array. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * BlobId blobId = BlobId.of(bucketName, blobName);
    * BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType("text/plain").build();
    * Blob blob = storage.create(blobInfo, "Hello, World!".getBytes(UTF_8));
@@ -1627,8 +1659,8 @@ public static Builder newBuilder() {
    * 

Example of creating a blob from a byte array. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * BlobId blobId = BlobId.of(bucketName, blobName);
    * BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType("text/plain").build();
    * Blob blob = storage.create(blobInfo, "Hello, World!".getBytes(UTF_8), 7, 5);
@@ -1654,8 +1686,8 @@ Blob create(
    * 

Example of creating a blob from an input stream. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * InputStream content = new ByteArrayInputStream("Hello, World!".getBytes(UTF_8));
    * BlobId blobId = BlobId.of(bucketName, blobName);
    * BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType("text/plain").build();
@@ -1665,8 +1697,8 @@ Blob create(
    * 

Example of uploading an encrypted blob. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * String encryptionKey = "my_encryption_key";
    * InputStream content = new ByteArrayInputStream("Hello, World!".getBytes(UTF_8));
    *
@@ -1693,7 +1725,7 @@ Blob create(
    * otherwise a {@link StorageException} is thrown.
    *
    * 
{@code
-   * String bucketName = "my_unique_bucket";
+   * String bucketName = "my-unique-bucket";
    * long bucketMetageneration = 42;
    * Bucket bucket = storage.get(bucketName,
    *     BucketGetOption.metagenerationMatch(bucketMetageneration));
@@ -1716,7 +1748,7 @@ Blob create(
    * matches the bucket's service metageneration otherwise a {@link StorageException} is thrown.
    *
    * 
{@code
-   * String bucketName = "my_unique_bucket";
+   * String bucketName = "my-unique-bucket";
    * Bucket bucket = storage.get(bucketName, BucketGetOption.fields(BucketField.METAGENERATION));
    * storage.lockRetentionPolicy(bucket, BucketTargetOption.metagenerationMatch());
    * }
@@ -1736,8 +1768,8 @@ Blob create( * otherwise a {@link StorageException} is thrown. * *
{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * long blobMetageneration = 42;
    * Blob blob = storage.get(bucketName, blobName,
    *     BlobGetOption.metagenerationMatch(blobMetageneration));
@@ -1757,8 +1789,8 @@ Blob create(
    * otherwise a {@link StorageException} is thrown.
    *
    * 
{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * long blobMetageneration = 42;
    * BlobId blobId = BlobId.of(bucketName, blobName);
    * Blob blob = storage.get(blobId, BlobGetOption.metagenerationMatch(blobMetageneration));
@@ -1772,8 +1804,8 @@ Blob create(
    *     href="https://cloud.google.com/storage/docs/encryption/customer-supplied-keys#encrypted-elements">Encrypted
    *     Elements
    *     
{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * String blobEncryptionKey = "";
    * BlobId blobId = BlobId.of(bucketName, blobName);
    * Blob blob = storage.get(blobId, BlobGetOption.decryptionKey(blobEncryptionKey));
@@ -1789,8 +1821,8 @@ Blob create(
    * 

Example of getting information on a blob. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * BlobId blobId = BlobId.of(bucketName, blobName);
    * Blob blob = storage.get(blobId);
    * }
@@ -1826,7 +1858,7 @@ Blob create( *

Example of listing blobs in a provided directory. * *

{@code
-   * String bucketName = "my_unique_bucket";
+   * String bucketName = "my-unique-bucket";
    * String directory = "my_directory/";
    * Page blobs = storage.list(bucketName, BlobListOption.currentDirectory(),
    *     BlobListOption.prefix(directory));
@@ -1850,7 +1882,7 @@ Blob create(
    * 

Example of updating bucket information. * *

{@code
-   * String bucketName = "my_unique_bucket";
+   * String bucketName = "my-unique-bucket";
    * BucketInfo bucketInfo = BucketInfo.newBuilder(bucketName).setVersioningEnabled(true).build();
    * Bucket bucket = storage.update(bucketInfo);
    * }
@@ -1871,8 +1903,8 @@ Blob create( * {@link StorageException} is thrown. * *
{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * Blob blob = storage.get(bucketName, blobName);
    * BlobInfo updatedInfo = blob.toBuilder().setContentType("text/plain").build();
    * storage.update(updatedInfo, BlobTargetOption.metagenerationMatch());
@@ -1892,8 +1924,8 @@ Blob create(
    * 

Example of adding new metadata values or updating existing ones. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * Map newMetadata = new HashMap<>();
    * newMetadata.put("keyToAddOrUpdate", "value");
    * Blob blob = storage.update(BlobInfo.newBuilder(bucketName, blobName)
@@ -1904,8 +1936,8 @@ Blob create(
    * 

Example of removing metadata values. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * Map newMetadata = new HashMap<>();
    * newMetadata.put("keyToRemove", null);
    * Blob blob = storage.update(BlobInfo.newBuilder(bucketName, blobName)
@@ -1928,7 +1960,7 @@ Blob create(
    * StorageException} is thrown.
    *
    * 
{@code
-   * String bucketName = "my_unique_bucket";
+   * String bucketName = "my-unique-bucket";
    * long bucketMetageneration = 42;
    * boolean deleted = storage.delete(bucketName,
    *     BucketSourceOption.metagenerationMatch(bucketMetageneration));
@@ -1951,8 +1983,8 @@ Blob create(
    * StorageException} is thrown.
    *
    * 
{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * long blobGeneration = 42;
    * boolean deleted = storage.delete(bucketName, blobName,
    *     BlobSourceOption.generationMatch(blobGeneration));
@@ -1978,8 +2010,8 @@ Blob create(
    * StorageException} is thrown.
    *
    * 
{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * long blobGeneration = 42;
    * BlobId blobId = BlobId.of(bucketName, blobName);
    * boolean deleted = storage.delete(blobId, BlobSourceOption.generationMatch(blobGeneration));
@@ -2001,8 +2033,8 @@ Blob create(
    * 

Example of deleting a blob. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * BlobId blobId = BlobId.of(bucketName, blobName);
    * boolean deleted = storage.delete(blobId);
    * if (deleted) {
@@ -2026,8 +2058,8 @@ Blob create(
    * 

Example of composing two blobs. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * String sourceBlob1 = "source_blob_1";
    * String sourceBlob2 = "source_blob_2";
    * BlobId blobId = BlobId.of(bucketName, blobName);
@@ -2061,8 +2093,8 @@ Blob create(
    * 

Example of copying a blob. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * String copyBlobName = "copy_blob_name";
    * CopyRequest request = CopyRequest.newBuilder()
    *     .setSource(BlobId.of(bucketName, blobName))
@@ -2074,8 +2106,8 @@ Blob create(
    * 

Example of copying a blob in chunks. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * String copyBlobName = "copy_blob_name";
    * CopyRequest request = CopyRequest.newBuilder()
    *     .setSource(BlobId.of(bucketName, blobName))
@@ -2091,8 +2123,8 @@ Blob create(
    * 

Example of rotating the encryption key of a blob. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * String oldEncryptionKey = "old_encryption_key";
    * String newEncryptionKey = "new_encryption_key";
    * BlobId blobId = BlobId.of(bucketName, blobName);
@@ -2118,8 +2150,8 @@ Blob create(
    * StorageException} is thrown.
    *
    * 
{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * long blobGeneration = 42";
    * byte[] content = storage.readAllBytes(bucketName, blobName,
    *     BlobSourceOption.generationMatch(blobGeneration));
@@ -2137,8 +2169,8 @@ Blob create(
    * StorageException} is thrown.
    *
    * 
{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * long blobGeneration = 42;
    * BlobId blobId = BlobId.of(bucketName, blobName, blobGeneration);
    * byte[] content = storage.readAllBytes(blobId);
@@ -2147,8 +2179,8 @@ Blob create(
    * 

Example of reading all bytes of an encrypted blob. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * String decryptionKey = "my_encryption_key";
    * byte[] content = storage.readAllBytes(
    *     bucketName, blobName, BlobSourceOption.decryptionKey(decryptionKey));
@@ -2165,9 +2197,9 @@ Blob create(
    * 

Example of using a batch request to delete, update and get a blob. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName1 = "my_blob_name1";
-   * String blobName2 = "my_blob_name2";
+   * String bucketName = "my-unique-bucket";
+   * String blobName1 = "my-blob-name1";
+   * String blobName2 = "my-blob-name2";
    * StorageBatch batch = storage.batch();
    * BlobId firstBlob = BlobId.of(bucketName, blobName1);
    * BlobId secondBlob = BlobId.of(bucketName, blobName2);
@@ -2196,8 +2228,8 @@ Blob create(
    * 

Example of reading a blob's content through a reader. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * try (ReadChannel reader = storage.reader(bucketName, blobName)) {
    *   ByteBuffer bytes = ByteBuffer.allocate(64 * 1024);
    *   while (reader.read(bytes) > 0) {
@@ -2227,8 +2259,8 @@ Blob create(
    * 

Example of reading a blob's content through a reader. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * BlobId blobId = BlobId.of(bucketName, blobName);
    * try (ReadChannel reader = storage.reader(blobId)) {
    *   ByteBuffer bytes = ByteBuffer.allocate(64 * 1024);
@@ -2252,8 +2284,8 @@ Blob create(
    * 

Example of writing a blob's content through a writer. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * BlobId blobId = BlobId.of(bucketName, blobName);
    * byte[] content = "Hello, World!".getBytes(UTF_8);
    * BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType("text/plain").build();
@@ -2276,12 +2308,15 @@ Blob create(
    * 

Example of writing content through a writer using signed URL. * *

{@code
-   * String bucketName = "my_unique_bucket";
-   * String blobName = "my_blob_name";
+   * String bucketName = "my-unique-bucket";
+   * String blobName = "my-blob-name";
    * BlobId blobId = BlobId.of(bucketName, blobName);
    * byte[] content = "Hello, World!".getBytes(UTF_8);
    * BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType("text/plain").build();
-   * URL signedURL = storage.signUrl(blobInfo, 1, TimeUnit.HOURS, Storage.SignUrlOption.httpMethod(HttpMethod.POST));
+   * URL signedURL = storage.signUrl(
+   *     blobInfo,
+   *     1, TimeUnit.HOURS,
+   *     Storage.SignUrlOption.httpMethod(HttpMethod.POST));
    * try (WriteChannel writer = storage.writer(signedURL)) {
    *    writer.write(ByteBuffer.wrap(content, 0, content.length));
    * }
@@ -2314,36 +2349,60 @@ Blob create(
    *   
  • The default credentials, if no credentials were passed to {@link StorageOptions} * * - *

    Example of creating a signed URL that is valid for 2 weeks, using the default credentials - * for signing the URL. + *

    Example of creating a signed URL that is valid for 1 week, using the default credentials for + * signing the URL, the default signing method (V2), and the default URL style (path-style): * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    -   * String blobName = "my_blob_name";
    -   * URL signedUrl = storage.signUrl(BlobInfo.newBuilder(bucketName, blobName).build(), 14,
    -   *     TimeUnit.DAYS);
    +   * String bucketName = "my-unique-bucket";
    +   * String blobName = "my-blob-name";
    +   * URL signedUrl = storage.signUrl(
    +   *     BlobInfo.newBuilder(bucketName, blobName).build(),
    +   *     7, TimeUnit.DAYS);
        * }
    * *

    Example of creating a signed URL passing the {@link SignUrlOption#withV4Signature()} option, - * which enables V4 signing. + * which enables V4 signing: + * + *

    {@code
    +   * String bucketName = "my-unique-bucket";
    +   * String blobName = "my-blob-name";
    +   * URL signedUrl = storage.signUrl(
    +   *     BlobInfo.newBuilder(bucketName, blobName).build(),
    +   *     7, TimeUnit.DAYS,
    +   *     Storage.SignUrlOption.withV4Signature());
    +   * }
    + * + *

    Example of creating a signed URL passing the {@link SignUrlOption#withVirtualHostedStyle()} + * option, which specifies the bucket name in the hostname of the URI, rather than in the path: + * + *

    {@code
    +   * URL signedUrl = storage.signUrl(
    +   *     BlobInfo.newBuilder(bucketName, blobName).build(),
    +   *     1, TimeUnit.DAYS,
    +   *     Storage.SignUrlOption.withVirtualHostedStyle());
    +   * }
    + * + *

    Example of creating a signed URL passing the {@link SignUrlOption#withPathStyle()} option, + * which specifies the bucket name in path portion of the URI, rather than in the hostname: * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    -   * String blobName = "my_blob_name";
    -   * URL signedUrl = storage.signUrl(BlobInfo.newBuilder(bucketName, blobName).build(),
    -   *     7, TimeUnit.DAYS, Storage.SignUrlOption.withV4Signature());
    +   * URL signedUrl = storage.signUrl(
    +   *     BlobInfo.newBuilder(bucketName, blobName).build(),
    +   *     1, TimeUnit.DAYS,
    +   *     Storage.SignUrlOption.withPathStyle());
        * }
    * *

    Example of creating a signed URL passing the {@link * SignUrlOption#signWith(ServiceAccountSigner)} option, that will be used for signing the URL. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    -   * String blobName = "my_blob_name";
    -   * String keyPath = "/path/to/key.json";
    -   * URL signedUrl = storage.signUrl(BlobInfo.newBuilder(bucketName, blobName).build(),
    -   *     14, TimeUnit.DAYS, SignUrlOption.signWith(
    -   *         ServiceAccountCredentials.fromStream(new FileInputStream(keyPath))));
    +   * String bucketName = "my-unique-bucket";
    +   * String blobName = "my-blob-name";
    +   * String kfPath = "/path/to/keyfile.json";
    +   * URL signedUrl = storage.signUrl(
    +   *     BlobInfo.newBuilder(bucketName, blobName).build(),
    +   *     7, TimeUnit.DAYS,
    +   *     SignUrlOption.signWith(ServiceAccountCredentials.fromStream(new FileInputStream(kfPath))));
        * }
    * *

    Note that the {@link ServiceAccountSigner} may require additional configuration to enable @@ -2353,8 +2412,7 @@ Blob create( * @param duration time until the signed URL expires, expressed in {@code unit}. The finest * granularity supported is 1 second, finer granularities will be truncated * @param unit time unit of the {@code duration} parameter - * @param options optional URL signing options {@code SignUrlOption.withHostName()} option to set - * a custom host name instead of using https://storage.googleapis.com. + * @param options optional URL signing options * @throws IllegalStateException if {@link SignUrlOption#signWith(ServiceAccountSigner)} was not * used and no implementation of {@link ServiceAccountSigner} was provided to {@link * StorageOptions} @@ -2373,9 +2431,9 @@ Blob create( *

    Example of getting information on several blobs using a single batch request. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    -   * String blobName1 = "my_blob_name1";
    -   * String blobName2 = "my_blob_name2";
    +   * String bucketName = "my-unique-bucket";
    +   * String blobName1 = "my-blob-name1";
    +   * String blobName2 = "my-blob-name2";
        * BlobId firstBlob = BlobId.of(bucketName, blobName1);
        * BlobId secondBlob = BlobId.of(bucketName, blobName2);
        * List blobs = storage.get(firstBlob, secondBlob);
    @@ -2394,9 +2452,9 @@ Blob create(
        * 

    Example of getting information on several blobs using a single batch request. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    -   * String blobName1 = "my_blob_name1";
    -   * String blobName2 = "my_blob_name2";
    +   * String bucketName = "my-unique-bucket";
    +   * String blobName1 = "my-blob-name1";
    +   * String blobName2 = "my-blob-name2";
        * List blobIds = new LinkedList<>();
        * blobIds.add(BlobId.of(bucketName, blobName1));
        * blobIds.add(BlobId.of(bucketName, blobName2));
    @@ -2419,9 +2477,9 @@ Blob create(
        * 

    Example of updating information on several blobs using a single batch request. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    -   * String blobName1 = "my_blob_name1";
    -   * String blobName2 = "my_blob_name2";
    +   * String bucketName = "my-unique-bucket";
    +   * String blobName1 = "my-blob-name1";
    +   * String blobName2 = "my-blob-name2";
        * Blob firstBlob = storage.get(bucketName, blobName1);
        * Blob secondBlob = storage.get(bucketName, blobName2);
        * List updatedBlobs = storage.update(
    @@ -2445,9 +2503,9 @@ Blob create(
        * 

    Example of updating information on several blobs using a single batch request. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    -   * String blobName1 = "my_blob_name1";
    -   * String blobName2 = "my_blob_name2";
    +   * String bucketName = "my-unique-bucket";
    +   * String blobName1 = "my-blob-name1";
    +   * String blobName2 = "my-blob-name2";
        * Blob firstBlob = storage.get(bucketName, blobName1);
        * Blob secondBlob = storage.get(bucketName, blobName2);
        * List blobs = new LinkedList<>();
    @@ -2469,9 +2527,9 @@ Blob create(
        * 

    Example of deleting several blobs using a single batch request. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    -   * String blobName1 = "my_blob_name1";
    -   * String blobName2 = "my_blob_name2";
    +   * String bucketName = "my-unique-bucket";
    +   * String blobName1 = "my-blob-name1";
    +   * String blobName2 = "my-blob-name2";
        * BlobId firstBlob = BlobId.of(bucketName, blobName1);
        * BlobId secondBlob = BlobId.of(bucketName, blobName2);
        * List deleted = storage.delete(firstBlob, secondBlob);
    @@ -2491,9 +2549,9 @@ Blob create(
        * 

    Example of deleting several blobs using a single batch request. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    -   * String blobName1 = "my_blob_name1";
    -   * String blobName2 = "my_blob_name2";
    +   * String bucketName = "my-unique-bucket";
    +   * String blobName1 = "my-blob-name1";
    +   * String blobName2 = "my-blob-name2";
        * List blobIds = new LinkedList<>();
        * blobIds.add(BlobId.of(bucketName, blobName1));
        * blobIds.add(BlobId.of(bucketName, blobName2));
    @@ -2515,7 +2573,7 @@ Blob create(
        * 

    Example of getting the ACL entry for an entity on a bucket. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    +   * String bucketName = "my-unique-bucket";
        * Acl acl = storage.getAcl(bucketName, User.ofAllAuthenticatedUsers());
        * }
    * @@ -2523,7 +2581,7 @@ Blob create( * user_project option. * *
    {@code
    -   * String bucketName = "my_unique_bucket";
    +   * String bucketName = "my-unique-bucket";
        * String userEmail = "google-cloud-java-tests@java-docs-samples-tests.iam.gserviceaccount.com";
        * BucketSourceOption userProjectOption = BucketSourceOption.userProject("myProject");
        * Acl acl = storage.getAcl(bucketName, new User(userEmail), userProjectOption);
    @@ -2545,7 +2603,7 @@ Blob create(
        * 

    Example of deleting the ACL entry for an entity on a bucket. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    +   * String bucketName = "my-unique-bucket";
        * boolean deleted = storage.deleteAcl(bucketName, User.ofAllAuthenticatedUsers());
        * if (deleted) {
        *   // the acl entry was deleted
    @@ -2558,7 +2616,7 @@ Blob create(
        * user_project option.
        *
        * 
    {@code
    -   * String bucketName = "my_unique_bucket";
    +   * String bucketName = "my-unique-bucket";
        * BucketSourceOption userProject = BucketSourceOption.userProject("myProject");
        * boolean deleted = storage.deleteAcl(bucketName, User.ofAllAuthenticatedUsers(), userProject);
        * }
    @@ -2580,14 +2638,14 @@ Blob create( *

    Example of creating a new ACL entry on a bucket. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    +   * String bucketName = "my-unique-bucket";
        * Acl acl = storage.createAcl(bucketName, Acl.of(User.ofAllAuthenticatedUsers(), Role.READER));
        * }
    * *

    Example of creating a new ACL entry on a requester_pays bucket with a user_project option. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    +   * String bucketName = "my-unique-bucket";
        * Acl acl = storage.createAcl(bucketName, Acl.of(User.ofAllAuthenticatedUsers(), Role.READER),
        *     BucketSourceOption.userProject("myProject"));
        * }
    @@ -2608,14 +2666,14 @@ Blob create( *

    Example of updating a new ACL entry on a bucket. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    +   * String bucketName = "my-unique-bucket";
        * Acl acl = storage.updateAcl(bucketName, Acl.of(User.ofAllAuthenticatedUsers(), Role.OWNER));
        * }
    * *

    Example of updating a new ACL entry on a requester_pays bucket with a user_project option. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    +   * String bucketName = "my-unique-bucket";
        * Acl acl = storage.updateAcl(bucketName, Acl.of(User.ofAllAuthenticatedUsers(), Role.OWNER),
        *     BucketSourceOption.userProject("myProject"));
        * }
    @@ -2636,7 +2694,7 @@ Blob create( *

    Example of listing the ACL entries for a blob. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    +   * String bucketName = "my-unique-bucket";
        * List acls = storage.listAcls(bucketName);
        * for (Acl acl : acls) {
        *   // do something with ACL entry
    @@ -2647,7 +2705,7 @@ Blob create(
        * option.
        *
        * 
    {@code
    -   * String bucketName = "my_unique_bucket";
    +   * String bucketName = "my-unique-bucket";
        * List acls = storage.listAcls(bucketName, BucketSourceOption.userProject("myProject"));
        * for (Acl acl : acls) {
        *   // do something with ACL entry
    @@ -2673,7 +2731,7 @@ Blob create(
        * 

    Example of getting the default ACL entry for an entity on a bucket. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    +   * String bucketName = "my-unique-bucket";
        * Acl acl = storage.getDefaultAcl(bucketName, User.ofAllAuthenticatedUsers());
        * }
    * @@ -2690,7 +2748,7 @@ Blob create( *

    Example of deleting the default ACL entry for an entity on a bucket. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    +   * String bucketName = "my-unique-bucket";
        * boolean deleted = storage.deleteDefaultAcl(bucketName, User.ofAllAuthenticatedUsers());
        * if (deleted) {
        *   // the acl entry was deleted
    @@ -2713,7 +2771,7 @@ Blob create(
        * 

    Example of creating a new default ACL entry on a bucket. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    +   * String bucketName = "my-unique-bucket";
        * Acl acl =
        *     storage.createDefaultAcl(bucketName, Acl.of(User.ofAllAuthenticatedUsers(), Role.READER));
        * }
    @@ -2731,7 +2789,7 @@ Blob create( *

    Example of updating a new default ACL entry on a bucket. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    +   * String bucketName = "my-unique-bucket";
        * Acl acl =
        *     storage.updateDefaultAcl(bucketName, Acl.of(User.ofAllAuthenticatedUsers(), Role.OWNER));
        * }
    @@ -2749,7 +2807,7 @@ Blob create( *

    Example of listing the default ACL entries for a blob. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    +   * String bucketName = "my-unique-bucket";
        * List acls = storage.listDefaultAcls(bucketName);
        * for (Acl acl : acls) {
        *   // do something with ACL entry
    @@ -2767,8 +2825,8 @@ Blob create(
        * 

    Example of getting the ACL entry for an entity on a blob. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    -   * String blobName = "my_blob_name";
    +   * String bucketName = "my-unique-bucket";
    +   * String blobName = "my-blob-name";
        * long blobGeneration = 42;
        * BlobId blobId = BlobId.of(bucketName, blobName, blobGeneration);
        * Acl acl = storage.getAcl(blobId, User.ofAllAuthenticatedUsers());
    @@ -2777,8 +2835,8 @@ Blob create(
        * 

    Example of getting the ACL entry for a specific user on a blob. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    -   * String blobName = "my_blob_name";
    +   * String bucketName = "my-unique-bucket";
    +   * String blobName = "my-blob-name";
        * String userEmail = "google-cloud-java-tests@java-docs-samples-tests.iam.gserviceaccount.com";
        * BlobId blobId = BlobId.of(bucketName, blobName);
        * Acl acl = storage.getAcl(blobId, new User(userEmail));
    @@ -2794,8 +2852,8 @@ Blob create(
        * 

    Example of deleting the ACL entry for an entity on a blob. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    -   * String blobName = "my_blob_name";
    +   * String bucketName = "my-unique-bucket";
    +   * String blobName = "my-blob-name";
        * long blobGeneration = 42;
        * BlobId blobId = BlobId.of(bucketName, blobName, blobGeneration);
        * boolean deleted = storage.deleteAcl(blobId, User.ofAllAuthenticatedUsers());
    @@ -2817,8 +2875,8 @@ Blob create(
        * 

    Example of creating a new ACL entry on a blob. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    -   * String blobName = "my_blob_name";
    +   * String bucketName = "my-unique-bucket";
    +   * String blobName = "my-blob-name";
        * long blobGeneration = 42;
        * BlobId blobId = BlobId.of(bucketName, blobName, blobGeneration);
        * Acl acl = storage.createAcl(blobId, Acl.of(User.ofAllAuthenticatedUsers(), Role.READER));
    @@ -2827,8 +2885,8 @@ Blob create(
        * 

    Example of updating a blob to be public-read. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    -   * String blobName = "my_blob_name";
    +   * String bucketName = "my-unique-bucket";
    +   * String blobName = "my-blob-name";
        * long blobGeneration = 42;
        * BlobId blobId = BlobId.of(bucketName, blobName, blobGeneration);
        * Acl acl = storage.createAcl(blobId, Acl.of(User.ofAllUsers(), Role.READER));
    @@ -2844,8 +2902,8 @@ Blob create(
        * 

    Example of updating a new ACL entry on a blob. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    -   * String blobName = "my_blob_name";
    +   * String bucketName = "my-unique-bucket";
    +   * String blobName = "my-blob-name";
        * long blobGeneration = 42;
        * BlobId blobId = BlobId.of(bucketName, blobName, blobGeneration);
        * Acl acl = storage.updateAcl(blobId, Acl.of(User.ofAllAuthenticatedUsers(), Role.OWNER));
    @@ -2861,8 +2919,8 @@ Blob create(
        * 

    Example of listing the ACL entries for a blob. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    -   * String blobName = "my_blob_name";
    +   * String bucketName = "my-unique-bucket";
    +   * String blobName = "my-blob-name";
        * long blobGeneration = 42;
        * BlobId blobId = BlobId.of(bucketName, blobName, blobGeneration);
        * List acls = storage.listAcls(blobId);
    @@ -2987,7 +3045,7 @@ HmacKeyMetadata updateHmacKeyState(
        * 

    Example of getting the IAM policy for a bucket. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    +   * String bucketName = "my-unique-bucket";
        * Policy policy = storage.getIamPolicy(bucketName);
        * }
    * @@ -3004,7 +3062,7 @@ HmacKeyMetadata updateHmacKeyState( * *
    {@code
        * // We want to make all objects in our bucket publicly readable.
    -   * String bucketName = "my_unique_bucket";
    +   * String bucketName = "my-unique-bucket";
        * Policy currentPolicy = storage.getIamPolicy(bucketName);
        * Policy updatedPolicy =
        *     storage.setIamPolicy(
    @@ -3028,7 +3086,7 @@ HmacKeyMetadata updateHmacKeyState(
        * 

    Example of testing permissions on a bucket. * *

    {@code
    -   * String bucketName = "my_unique_bucket";
    +   * String bucketName = "my-unique-bucket";
        * List response =
        *     storage.testIamPermissions(
        *         bucket,
    diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java
    index 8b6ccf3b16b3..78db9fc9e283 100644
    --- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java
    +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java
    @@ -88,7 +88,9 @@ final class StorageImpl extends BaseService implements Storage {
       private static final String EMPTY_BYTE_ARRAY_CRC32C = "AAAAAA==";
       private static final String PATH_DELIMITER = "/";
       /** Signed URLs are only supported through the GCS XML API endpoint. */
    -  private static final String STORAGE_XML_HOST_NAME = "https://storage.googleapis.com";
    +  private static final String STORAGE_XML_URI_SCHEME = "https";
    +
    +  private static final String STORAGE_XML_URI_HOST_NAME = "storage.googleapis.com";
     
       private static final Function, Boolean> DELETE_FUNCTION =
           new Function, Boolean>() {
    @@ -635,9 +637,10 @@ public URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOptio
           optionMap.put(option.getOption(), option.getValue());
         }
     
    +    boolean isV2 =
    +        getPreferredSignatureVersion(optionMap).equals(SignUrlOption.SignatureVersion.V2);
         boolean isV4 =
    -        SignUrlOption.SignatureVersion.V4.equals(
    -            optionMap.get(SignUrlOption.Option.SIGNATURE_VERSION));
    +        getPreferredSignatureVersion(optionMap).equals(SignUrlOption.SignatureVersion.V4);
     
         ServiceAccountSigner credentials =
             (ServiceAccountSigner) optionMap.get(SignUrlOption.Option.SERVICE_ACCOUNT_CRED);
    @@ -655,14 +658,12 @@ public URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOptio
                     getOptions().getClock().millisTime() + unit.toMillis(duration),
                     TimeUnit.MILLISECONDS);
     
    -    String storageXmlHostName =
    -        optionMap.get(SignUrlOption.Option.HOST_NAME) != null
    -            ? (String) optionMap.get(SignUrlOption.Option.HOST_NAME)
    -            : STORAGE_XML_HOST_NAME;
    +    checkArgument(
    +        !(optionMap.containsKey(SignUrlOption.Option.VIRTUAL_HOSTED_STYLE)
    +            && optionMap.containsKey(SignUrlOption.Option.PATH_STYLE)),
    +        "Cannot specify both the VIRTUAL_HOSTED_STYLE and PATH_STYLE SignUrlOptions together.");
     
    -    // The bucket name itself should never contain a forward slash. However, parts already existed
    -    // in the code to check for this, so we remove the forward slashes to be safe here.
    -    String bucketName = CharMatcher.anyOf(PATH_DELIMITER).trimFrom(blobInfo.getBucket());
    +    String bucketName = slashlessBucketNameFromBlobInfo(blobInfo);
         String escapedBlobName = "";
         if (!Strings.isNullOrEmpty(blobInfo.getName())) {
           escapedBlobName =
    @@ -672,12 +673,28 @@ public URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOptio
                   .replace(";", "%3B");
         }
     
    -    String stPath = constructResourceUriPath(bucketName, escapedBlobName);
    +    boolean usePathStyle = shouldUsePathStyleForSignedUrl(optionMap);
    +
    +    String storageXmlHostName =
    +        usePathStyle
    +            ? STORAGE_XML_URI_SCHEME + "://" + getBaseStorageHostName(optionMap)
    +            : STORAGE_XML_URI_SCHEME + "://" + bucketName + "." + getBaseStorageHostName(optionMap);
    +
    +    String stPath =
    +        usePathStyle
    +            ? constructResourceUriPath(bucketName, escapedBlobName, optionMap)
    +            : constructResourceUriPath("", escapedBlobName, optionMap);
    +
         URI path = URI.create(stPath);
    +    // For V2 signing, even if we don't specify the bucket in the URI path, we still need the
    +    // canonical resource string that we'll sign to include the bucket.
    +    URI pathForSigning =
    +        isV2 ? URI.create(constructResourceUriPath(bucketName, escapedBlobName, optionMap)) : path;
     
         try {
           SignatureInfo signatureInfo =
    -          buildSignatureInfo(optionMap, blobInfo, expiration, path, credentials.getAccount());
    +          buildSignatureInfo(
    +              optionMap, blobInfo, expiration, pathForSigning, credentials.getAccount());
           String unsignedPayload = signatureInfo.constructUnsignedPayload();
           byte[] signatureBytes = credentials.sign(unsignedPayload.getBytes(UTF_8));
           StringBuilder stBuilder = new StringBuilder();
    @@ -705,10 +722,30 @@ public URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOptio
         }
       }
     
    -  private String constructResourceUriPath(String slashlessBucketName, String escapedBlobName) {
    +  private String constructResourceUriPath(
    +      String slashlessBucketName,
    +      String escapedBlobName,
    +      EnumMap optionMap) {
    +    if (Strings.isNullOrEmpty(slashlessBucketName)) {
    +      if (Strings.isNullOrEmpty(escapedBlobName)) {
    +        return PATH_DELIMITER;
    +      }
    +      if (escapedBlobName.startsWith(PATH_DELIMITER)) {
    +        return escapedBlobName;
    +      }
    +      return PATH_DELIMITER + escapedBlobName;
    +    }
    +
         StringBuilder pathBuilder = new StringBuilder();
         pathBuilder.append(PATH_DELIMITER).append(slashlessBucketName);
         if (Strings.isNullOrEmpty(escapedBlobName)) {
    +      boolean isV2 =
    +          getPreferredSignatureVersion(optionMap).equals(SignUrlOption.SignatureVersion.V2);
    +      // If using virtual-hosted style URLs with V2 signing, the path string for a bucket resource
    +      // must end with a forward slash.
    +      if (optionMap.containsKey(SignUrlOption.Option.VIRTUAL_HOSTED_STYLE) && isV2) {
    +        pathBuilder.append(PATH_DELIMITER);
    +      }
           return pathBuilder.toString();
         }
         if (!escapedBlobName.startsWith(PATH_DELIMITER)) {
    @@ -718,6 +755,28 @@ private String constructResourceUriPath(String slashlessBucketName, String escap
         return pathBuilder.toString();
       }
     
    +  private SignUrlOption.SignatureVersion getPreferredSignatureVersion(
    +      EnumMap optionMap) {
    +    // Check for an explicitly specified version in the map.
    +    for (SignUrlOption.SignatureVersion version : SignUrlOption.SignatureVersion.values()) {
    +      if (version.equals(optionMap.get(SignUrlOption.Option.SIGNATURE_VERSION))) {
    +        return version;
    +      }
    +    }
    +    // TODO(#6362): V2 is the default, and thus can be specified either explicitly or implicitly
    +    // Change this to V4 once we make it the default.
    +    return SignUrlOption.SignatureVersion.V2;
    +  }
    +
    +  private boolean shouldUsePathStyleForSignedUrl(EnumMap optionMap) {
    +    // TODO(#6362): If we decide to change the default style used to generate URLs, switch this
    +    // logic to return false unless PATH_STYLE was explicitly specified.
    +    if (optionMap.containsKey(SignUrlOption.Option.VIRTUAL_HOSTED_STYLE)) {
    +      return false;
    +    }
    +    return true;
    +  }
    +
       /**
        * Builds signature info.
        *
    @@ -760,14 +819,44 @@ private SignatureInfo buildSignatureInfo(
     
         signatureInfoBuilder.setTimestamp(getOptions().getClock().millisTime());
     
    -    @SuppressWarnings("unchecked")
    -    Map extHeaders =
    -        (Map)
    -            (optionMap.containsKey(SignUrlOption.Option.EXT_HEADERS)
    -                ? (Map) optionMap.get(SignUrlOption.Option.EXT_HEADERS)
    -                : Collections.emptyMap());
    +    ImmutableMap.Builder extHeaders = new ImmutableMap.Builder();
    +
    +    boolean isV4 =
    +        SignUrlOption.SignatureVersion.V4.equals(
    +            optionMap.get(SignUrlOption.Option.SIGNATURE_VERSION));
    +    if (isV4) { // We don't sign the host header for V2 signed URLs; only do this for V4.
    +      // Add the host here first, allowing it to be overridden in the EXT_HEADERS option below.
    +      if (optionMap.containsKey(SignUrlOption.Option.VIRTUAL_HOSTED_STYLE)) {
    +        extHeaders.put(
    +            "host",
    +            slashlessBucketNameFromBlobInfo(blobInfo) + "." + getBaseStorageHostName(optionMap));
    +      } else if (optionMap.containsKey(SignUrlOption.Option.HOST_NAME)) {
    +        extHeaders.put("host", getBaseStorageHostName(optionMap));
    +      }
    +    }
    +
    +    if (optionMap.containsKey(SignUrlOption.Option.EXT_HEADERS)) {
    +      extHeaders.putAll((Map) optionMap.get(SignUrlOption.Option.EXT_HEADERS));
    +    }
     
    -    return signatureInfoBuilder.setCanonicalizedExtensionHeaders(extHeaders).build();
    +    return signatureInfoBuilder
    +        .setCanonicalizedExtensionHeaders((Map) extHeaders.build())
    +        .build();
    +  }
    +
    +  private String slashlessBucketNameFromBlobInfo(BlobInfo blobInfo) {
    +    // The bucket name itself should never contain a forward slash. However, parts already existed
    +    // in the code to check for this, so we remove the forward slashes to be safe here.
    +    return CharMatcher.anyOf(PATH_DELIMITER).trimFrom(blobInfo.getBucket());
    +  }
    +
    +  /** Returns the hostname used to send requests to Cloud Storage, e.g. "storage.googleapis.com". */
    +  private String getBaseStorageHostName(Map optionMap) {
    +    String specifiedBaseHostName = (String) optionMap.get(SignUrlOption.Option.HOST_NAME);
    +    if (!Strings.isNullOrEmpty(specifiedBaseHostName)) {
    +      return specifiedBaseHostName.replaceFirst("http(s)?://", "");
    +    }
    +    return STORAGE_XML_URI_HOST_NAME;
       }
     
       @Override
    diff --git a/google-cloud-clients/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java b/google-cloud-clients/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
    index ce4fa12f30ed..305dd17e1cc6 100644
    --- a/google-cloud-clients/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
    +++ b/google-cloud-clients/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
    @@ -1819,17 +1819,21 @@ public void testGetSignedUrl() throws IOException {
         if (storage.getOptions().getCredentials() != null) {
           assumeTrue(storage.getOptions().getCredentials() instanceof ServiceAccountSigner);
         }
    -
         String blobName = "test-get-signed-url-blob/with/slashes/and?special=!#$&'()*+,:;=?@[]";
         BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).build();
         Blob remoteBlob = storage.create(blob, BLOB_BYTE_CONTENT);
         assertNotNull(remoteBlob);
    -    URL url = storage.signUrl(blob, 1, TimeUnit.HOURS);
    -    URLConnection connection = url.openConnection();
    -    byte[] readBytes = new byte[BLOB_BYTE_CONTENT.length];
    -    try (InputStream responseStream = connection.getInputStream()) {
    -      assertEquals(BLOB_BYTE_CONTENT.length, responseStream.read(readBytes));
    -      assertArrayEquals(BLOB_BYTE_CONTENT, readBytes);
    +    for (Storage.SignUrlOption urlStyle :
    +        Arrays.asList(
    +            Storage.SignUrlOption.withPathStyle(),
    +            Storage.SignUrlOption.withVirtualHostedStyle())) {
    +      URL url = storage.signUrl(blob, 1, TimeUnit.HOURS, urlStyle);
    +      URLConnection connection = url.openConnection();
    +      byte[] readBytes = new byte[BLOB_BYTE_CONTENT.length];
    +      try (InputStream responseStream = connection.getInputStream()) {
    +        assertEquals(BLOB_BYTE_CONTENT.length, responseStream.read(readBytes));
    +        assertArrayEquals(BLOB_BYTE_CONTENT, readBytes);
    +      }
         }
       }
     
    @@ -1841,15 +1845,22 @@ public void testPostSignedUrl() throws IOException {
         String blobName = "test-post-signed-url-blob";
         BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).build();
         assertNotNull(storage.create(blob));
    -    URL url =
    -        storage.signUrl(blob, 1, TimeUnit.HOURS, Storage.SignUrlOption.httpMethod(HttpMethod.POST));
    -    URLConnection connection = url.openConnection();
    -    connection.setDoOutput(true);
    -    connection.connect();
    -    Blob remoteBlob = storage.get(BUCKET, blobName);
    -    assertNotNull(remoteBlob);
    -    assertEquals(blob.getBucket(), remoteBlob.getBucket());
    -    assertEquals(blob.getName(), remoteBlob.getName());
    +    for (Storage.SignUrlOption urlStyle :
    +        Arrays.asList(
    +            Storage.SignUrlOption.withPathStyle(),
    +            Storage.SignUrlOption.withVirtualHostedStyle())) {
    +
    +      URL url =
    +          storage.signUrl(
    +              blob, 1, TimeUnit.HOURS, Storage.SignUrlOption.httpMethod(HttpMethod.POST), urlStyle);
    +      URLConnection connection = url.openConnection();
    +      connection.setDoOutput(true);
    +      connection.connect();
    +      Blob remoteBlob = storage.get(BUCKET, blobName);
    +      assertNotNull(remoteBlob);
    +      assertEquals(blob.getBucket(), remoteBlob.getBucket());
    +      assertEquals(blob.getName(), remoteBlob.getName());
    +    }
       }
     
       @Test
    @@ -1862,12 +1873,20 @@ public void testV4SignedUrl() throws IOException {
         BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).build();
         Blob remoteBlob = storage.create(blob, BLOB_BYTE_CONTENT);
         assertNotNull(remoteBlob);
    -    URL url = storage.signUrl(blob, 1, TimeUnit.HOURS, Storage.SignUrlOption.withV4Signature());
    -    URLConnection connection = url.openConnection();
    -    byte[] readBytes = new byte[BLOB_BYTE_CONTENT.length];
    -    try (InputStream responseStream = connection.getInputStream()) {
    -      assertEquals(BLOB_BYTE_CONTENT.length, responseStream.read(readBytes));
    -      assertArrayEquals(BLOB_BYTE_CONTENT, readBytes);
    +    for (Storage.SignUrlOption urlStyle :
    +        Arrays.asList(
    +            Storage.SignUrlOption.withPathStyle(),
    +            Storage.SignUrlOption.withVirtualHostedStyle())) {
    +
    +      URL url =
    +          storage.signUrl(
    +              blob, 1, TimeUnit.HOURS, Storage.SignUrlOption.withV4Signature(), urlStyle);
    +      URLConnection connection = url.openConnection();
    +      byte[] readBytes = new byte[BLOB_BYTE_CONTENT.length];
    +      try (InputStream responseStream = connection.getInputStream()) {
    +        assertEquals(BLOB_BYTE_CONTENT.length, responseStream.read(readBytes));
    +        assertArrayEquals(BLOB_BYTE_CONTENT, readBytes);
    +      }
         }
       }
     
    @@ -2661,23 +2680,29 @@ public void testUploadUsingSignedURL() throws Exception {
         String blobName = "test-signed-url-upload";
         BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).build();
         assertNotNull(storage.create(blob));
    -    URL signUrl =
    -        storage.signUrl(blob, 1, TimeUnit.HOURS, Storage.SignUrlOption.httpMethod(HttpMethod.POST));
    -    byte[] bytesArrayToUpload = BLOB_STRING_CONTENT.getBytes();
    -    try (WriteChannel writer = storage.writer(signUrl)) {
    -      writer.write(ByteBuffer.wrap(bytesArrayToUpload, 0, bytesArrayToUpload.length));
    -    }
    +    for (Storage.SignUrlOption urlStyle :
    +        Arrays.asList(
    +            Storage.SignUrlOption.withPathStyle(),
    +            Storage.SignUrlOption.withVirtualHostedStyle())) {
    +      URL signUrl =
    +          storage.signUrl(
    +              blob, 1, TimeUnit.HOURS, Storage.SignUrlOption.httpMethod(HttpMethod.POST), urlStyle);
    +      byte[] bytesArrayToUpload = BLOB_STRING_CONTENT.getBytes();
    +      try (WriteChannel writer = storage.writer(signUrl)) {
    +        writer.write(ByteBuffer.wrap(bytesArrayToUpload, 0, bytesArrayToUpload.length));
    +      }
     
    -    int lengthOfDownLoadBytes = -1;
    -    BlobId blobId = BlobId.of(BUCKET, blobName);
    -    Blob blobToRead = storage.get(blobId);
    -    try (ReadChannel reader = blobToRead.reader()) {
    -      ByteBuffer bytes = ByteBuffer.allocate(64 * 1024);
    -      lengthOfDownLoadBytes = reader.read(bytes);
    -    }
    +      int lengthOfDownLoadBytes = -1;
    +      BlobId blobId = BlobId.of(BUCKET, blobName);
    +      Blob blobToRead = storage.get(blobId);
    +      try (ReadChannel reader = blobToRead.reader()) {
    +        ByteBuffer bytes = ByteBuffer.allocate(64 * 1024);
    +        lengthOfDownLoadBytes = reader.read(bytes);
    +      }
     
    -    assertEquals(bytesArrayToUpload.length, lengthOfDownLoadBytes);
    -    assertTrue(storage.delete(BUCKET, blobName));
    +      assertEquals(bytesArrayToUpload.length, lengthOfDownLoadBytes);
    +      assertTrue(storage.delete(BUCKET, blobName));
    +    }
       }
     
       @Test