How to send json object as post request in CRONET? - java

I am developing android application in which i send data to server using cronet now i want to send data to server in json object but not know how to send in object?
Following is my snippet code for GET method (WORKING).
Can anyone share how to use POST Method in android cronet ?
Dependencies
implementation 'org.chromium.net:cronet-embedded:71.3578.98'
MainActivity
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import org.chromium.net.CronetEngine
import java.util.concurrent.Executors
class MainActivity : AppCompatActivity() {
companion object {
// Web page url
private const val JSON_PLACEHOLDER_API_URL = "https://jsonplaceholder.typicode.com/todos/1"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Build a Cronet engine
val cronetEngine =
CronetEngine.Builder(this)
.enableBrotli(true)
.build()
// Build the request
val request =
cronetEngine.newUrlRequestBuilder(
JSON_PLACEHOLDER_API_URL,
RequestCallback(),
Executors.newSingleThreadExecutor()
).build()
// Start the request
request.start()
}
}
RequestCallback
import android.util.Log
import org.chromium.net.CronetException
import org.chromium.net.UrlRequest
import org.chromium.net.UrlResponseInfo
import java.nio.ByteBuffer
import java.nio.charset.Charset
/**
* Different methods are invoked for different response status
*/
class RequestCallback : UrlRequest.Callback() {
companion object {
// Log cat tag
private val TAG = RequestCallback::class.java.simpleName
}
override fun onResponseStarted(request: UrlRequest?, info: UrlResponseInfo?) {
Log.i(TAG, "Response Started")
val statusCode = info?.httpStatusCode
Log.i(TAG, "Status Code $statusCode")
if (statusCode == 200) {
// Read the buffer
request?.read(ByteBuffer.allocateDirect(32 * 1024))
}
}
override fun onReadCompleted(request: UrlRequest?, info: UrlResponseInfo?, byteBuffer: ByteBuffer?) {
Log.i(TAG, "Response Completed")
// Flip the buffer
byteBuffer?.flip()
// Convert the byte buffer to a string
byteBuffer?.let {
val byteArray = ByteArray(it.remaining())
it.get(byteArray)
String(byteArray, Charset.forName("UTF-8"))
}.apply {
Log.d(TAG, "Response: $this")
}
// Clear the buffer
byteBuffer?.clear()
// Read the buffer
request?.read(byteBuffer)
}
override fun onFailed(request: UrlRequest?, info: UrlResponseInfo?, error: CronetException?) {
Log.e(TAG, "Response Failed: ${error?.message}")
}
override fun onSucceeded(request: UrlRequest?, info: UrlResponseInfo?) {
Log.i(TAG, "Response Succeeded")
}
override fun onRedirectReceived(request: UrlRequest?, info: UrlResponseInfo?, newLocationUrl: String?) {
Log.i(TAG, "Response Redirect to $newLocationUrl")
request?.followRedirect()
}
override fun onCanceled(request: UrlRequest?, info: UrlResponseInfo?) {
super.onCanceled(request, info)
Log.i(TAG, "Response cancelled")
}
}
Output
Response: {
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}

Example:
val myBuilder = CronetEngine.Builder(context)
// Enable caching of HTTP data and
// other information like QUIC server information, HTTP/2 protocol and QUIC protocol.
val cronetEngine: CronetEngine = myBuilder
.enableHttpCache(CronetEngine.Builder.HTTP_CACHE_IN_MEMORY, 100 * 1024.toLong())
.enableHttp2(true)
.enableQuic(true)
.build()
val executor: Executor = Executors.newSingleThreadExecutor()
val requestBuilder = cronetEngine.newUrlRequestBuilder(
"FULL-URL",
MyUrlRequestCallback(),
executor
)
// Content-Type is required, removing it will cause Exception
requestBuilder.addHeader("Content-Type","application/json; charset=UTF-8")
requestBuilder.setHttpMethod("POST")
val myUploadDataProvider = MyUploadDataProvider()
requestBuilder.setUploadDataProvider(myUploadDataProvider,executor)
val request: UrlRequest = requestBuilder.build()
request.start()
MyUploadDataProvider Class:
import android.util.Log
import org.chromium.net.UploadDataProvider
import org.chromium.net.UploadDataSink
import java.lang.Exception
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
private const val TAG = "MyUploadDataProvider"
//TODO replace username and passowrd "_user & _pass"
var string: String ="{\"username\":\"_user\",\"password\":\"_pass\"}"
val charset = StandardCharsets.UTF_8
class MyUploadDataProvider() : UploadDataProvider() {
override fun getLength(): Long {
val size:Long = string.length.toLong()
Log.e(TAG,"Length = "+size)
return size
}
override fun rewind(uploadDataSink: UploadDataSink?) {
Log.e(TAG,"REWIND IS CALLED")
uploadDataSink!!.onRewindSucceeded()
}
override fun read(uploadDataSink: UploadDataSink?, byteBuffer: ByteBuffer?) {
Log.e(TAG,"READ IS CALLED")
byteBuffer!!.put(string.toByteArray(charset))
//byteBuffer.rewind()
//For chunked uploads, true if this is the final read. It must be false for non-chunked uploads.
uploadDataSink!!.onReadSucceeded(false)
}
}

Related

AudioTrack in android

I have application that is send audio track to another client by server this metho is create a buffer from microphone and sent to webSocket in the backend
I want to reverse the method to play the audio in the other client phone
Backend is django and client side is android all i need is a method to convert bytes to audio stream in the other client side something like a phonecall but only one side is streaming audio
method owner : https://blog.canopas.com/android-send-live-audio-stream-from-client-to-server-using-websocket-and-okhttp-client-ecc9f28118d9
import android.Manifest
import android.content.pm.PackageManager
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import okhttp3.*
import okio.ByteString.Companion.toByteString
import java.util.concurrent.TimeUnit
private const val RECORDER_SAMPLERATE = 44100
private val RECORDER_CHANNELS: Int = AudioFormat.CHANNEL_IN_STEREO
private val RECORDER_AUDIO_ENCODING: Int = AudioFormat.ENCODING_PCM_16BIT
private var webSocket: WebSocket? = null
private var audioRecord: AudioRecord? = null
val BUFFER_SIZE_RECORDING = AudioRecord.getMinBufferSize(
RECORDER_SAMPLERATE,
RECORDER_CHANNELS,
RECORDER_AUDIO_ENCODING
) * 4
var client: OkHttpClient? = null
var request: Request = Request.Builder().url("ws://192.168.1.3:8000/ws/chat/15/").build()
class SoundDjango : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sound_django)
client = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.RECORD_AUDIO
) != PackageManager.PERMISSION_GRANTED
) {
return
}
audioRecord = AudioRecord(
MediaRecorder.AudioSource.MIC,
RECORDER_SAMPLERATE, RECORDER_CHANNELS,
RECORDER_AUDIO_ENCODING, BUFFER_SIZE_RECORDING
)
audioRecord?.startRecording()
initWebSocket()
record()
}
fun initWebSocket() {
webSocket = client?.newWebSocket(request, object : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) {
super.onOpen(webSocket, response)
}
override fun onMessage(webSocket: WebSocket, text: String) {
super.onMessage(webSocket, text)
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
super.onFailure(webSocket, t, response)
}
})
}
private fun record()
{
val buf = ByteArray(BUFFER_SIZE_RECORDING)
GlobalScope.launch {
try {
do {
val byteRead = audioRecord?.read(buf,0, buf.size)?: break
if (byteRead < -1)
break
var toSend = buf.toByteString(0, byteRead)
webSocket?.send(buf.toByteString(0, byteRead))
webSocket?.send("{\"message\": \"" + toSend + "\"}")
} while (true)
} catch (e: Exception) {
stop()
}
}
}
fun stop() {
webSocket?.cancel()
audioRecord?.stop()
audioRecord?.release()
audioRecord = null
}
}
Output in backend like this :
{"message": "[size=28416 hex=04001c00fdff130002001c0005001b0000001f00ffff1800fbff1700fcff2200ffff280000002d00fcff2400f4ff1c00f6ff1400f7ff1900f5ff1f00fbff1d00…]"}
{"message": "[size=28416 hex=e5ffdfffe4ffd2ffe4ffd8ffeaffe4fff0ffe5ffebffe3ffe9ffdbffe3ffcdffe8ffccfff5ffd9fff5ffe1ffeaffe8ffe7ffe1ffe9ffd7fff2ffd2fff2ffdbff…]"}
{"message": "[size=28416 hex=12001a00100015000a001f000b001f0009002200080018000d001a0008001b000b0018000e002600110027000c002c000600220009001c000f00250006003000…]"}
{"message": "[size=28416 hex=faff0d00fcff0b00f8ff0e00f9ff1000f9ff0e00faff0e00f8ff1500f6ff0d00f4ff0f00f1ff0f00f2ff0a00f3ff0800f0ff1100f5ff1100f7ff1200f8ff1400…]"}

Retrofit Post on Android kotlin not working but working on postman

I am trying to Hit the API using the Retrofit Post method to put the form data but the problem is that the API returns null all the time even if I try to pass the params. I tested the API by running it in Postman it works fine and I also added internet permission and set usesCleartextTraffic to true under manifest.xml, so it would be really helpful if anyone guides me to rectify this issue.
Output When I tried to debugg
call = Unresolved reference: call
response = Unresolved reference: response
this = {MainActivity#9661}
params = {HashMap#10022} size = 5
"password" -> "Ram#123"
"countryCode" -> {Integer#10042} 91
"name" -> "Ranjith"
"mobile" -> {Long#10046} 99********
"emailID" -> "var***#gmail.com"
Response back from the API with body null all the time
response = {Response#10220} "Response{protocol=http/1.1, code=400, message=Bad Request, url=http://*.***.***.***:***0/api/v1/****/auth/register}"
body = null
errorBody = {ResponseBody$1#10224}
content = {Buffer#10228} "[text={"code":400,"error":true,"msg":"Enter a valid name"}]"
contentLength = 52
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
uploadBt.setOnClickListener {
callApi()
}
}
private fun callApi() {
val params = HashMap<String?, Any?>()
params["name"] = "Ranjith"
params["mobile"] = 99*******
params["emailID"] = "var***#gmail.com"
params["password"] = "Ram#123"
params["countryCode"] = 91
RetrofitClient.instance.registerUser(params)
.enqueue(object : Callback<DefaultResponse>{
override fun onResponse(call: Call<DefaultResponse>, response: Response<DefaultResponse>) {
print(response)
if(response.isSuccessful)
{
Toast.makeText(applicationContext, response.body()?.msg, Toast.LENGTH_SHORT).show()
}else
{
var jsonObject: JSONObject? = null
try {
jsonObject = JSONObject(response.errorBody()!!.string())
val code = jsonObject.getString("code")
val error = jsonObject.getString("error")
val message = jsonObject.getString("msg")
Toast.makeText(applicationContext, message+"\n"+code+"\n"+error, Toast.LENGTH_LONG).show()
} catch (e: JSONException) {
Toast.makeText(applicationContext, e.toString(), Toast.LENGTH_LONG).show()
e.printStackTrace()
}
}
}
override fun onFailure(call: Call<DefaultResponse>, t: Throwable) {
Toast.makeText(applicationContext, t.toString(), Toast.LENGTH_SHORT).show()
}
})
}
}
Interface Api
interface Api {
#FormUrlEncoded
#POST("/api/v1/******/****/register")
fun registerUser(
#FieldMap params: HashMap<String?, Any?>
): Call<DefaultResponse>
}
RetrofitClient.kt object class
object RetrofitClient {
private const val BASE_URL = "http://3.1**.**5.1*1:****"
private val okHttpClient = OkHttpClient.Builder()
.addInterceptor { chain ->
val original = chain.request()
val requestBuilder = original.newBuilder()
.method(original.method(), original.body())
val request = requestBuilder.build()
chain.proceed(request)
}.build()
val instance: Api by lazy{
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()
retrofit.create(Api::class.java)
}
}
DefaultResponse.kt model data class
data class DefaultResponse(
#SerializedName("code")
val code: Int,
#SerializedName("err")
val error: Boolean,
#SerializedName("msg")
val msg: String
)
Through Postman

Retrofit 2 request doesn't give me data

I'm trying to send a GET request through Retrofit 2.
However, the request doesn't do anything..
API SERVICE
package com.example.brews.network
import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import kotlinx.coroutines.Deferred
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
import retrofit2.http.Query
/*
This is the sandbox base url (way less data than production environment)
When deploying app -> use production base url
*/
private const val BASE_URL = "https://sandbox-api.brewerydb.com/v2/"
/**
* Build the Moshi object that Retrofit will be using, making sure to add the Kotlin adapter for
* full Kotlin compatibility.
*/
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
/**
* Use the Retrofit builder to build a retrofit object using a Moshi converter with our Moshi
* object.
*/
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.baseUrl(BASE_URL)
.build()
/**
* A public interface that exposes the [getProperties] method
*/
interface BreweryApiService {
/**
* Returns a Coroutine [Deferred] [List] of [BeerProperty] which can be fetched with await() if
* in a Coroutine scope.
* The #GET annotation indicates that the "beers" endpoint will be requested with the GET
* HTTP method
*/
#GET("beers/")
fun getProperties(#Query("?key") key: String):
// The Coroutine Call Adapter allows us to return a Deferred, a Job with a result
Deferred<List<BeerProperty>>
}
/**
* A public Api object that exposes the lazy-initialized Retrofit service
*/
object BreweryApi {
val retrofitService: BreweryApiService by lazy { retrofit.create(BreweryApiService::class.java) }
}
DATA CLASS
package com.example.brews.network
data class BeerProperty(
val id: Int,
val name: String
)
Method that fills up my list
private fun getBeersProperties() {
coroutineScope.launch {
var getPropertiesDeferred =
BreweryApi.retrofitService.getProperties("13e9caaf80adac04dce90ef55600d898")
try {
_status.value = BreweryApiStatus.LOADING
val listResult = getPropertiesDeferred.await()
_status.value = BreweryApiStatus.DONE
_properties.value = listResult
} catch (e: Exception) {
_status.value = BreweryApiStatus.ERROR
_properties.value = ArrayList()
}
}
}
The JSON retrieved by the link
{
"currentPage": 1,
"numberOfPages": 23,
"totalResults": 1109,
"data": [
{
"id": "c4f2KE",
"name": "'Murican Pilsner",
"nameDisplay": "'Murican Pilsner",
"abv": "5.5",
"glasswareId": 4,
"styleId": 98,
"isOrganic": "N",
"isRetired": "N"
}
]
}
What I need to retrieve is the 'ID' and 'Name' inside the 'Data'. However, this is in an array and I don't know how to extract it with retrofit..
You need to have a DAO object to get the JSON response from retrofit and then parse the JSON object to get the result you want.
So create a DAO object like this:-
data class BeerResponse(
val data: List<BeerProperty>?
)
And change your service method to:-
#GET("beers/")
fun getProperties(#Query("?key") key: String):
// The Coroutine Call Adapter allows us to return a Deferred, a Job with a result
Deferred<BeerResponse>
Then in your getBeerProperties method, change it to this:-
private fun getBeersProperties() {
coroutineScope.launch {
var getPropertiesDeferred =
BreweryApi.retrofitService.getProperties("13e9caaf80adac04dce90ef55600d898")
try {
_status.value = BreweryApiStatus.LOADING
val listResult = getPropertiesDeferred.await()
listResult.data?.let {
_status.value = BreweryApiStatus.DONE
_properties.value = it
} ?: let {
_status.value = BreweryApiStatus.ERROR
_properties.value = ArrayList()
}
} catch (e: Exception) {
_status.value = BreweryApiStatus.ERROR
_properties.value = ArrayList()
}
}
your implementation of the network API is wrong and should change to this
data class BeerProperty(
val id: Int,
val name: String
)
data class Response(
val data: List<BeerProperty>?
)
interface BreweryApiService {
#GET("beers/")
fun getProperties(#Query("?key") key: String):
Deferred<Response>
}
you could also put the number of pages, current page and ... in the Response class
also, you can use JSON to kotlin class plugin in android studio to make these data classes faster and with fewer mistakes, you can also use websites like this to see the JSON in prety format which is more readable

Callback get response Sync kotlin

I'm tring to get the response of a callback in sync mode because I the value of the response is needed to all application to work, without this value ( token ) I can't continue to use the application.
This is my companion object inside the retrofit interface. I need to set the token before creation of a client.
What I'm doing wrong?
EDIT :
I put as this Logs as you write :
companion object {
private var mToken: String = ""
fun create(activity: Activity): MyPhoneInterface {
Log.d("tokenMyPhoneInterface", activity.localClassName)
getToken(activity)
Log.d("tokenMyPhoneInterface", "client token $mToken")
val client = OkHttpClient.Builder()
.addInterceptor { chain ->
val request = chain.request().newBuilder()
.addHeader("Authorization", mToken).build()
chain.proceed(request)
}.build()
val retrofit = Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.baseUrl(BuildConfig.API_HOST)
.build()
return retrofit.create(MyPhoneInterface::class.java)
}
private fun getToken(activity: Activity) {
if(!activity.isFinishing && isJwtExpired(mToken)){
val latch = CountDownLatch(1)
(LoginManager(activity)).getToken(true, object : ServiceCallback<String> {
override fun success(token: String) {
Log.d("tokenMyPhoneInterface", "token $token")
mToken = token
latch.countDown()
}
override fun failure(serviceError: ServiceError) {
Log.d("tokenMyPhoneInterface", serviceError.errorMessage)
latch.countDown()
}
})
Log.d("tokenMyPhoneInterface", "before await ")
latch.await()
Log.d("tokenMyPhoneInterface", "after await")
}
}
}
But I the system is blocked in the latch.await() and the logs is :
05-14 18:19:00.127 848-848/com.italy.myphone D/tokenMyPhoneInterface: view.splash.activity.Splash
05-14 18:19:00.131 848-848/com.italy.myphone D/tokenMyPhoneInterface: before await
EDIT answer2:
sealed class TokenResult {
class Success(val token: String) : TokenResult()
class Error(val serviceError: ServiceError) : TokenResult()}
suspend fun getToken(activity: Activity): TokenResult {
return suspendCancellableCoroutine { continuation ->
(LoginManager(activity)).getToken(true, object : ServiceCallback<String> {
override fun success(token: String) {
continuation.resume(TokenResult.Success(token))
}
override fun failure(serviceError: ServiceError) {
continuation.resume(TokenResult.Error(serviceError))
}
})
}}
And this is how to I try to call the suspend function :
companion object {
fun create(activity: Activity): MyPhoneInterface{
Log.d("tokenMyPhoneInterface", activity.localClassName)
var token = runBlocking {
return#runBlocking getToken(activity)
}
Log.d("tokenMyPhoneInterface", "obtained token")
Log.d("tokenMyPhoneInterface", "client token $tokenResult")
val client = OkHttpClient.Builder()
.addInterceptor { chain ->
val request = chain.request().newBuilder()
.addHeader("Authorization", "").build()
chain.proceed(request)
}.build()
val retrofit = Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.baseUrl(BuildConfig.API_HOST)
.build()
return retrofit.create(MyPhoneInterface::class.java)
}
}
That is inside an interface and this is the code that I use to call the interface/companion object in the activity :
private val mMyPhoneInterface by lazy {
MyPhoneInterface.create(activity)
}
The best way I know to get the response of a callback in sync mode is using
Coroutines and the function suspendCancellableCoroutine
In your case you can have this function:
suspend fun getToken(activity: Activity): TokenResult {
return suspendCancellableCoroutine { continuation ->
(LoginManager(activity)).getToken(true, object : ServiceCallback<String> {
override fun success(token: String) {
continuation.resume(TokenResult.Success(token))
}
override fun failure(serviceError: ServiceError) {
continuation.resume(TokenResult.Error(serviceError))
}
})
}
}
sealed class TokenResult {
class Success(val token: String) : TokenResult()
class Error(val serviceError: ServiceError) : TokenResult()
}
And in your activity.onCreate:
override fun onCreate(savedInstanceState: Bundle?) = runBlocking {
super.onCreate(savedInstanceState)
val tokenResult = getToken(this)
if(tokenResult is Error){
finish()
return#runBlocking
}
//create client here with tokenResult.token value
}
Give it a try and let me know...
EDIT: In the example I use runBlocking because getToken is a suspend function. In your own code you should handle this logic outside the activity.
EDIT:
To eneable coroutines in your project add the following lines in your gradle file:
dependencies {
//other dependencies
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.22.5"
}
kotlin {
experimental {
coroutines "enable"
}
}
At the end I use JavaRx in order to sync a callback.. this is the snippet.
fun getToken(loginManager: LoginManager): String {
return Single
.create(SingleOnSubscribe<String> { emitter ->
loginManager.getToken(object : TokenSimpleCallback {
override fun onSuccess(token: String) {
emitter.onSuccess(token)
}
override fun onFailure(loginServiceError: LoginServiceError) {
emitter.onError(Throwable(loginServiceError.toString()))
}
})
}).blockingGet()}
You shouldn't use latch.count. It is equals to 1 and latch.count > 1 is false. Then your function is returned. Just use latch.await() and it will await for one of callbacks.
Sorry if I am wrong, I don't have enough repo to comment.

Jsoup http logging

Is there a way to log the http request and response?
Let's assume the below request
Connection.Response res = Jsoup.connect("LOGIN_URL_HERE")
.data("user", "USER", "pass", "PASS")
.method(Connection.Method.POST)
.execute();
How can I log the http request and response? Please mind that I want the HTTP and not just the HTML that will be parsed.
By default jsoup uses a implementation of java.net.HttpURLConnection So I suppose you need to turn on logging for that implementation (probably: sun.net.www.protocol.http.HttpURLConnection) or for java.net.
There is a system property that will enable logging for java net utils
-Djavax.net.debug=all
As Jsoup lacks logging (version I'm using: 1.12.1) and using the -Djavax.net.debug=all JVM argument logs are too verbose, the best way I found is to decorate the HttpConnection class, so one can customize what is logged. To achive this, the execute method call needs to be surrounded by logging the properties of the Connection.Request and Connection.Response.
Sample implementation using SLF4J:
import org.jsoup.Connection;
import org.jsoup.helper.HttpConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class DiagnosticConnection extends HttpConnection {
static final Logger LOG = LoggerFactory.getLogger(DiagnosticConnection.class);
#Override
public Connection.Response execute() throws IOException {
log(this.request());
Connection.Response response = super.execute();
log(response);
return response;
}
public static Connection connect(String url) {
Connection connection = new DiagnosticConnection();
connection.url(url);
return connection;
}
private static void log(Connection.Request request) {
LOG.info("========================================");
LOG.info("[url] {}", request.url());
LOG.info("== REQUEST ==");
logBase(request);
LOG.info("[method] {}", request.method());
LOG.info("[data] {}", request.data());
LOG.info("[request body] {}", request.requestBody());
}
private static void log(Connection.Response response) {
LOG.info("== RESPONSE ==");
logBase(response);
LOG.info("[code] {}", response.statusCode());
LOG.info("[status msg] {}", response.statusMessage());
LOG.info("[body] {}", response.body());
LOG.info("========================================");
}
private static void logBase(Connection.Base<?> base) {
LOG.info("[headers] {}", base.headers());
LOG.info("[cookies] {}", base.cookies());
}
}
When using the decorator, instead of Jsoup.connect(<URL>) you should use DiagnosticConnection.connect(<URL>)
Based on Gergely Toth response, I have created my own LoggerHttpConnection and I'm working with that.
import android.util.Log
import org.jsoup.Connection
import org.jsoup.helper.HttpConnection
import org.jsoup.nodes.Document
import org.jsoup.parser.Parser
import java.io.InputStream
import java.net.Proxy
import java.net.URL
import javax.net.ssl.SSLSocketFactory
class LoggerHttpConnection private constructor(
private val delegate: HttpConnection,
private val saveFile: Boolean
) : Connection {
private val tag = "LoggerHttpConnection"
companion object {
fun connect(url: String, saveFile: Boolean = false): LoggerHttpConnection {
return LoggerHttpConnection(
HttpConnection.connect(url) as HttpConnection,
saveFile
)
}
}
private fun log(request: Connection.Request): String {
Log.i(tag, "========================================")
var line = "[url] ${request.url()}"
var log = "$line\n\n== REQUEST ==\n"
Log.i(tag, line)
Log.i(tag, "== REQUEST ==")
log += logBase(request)
line = "[method] ${request.method()}"
log += "$line\n"
Log.i(tag, line)
for (data in request.data()) {
line = "[data] ${data.key()}=${data.value()}"
log += "$line\n"
Log.i(tag, line)
}
line = "[request body] ${request.requestBody()}"
log += "$line\n"
Log.i(tag, line)
return log
}
private fun log(response: Connection.Response): String {
var line = ""
var log = "\n== RESPONSE ==\n"
Log.i(tag, "== RESPONSE ==")
log += logBase(response)
line = "[code] ${response.statusCode()}"
log += "$line\n"
Log.i(tag, line)
line = "[status msg] ${response.statusMessage()}"
log += "$line\n"
Log.i(tag, line)
line = "[body] ${response.body()}"
log += "$line\n"
Log.i(tag, line)
Log.i(tag, "========================================")
return log
}
private fun logBase(base: Connection.Base<*>): String {
var line = ""
var log = ""
for (header in base.headers()) {
line = "[header] ${header.key}=${header.value}"
log += "$line\n"
Log.i(tag, line)
}
for (cookie in base.cookies()) {
line = "[cookie] ${cookie.key}: ${cookie.value}"
log += "$line\n"
Log.i(tag, line)
}
return log
}
override fun execute(): Connection.Response {
var logs = log(request())
val response = delegate.execute()
logs += log(response)
if (saveFile)
logs.saveToFile("request_log") //do something to save your log in a file if its necesary
return response
}
override fun ignoreContentType(ignoreContentType: Boolean): Connection {
delegate.ignoreContentType(ignoreContentType)
return this
}
override fun postDataCharset(charset: String?): Connection {
delegate.postDataCharset(charset)
return this
}
override fun get(): Document {
return delegate.get()
}
override fun post(): Document {
return delegate.post()
}
/** Continue implementing necessary methods for Connection */
}
Now just declare your request using LoggerHttpConnection instead Jsoup and everything will work
Connection.Response res = LoggerHttpConnection.connect("LOGIN_URL_HERE")
.data("user", "USER", "pass", "PASS")
.method(Connection.Method.POST)
.execute();

Categories

Resources