square / retrofit

A type-safe HTTP client for Android and the JVM
https://square.github.io/retrofit/
Apache License 2.0
42.86k stars 7.29k forks source link

POST to a dynamic absolute URL does not work (but works with Apache HttpClient) #3908

Open mzattera opened 1 year ago

mzattera commented 1 year ago

I need to POST to an URL like https://xxxx.eu-west-1.aws.endpoints.huggingface.cloud (Hugging Face Inference Endpoint), however if I use the below API definition:

    @POST
    Single<String> myMethod(@Url @NonNull String url, @Body MyRequest req);

and I call

       String resp = myMethod("https://xxxx.eu-west-1.aws.endpoints.huggingface.cloud", req);

Retrofit POSTs to https://xxxx.eu-west-1.aws.endpoints.huggingface.cloud/ which does not work (notice the trailing /).

JakeWharton commented 1 year ago

Those URLs are equivalent. Both will result in an HTTP request which looks like

POST / HTTP/1.1
Host: xxxx.eu-west-1.aws.endpoints.huggingface.cloud

Being sent to the IP that host resolves to.

mzattera commented 1 year ago

Thanks Jake, this is what I think too however, there is something weird happening when I pass a full URL.

In the attached example, I call the same API using Apache HttpClient and Retrofit (I think I am creating the very same request). The former works well but Retrofit doesn't . However, the same Retrofit call works with a relative URL (model name), so it seems there is nothing wrong in the request, in terms of parameters and headers.

Test.zip (the endpoint in the code is public and active, so you should be able to test this yourself)

The program will produce below output:

---[Apache HtttpClient with URL: https://by62y2zqbeqalfay.eu-west-1.aws.endpoints.huggingface.cloud]------------
HTTP/1.1 200 OK
---------------
[ [ {
  "generated_text" : "Alan Turing was able to perform a \"massive computer simulation\" — which involved several \"trivial\" computer simulations of real-world situations — where the algorithm was based on Turing's mathematical formulation of the Turing machine, and which was performed through an"
} ] ]
---------------
---[Retrofit 2 with URL: https://by62y2zqbeqalfay.eu-west-1.aws.endpoints.huggingface.cloud]------------
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by retrofit2.Platform (file:/D:/.m2/repository/com/squareup/retrofit2/retrofit/2.9.0/retrofit-2.9.0.jar) to constructor java.lang.invoke.MethodHandles$Lookup(java.lang.Class,int)
WARNING: Please consider reporting this to the maintainers of retrofit2.Platform
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
retrofit2.adapter.rxjava2.HttpException: HTTP 400 Bad Request
    at retrofit2.adapter.rxjava2.BodyObservable$BodyObserver.onNext(BodyObservable.java:57)
    at retrofit2.adapter.rxjava2.BodyObservable$BodyObserver.onNext(BodyObservable.java:38)
    at retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual(CallExecuteObservable.java:48)
    at io.reactivex.Observable.subscribe(Observable.java:10151)
    at retrofit2.adapter.rxjava2.BodyObservable.subscribeActual(BodyObservable.java:35)
    at io.reactivex.Observable.subscribe(Observable.java:10151)
    at io.reactivex.internal.operators.observable.ObservableSingleSingle.subscribeActual(ObservableSingleSingle.java:35)
    at io.reactivex.Single.subscribe(Single.java:2517)
    at io.reactivex.Single.blockingGet(Single.java:2001)
    at io.github.mzattera.test.Test$HuggingFaceClient.callApi(Test.java:118)

Below the (Maven) dependencies to run the code.

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <!-- Java version -->
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>

        <!-- Library versions -->
        <jackson.version>2.14.2</jackson.version>
        <retrofit.version>2.9.0</retrofit.version>
    </properties>
    <dependencies>
        <!-- Retrofit (create Java HTTP client from REST APIs) -->
        <dependency>
            <groupId>com.squareup.retrofit2</groupId>
            <artifactId>retrofit</artifactId>
            <version>${retrofit.version}</version>
        </dependency>
        <dependency>
            <groupId>com.squareup.retrofit2</groupId>
            <artifactId>adapter-rxjava2</artifactId>
            <version>${retrofit.version}</version>
        </dependency>
        <dependency>
            <groupId>com.squareup.retrofit2</groupId>
            <artifactId>converter-jackson</artifactId>
            <version>${retrofit.version}</version>
        </dependency>

        <!-- Jackson (JSON annotations and bindings) -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>
        <!--
        https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.14</version>
        </dependency>
    </dependencies>
mzattera commented 1 year ago

I have uploaded a small Eclipse project with the code here.

drewd commented 2 months ago

Is the website still functioning? I downloaded the Eclipse project and the API returns 400 in either case. Same output using curl.

---[Apache HtttpClient with URL: https://by62y2zqbeqalfay.eu-west-1.aws.endpoints.huggingface.cloud]------------
POST https://by62y2zqbeqalfay.eu-west-1.aws.endpoints.huggingface.cloud/ HTTP/1.1
Authorization: Bearer public-endpoint-no-key-needed
Content-Type: application/json
[Content-Type: application/json; charset=UTF-8,Content-Length: 30,Chunked: false]
{"inputs":["Alan Turing was"]}
HTTP/1.1 400 Bad Request
---------------
{
  "error" : "400: Invalid state"
}

I think I have seen this before but cannot remember what I did as a workaround.