androidretrofitokhttp

Retrofit: Remove Content-Length header


I'm using Retrofit (version 2.9.0) to make request to the API server. This library by default adds Content-Length header to the request. Unfortunately server rejects this header for "security" purposes - I don't have affect to this. Is it possible to remove this header from request using Retrofit? Or do I have to create my own rest api client?

removeHeader("Content-Length") function of Request.Builder does not works, it looks like library adds it after request build. I can change value of this header calling addHeader("Content-Length", "any-value") but it's not what I need.

override fun intercept(chain: Interceptor.Chain): Response {
        val builder = chain.request().newBuilder().apply {
            removeHeader("Content-Length") // Doesn't works
        }
        
        return chain.proceed(builder.build())
    }

#Edit

For the people below. I already confirmed that with logging interceptor, so there is no need to write useless code here. And the fuction above is from... Interceptor ... but If you want and that can help:

HeaderInterceptor

class HeaderInterceptor() : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val builder = chain.request().newBuilder().apply {
            removeHeader("Content-Length") // Doesn't works
        }

        return chain.proceed(builder.build())
    }
}

Building Retrofit Client

    @Singleton
    @Provides
    fun providesUserApi(headerInterceptor: HeaderInterceptor): UsersApi {
        val loggingInterceptor = HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        }

        val client = OkHttpClient.Builder().apply {
            addInterceptor(tokenInterceptor)
            addInterceptor(headerInterceptor)
        }.build()

        return Retrofit.Builder()
            .baseUrl(Constants.BASE_USERS_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .client(client).build()
            .create(UsersApi::class.java)
    }

API Interface

interface UsersApi {

    @POST("/api/auth/login")
    suspend fun login(@Body user: LoginRequest): Response<LoginResponse>
}

LoginRequest is simple POJO data class which holds login/password fields and LoginResponse is data returned by server.

Logcat

I/okhttp.OkHttpClient: --> POST https://this_is_api_server.com/api/auth/login
I/okhttp.OkHttpClient: Content-Length: 60
I/okhttp.OkHttpClient: {"device_type":"ANDROID","email":"admin","password":"admin"}
I/okhttp.OkHttpClient: --> END POST (60-byte body)
I/okhttp.OkHttpClient: <-- HTTP FAILED: java.net.SocketException: socket failed: EPERM (Operation not permitted)

Event log from client

I masked domain name and IP addresses with XXX.

I/okhttp.OkHttpClient: [2 ms] callStart: Request{method=POST, url=https://securedapiserver.com/api/auth/v1/loginByEmail, tags={class retrofit2.Invocation=package.name.api.UsersApi.login() [LoginRequest(device_type=ANDROID, email=test, password=test)]}}
I/okhttp.OkHttpClient: --> POST https://securedapiserver.com/api/auth/v1/loginByEmail
I/okhttp.OkHttpClient: Content-Length: 58
I/okhttp.OkHttpClient: {"device_type":"ANDROID","email":"test","password":"test"}
I/okhttp.OkHttpClient: --> END POST (58-byte body)
I/okhttp.OkHttpClient: [98 ms] proxySelectStart: https://securedapiserver.com/
I/okhttp.OkHttpClient: [103 ms] proxySelectEnd: [DIRECT]
I/okhttp.OkHttpClient: [105 ms] dnsStart: securedapiserver.com
I/okhttp.OkHttpClient: [158 ms] dnsEnd: [securedapiserver.com/XXX.XXX.XXX.XXX, securedapiserver.com/XXX.XXX.XXX.XXX, securedapiserver.com/XXX.XXX.XXX.XXX, securedapiserver.com/XXX.XXX.XXX.XXX, securedapiserver.com/XXX:XXX:XXX:XXX, securedapiserver.com/XXX:XXX:XXX:XXX, securedapiserver.com/XXX:XXX:XXX:XXX, securedapiserver.com/XXX:XXX:XXX:XXX, securedapiserver.com/XXX:XXX:XXX:XXX, securedapiserver.com/XXX:XXX:XXX:XXX, securedapiserver.com/XXX:XXX:XXX:XXX, securedapiserver.com/XXX:XXX:XXX:XXX]
I/okhttp.OkHttpClient: [175 ms] connectStart: securedapiserver.com/XXX.XXX.XXX.XXX:443 DIRECT
I/okhttp.OkHttpClient: [176 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [178 ms] connectStart: securedapiserver.com/XXX.XXX.XXX.XXX:443 DIRECT
I/okhttp.OkHttpClient: [179 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [180 ms] connectStart: securedapiserver.com/XXX.XXX.XXX.XXX:443 DIRECT
I/okhttp.OkHttpClient: [181 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [182 ms] connectStart: securedapiserver.com/XXX.XXX.XXX.XXX:443 DIRECT
I/okhttp.OkHttpClient: [183 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [184 ms] connectStart: securedapiserver.com/XXX:XXX:XXX:XXX:443 DIRECT
I/okhttp.OkHttpClient: [184 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [186 ms] connectStart: securedapiserver.com/XXX:XXX:XXX:XXX:443 DIRECT
I/okhttp.OkHttpClient: [187 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [188 ms] connectStart: securedapiserver.com/XXX:XXX:XXX:XXX:443 DIRECT
I/okhttp.OkHttpClient: [189 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [191 ms] connectStart: securedapiserver.com/XXX:XXX:XXX:XXX:443 DIRECT
I/okhttp.OkHttpClient: [194 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [196 ms] connectStart: securedapiserver.com/XXX:XXX:XXX:XXX:443 DIRECT
I/okhttp.OkHttpClient: [197 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [198 ms] connectStart: securedapiserver.com/XXX:XXX:XXX:XXX:443 DIRECT
I/okhttp.OkHttpClient: [199 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [201 ms] connectStart: securedapiserver.com/XXX:XXX:XXX:XXX:443 DIRECT
I/okhttp.OkHttpClient: [201 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [202 ms] connectStart: securedapiserver.com/XXX:XXX:XXX:XXX:443 DIRECT
I/okhttp.OkHttpClient: [203 ms] connectFailed: null java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/System.out: [java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted), java.net.SocketException: socket failed: EPERM (Operation not permitted)]
I/okhttp.OkHttpClient: <-- HTTP FAILED: java.net.SocketException: socket failed: EPERM (Operation not permitted)
I/okhttp.OkHttpClient: [212 ms] callFailed: java.net.SocketException: socket failed: EPERM (Operation not permitted)
E/NetworkCall: socket failed: EPERM (Operation not permitted)

** EDIT 2021-03-19 **

Thanks to @Yuri Schimke the problem was solved. I had to completely remove emulator and create new one. It looks like there was problem with http stack?


Solution

  • java.net.SocketException: socket failed: EPERM (Operation not permitted) looks like a client side error. The Android OS blocking you app from sending network requests. You should check you have granted INTERNET permissions to your app. You probably have an exception with stacktrace that you could include that would confirm this, without seeing that we are generally guessing here.

    To confirm the headers look at adding an OkHttp network interceptor and making the change there if needed https://square.github.io/okhttp/interceptors/

    Or just start with looking at using a Logging interceptor to confirm your suspicions https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor

    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.setLevel(Level.HEADERS);
    OkHttpClient client = new OkHttpClient.Builder()
      .addInterceptor(logging)
      .build();
    

    To confirm whether this is a client side socket error, or a valid 400 response from the server you should use this approach https://stackoverflow.com/a/66398516/1542667 to log connection events.

    [0 ms] callStart: Request{method=GET, url=https://httpbin.org/get}
    [11 ms] proxySelectStart: https://httpbin.org/
    [12 ms] proxySelectEnd: [DIRECT]
    [12 ms] dnsStart: httpbin.org
    [55 ms] dnsEnd: [httpbin.org/54.147.165.197, httpbin.org/34.231.30.52, httpbin.org/54.91.118.50, httpbin.org/18.214.80.1, httpbin.org/54.166.163.67, httpbin.org/34.199.75.4]
    [62 ms] connectStart: httpbin.org/54.147.165.197:443 DIRECT
    [176 ms] secureConnectStart
    [747 ms] secureConnectEnd: Handshake{tlsVersion=TLS_1_2 cipherSuite=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 peerCertificates=[CN=httpbin.org, CN=Amazon, OU=Server CA 1B, O=Amazon, C=US, CN=Amazon Root CA 1, O=Amazon, C=US] localCertificates=[]}
    [765 ms] connectEnd: h2
    [767 ms] connectionAcquired: Connection{httpbin.org:443, proxy=DIRECT hostAddress=httpbin.org/54.147.165.197:443 cipherSuite=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 protocol=h2}
    [775 ms] requestHeadersStart
    [783 ms] requestHeadersEnd
    [993 ms] responseHeadersStart
    [994 ms] responseHeadersEnd: Response{protocol=h2, code=200, message=, url=https://httpbin.org/get}
    [999 ms] responseBodyStart
    [999 ms] responseBodyEnd: byteCount=267
    [999 ms] connectionReleased
    [1000 ms] callEnd