Android okhttp3 socket connection problem - android

Can't connect to socket server with error: java.net.ProtocolException: Expected HTTP 101 response but was '404 Not Found'.
Code:
`
private fun run() {
val client: OkHttpClient = OkHttpClient.Builder()
.readTimeout(3, TimeUnit.SECONDS)
.build()
val request: Request = Request.Builder()
.url(AppParams.SOCKET)
.build()
client.newWebSocket(request, this)
client.dispatcher.executorService.shutdown()
}
`
Url format is: "https://url.com:3003/".
The SocketIO library works correctly
Tried to remove the prefix, change the port, use another link..

You should change the link to "wss://url.com:3003/"

Related

Android , Okhttp - how to add subprotocol to websocket connection

in my app I'm using OkHttp to establish Websocket connection. Here's method to start connection :
private fun start(token: String, id: String, url: String) {
val request: Request = Request
.Builder()
.url(url)
.header("Authorization", token)
.header("iD", id)
.build()
val listener = ValidationWebSocketListener
client.newWebSocket(request, listener)
client.dispatcher.executorService.shutdown()
}
However I have to add subprotocol - "websocket" . How can I achieve that ? Is it possible ?
I haven't confirmed but it should be possible with
request = Request.Builder()
.url(...)
.addHeader("Sec-WebSocket-Protocol", "graphql-ws")
...
.build();

Retrofit2 can't test with localhost:8080

I am writing a Unit Test to test my API calls, using Retrofit2.
I have a mock of the server that I can launch locally (using localhost:8080)
I always receive a 403 error - Forbidden but it's working great with Postman
#Config(sdk = [28])
#RunWith(RobolectricTestRunner::class)
class MockServerTest {
private lateinit var result: Response<CacheResult>
private lateinit var api: FakeAPI
#Before
fun setUp() {
api = CompanySingleton.retrofit.create(FakeAPI::class.java)
}
#Test
fun getResult() {
runBlocking {
result = api.cache()
assertThat(result.isSuccessful, equalTo(true))
}
}
}
object CompanySingleton {
private val okHttpClient: OkHttpClient = OkHttpClient.Builder()
.addInterceptor(Interceptor { chain ->
val original = chain.request()
val requestBuilder = original
.newBuilder()
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "OAuth test")
.addHeader("x-device-id", "test")
val request = requestBuilder.build()
chain.proceed(request)
}
).build()
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl("http://localhost:8080/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
in my case testing with emulators I put http://10.0.2.2:8000 to access my local host
I assume that the api is being served on your laptop/pc (and postman definitely running on it can reach it using localhost). I think you should use the local IP of your laptop (e.g. 192.168.1.10) on android. Also don't forget to open 8080 port on your firewall (or simply turn it off)
When I have to test some local service, I usually use ngrok. It's simple to run, just in terminal you type:
./ngrok http 3000

Receiving incomplete response from retrofit

I am using retrofit version 2.6.1 for making http requests over the network. The JSON I am expecting is 42466 characters long. However, I am receiving only 4073 characters and API are working fine on web browser and postman.
So I added custom okhttp client and increase timeout but it does not help me.
private var okHttpClient: OkHttpClient = OkHttpClient().newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.build()
Then I tried adding a logging interceptor and I found that okhttp is receiving the response what I wanted in interceptor logs but in chunks.
private val httpInterceptor: HttpLoggingInterceptor = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
private var okHttpClient: OkHttpClient = OkHttpClient().newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.addInterceptor(httpInterceptor)
.build()
At last, I assigned http client and interceptor to retrofit builder and this is how it's look
private val centralRetrofit = Retrofit.Builder().baseUrl("https://www.********.com/")
.addConverterFactory(ScalarsConverterFactory.create())
.client(okHttpClient)
.build()
.create(MusicAccess::class.java)
So, I think using post request will help me rather than get and tried taking all the response in string format to check the reponse
#Headers("Content-Type: text/html; charset=UTF-8")
#POST("**********")
fun getMusic(): Call<String>
But it also did not get to conclusion after that I thought http response will have size limit and used reader to access the json from url by following way.
val client = OkHttpClient()
val request = Request.Builder().url("********")
.addHeader("Content-Type", "application/json")
.build()
val response = client.newCall(request).execute()
val input = response.body()?.byteStream()
val reader = BufferedReader(InputStreamReader(input))
But as this https://stackoverflow.com/a/26023885/7639056 answer state that we cannot configure the OkHttpClient to read more than 2048 bytes from the buffer
So is there any way I can get all the data at once?
Eventually I figured out that why this is happening.
There were no problem with the HTTP request. But only a part of it was being printed in logcat due to it's limited buffer size.
There is a size limit of 1024 bytes for binary logs. The size limit for non-binary logs are as shown below.
#define LOGGER_ENTRY_MAX_LEN (4*1024)
#define LOGGER_ENTRY_MAX_PAYLOAD (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))
To set the limit to a larger number, Preferences/Settings -> Editor -> General -> Console, check the box next to Override console cycle buffer size, and enter the number.
I am feeling so embarrassed, but thank you for your support.

OkHttp Websockets - Add a body when connecting to websocket

I am using the okHttp websocket library and I am successfully connecting to my websocket server, but currently I am only getting the connection id when connected. I want to send some extra info in the body, but I don't know how to add it using okHttp
Request request = new Request.Builder()
.url("wss://mywebsocketurl.com")
.build();
I have tried
RequestBody requestBody = new FormBody.Builder()
.add("camera_id", "e9502c54-927c-4639-a94f-8d03149c9c62")
.build();
Request request = new Request.Builder()
.url("wss://mywebsocketurl.com")
.method("POST", requestBody)
.build();
Request request = new Request.Builder()
.url("wss://mywebsocketurl.com")
.post(requestBody)
.build();
But it keeps returning
java.lang.IllegalArgumentException: method GET must not have a request body.
I know it's too late to answer.
The url should anyways be GET only. So i made the connection and on onOpen callback function, i send the body. It worked.
private fun initializeSocket(url: String) {
val request: Request = Request.Builder().url(url).build()
val listener = WebSocketListener()
webSocket = client!!.newWebSocket(request, listener)
client?.dispatcher()?.executorService()?.shutdown()
}
private class WebSocketListener : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response?) {
val content = "{\n" +
" \"messageType\": \"INIT_CONNECTION\"\n" +
"}"
webSocket.send(content)
}
...
}

Use cached response only. Without network call

There is an API that has calls limit, in this case I want to cache response and don't run network response if cache is still valid.
First of all I have cache interceptor
fun provideCacheInterceptor(): Interceptor = Interceptor { chain ->
val response = chain.proceed(chain.request())
val cacheControl = CacheControl.Builder()
.maxAge(6, TimeUnit.HOURS)
.maxStale(6, TimeUnit.HOURS)
.onlyIfCached()
.build()
response.newBuilder()
.header("CacheControl", cacheControl.toString())
.build()
}
I attach cache and interceptor to the client
client = OkHttpClient().newBuilder()
.cache(cache)
.addInterceptor(loggingInterceptor)
.addInterceptor(cacheInterceptor)
As I result when I'm trying to check if the reponse from cache and/or from network
Log.e("!##", "cached: ${it.raw().cacheResponse()?.toString()}")
Log.e("!##", "network: ${it.raw().networkResponse()?.toString()}")
I get
cached: Response{protocol=http/1.1, code=200, message=, url=https://API}
network: Response{protocol=h2, code=200, message=, url=https://API}
Is there any way not to call the network endpoint if cache is still valid?
Problem in my implementation that I proceed the original request and apply header to response (my bad), need to modify initial request and proceed modified request then.
Also seems like Cache-Control header typo.
The proper cache interceptor looks like
fun provideCacheInterceptor(networkManager: NetworkManager): Interceptor = Interceptor { chain ->
val request = chain.request()
val cacheControl = CacheControl.Builder()
.maxAge(6, TimeUnit.HOURS)
.maxStale(6, TimeUnit.HOURS)
.build()
chain.proceed(request.newBuilder()
.header("Cache-Control", cacheControl.toString())
.build())
}

Categories

Resources