Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Content-Type cannot be set using message headers in GCP adapter #1172

Closed
kimberninger opened this issue Aug 8, 2024 · 1 comment
Closed
Assignees
Labels
Milestone

Comments

@kimberninger
Copy link

Describe the bug

When using spring-cloud-function-adapter-gcp with a function that responds with a Message object to an HTTP trigger, the response's Content-Type header is not set according to the Message's corresponding header as presumably intended. It is rather overridden by the original request's content type.

It seems like PR #719 was originally meant to fix that the content type was incorrectly referenced by key contentType rather than the correct Content-Type, but actually it did not resolve that issue. Instead, it introduced the aforementioned bug. So now, the response contains two headers contentType and Content-Type which both do not reflect the value passed as message header.

Versions used:

  • Java 21
  • Spring Boot 3.3.2
  • Spring Cloud 2023.0.3
  • Spring Cloud Function & Spring Cloud Function Adapter GCP 4.1.3
  • Google Function Maven Plugin 0.11.0

Steps to reproduce

To reproduce the bug in a minimal setting, I used spring initializr with these settings to create a blank Spring Boot Application and replaced the contents of src/main/java/com/example/demo/DemoApplication.java with the following code:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;

import java.util.Map;
import java.util.function.Function;

@SpringBootApplication
public class DemoApplication {
    @Bean
    public Function<String, Message<?>> computation() {
        return request -> {
            var headers = new MessageHeaders(Map.of("Content-Type", "application/problem+json"));
            return MessageBuilder.createMessage(request, headers);
        };
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

Also, the Maven manifest had to be extended to include the Spring Cloud Function and Spring Cloud Function GCP Adapter, as well as Google's Function Maven plugin to run the function:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.2</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>21</java.version>
        <spring-cloud.version>2023.0.3</spring-cloud.version>
        <spring-cloud-function.version>4.1.3</spring-cloud-function.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-function-context</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-function-adapter-gcp</artifactId>
            <version>${spring-cloud-function.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <outputDirectory>target/deploy</outputDirectory>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.springframework.cloud</groupId>
                        <artifactId>spring-cloud-function-adapter-gcp</artifactId>
                        <version>${spring-cloud-function.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>com.google.cloud.functions</groupId>
                <artifactId>function-maven-plugin</artifactId>
                <version>0.11.0</version>
                <configuration>
                    <functionTarget>org.springframework.cloud.function.adapter.gcp.GcfJarLauncher</functionTarget>
                    <port>8080</port>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Finally, the Function Maven Plugin need a pointer to the application's main class in src/main/resources/META-INF/MANIFEST.MF:

Main-Class: com.example.demo.DemoApplication

After starting the application using ./mvnw function:run the function is now ready to accept requests:

curl -v localhost:8080 -H "Content-Type: application/json" -d '"Hello World"'

Expected behavior

The Content-Type header of the resulting response should have the value application/problem+json and no header named contentType should exist.

Actual behavior

The request above results in the following response:

HTTP/1.1 200 OK
Date: Thu, 08 Aug 2024 09:16:37 GMT
Content-Type: application/json
id: 6dcf1611-85f9-ae1d-ebd6-9a55539ac228
contentType: application/json
timestamp: 1723108597817
Transfer-Encoding: chunked
Server: Jetty(9.4.51.v20230217)

Both headers Content-Type and contentType exist and they both carry the unexpected value application/json.

If no content type is given with the request (curl -v localhost:8080 -d '"Hello World"') then the response looks as follows:

HTTP/1.1 200 OK
Date: Thu, 08 Aug 2024 09:14:18 GMT
Content-Type: application/x-www-form-urlencoded
id: 82984af0-d041-d585-2e08-f4c687009cee
contentType: application/json
timestamp: 1723108458897
Transfer-Encoding: chunked
Server: Jetty(9.4.51.v20230217)

Here the headers Content-Type and contentType still exist simultaneously, and they even carry different values.

@kimberninger
Copy link
Author

kimberninger commented Aug 8, 2024

It is probably also worth mentioning that the current behavior differs from the default behavior when using spring-cloud-starter-function-web instead of the GCP adapter to run the function. In this case, only Content-Type: application/problem+json results from the example above.

@olegz olegz self-assigned this Sep 9, 2024
@olegz olegz added the GCF label Sep 9, 2024
@olegz olegz added this to the 4.1.4 milestone Sep 9, 2024
@olegz olegz closed this as completed in 85a4fff Sep 9, 2024
olegz added a commit that referenced this issue Sep 9, 2024
Also, ensures that the proper content type is set in GCF FunctionInvoker

Resolves #1172
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants