Things to forget when intercepting a request with Android's WebView # shouldInterceptRequest

When you want to implement HTTP response handling and timeout independently in Android WebView, you may override shouldInterceptRequest and implement HTTP communication independently.

Since it is a sample, it is a rough implementation, but let's say that it is implemented as follows.

override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? {
    val latch = CountDownLatch(1)
    var res: InputStream? = null

    val call = createOkHttpClient().newCall(Request.Builder().url(request?.url.toString()).method("POST", RequestBody.create(null, "hoge")).build())
    call.enqueue(object: Callback {
        override fun onFailure(call: Call, e: IOException) {
            latch.countDown()
        }

        override fun onResponse(call: Call, response: Response) {
            res = response.body()?.byteStream()
            latch.countDown()
        }
    })

    latch.await()
    return WebResourceResponse("text/html", "UTF-8",res)
}

private val cookieStore = HashMap<String, MutableList<Cookie>>()

fun createOkHttpClient(): OkHttpClient {
    return OkHttpClient.Builder()
        .cookieJar(object: CookieJar {
            override fun saveFromResponse(url: HttpUrl, cookies: MutableList<Cookie>) {
                cookieStore[url.host()] = cookies
        }

            override fun loadForRequest(url: HttpUrl): MutableList<Cookie> {
                val cookies = cookieStore[url.host()]
                return cookies ?: ArrayList()
            }
        })
        .build()
}

But be careful when using this. For example, consider the case of getting the following HTML.

<html>                                                                                                                                                                                                                                        
<body>
<script type="text/javascript">
  function doPost() {
    document.TestForm.submit();
  }
</script>

<h1>Test</h1>

<form id="TestForm" name="TestForm" action="http://192.168.100.2:3000/hoge" method="post">
  <input type="hidden" name="hoge" value="hogeVal"/>
  <input type="hidden" name="fuga" value="fugaVal"/>
  <input type="submit" value="submit">
</form>
<script type="text/javascript">
  doPost();
</script>
</body>
</html>

Since WebView has an HTML parsing function, the doPost function is called when reading the above HTML, and for the `` `http://192.168.100.2:3000/hoge``` defined in the

tag. And automatically send the request.

At this time, if WebView is responsible for loading resources with shouldInterceptRequest (unless you are overriding shouldInterceptRequest), The value of the hidden attribute of the \ <input > tag is automatically POSTed, but when the developer implements the request by OkHTTP etc., he / she must analyze the HTML content and add it by himself / herself. not. (By default, the value of hidden attribute is not given)

Let's set up the following server and look at the log.

{-# LANGUAGE OverloadedStrings #-}                                                                                                                                                                                                            
module Main where

import Web.Scotty
import Control.Monad.IO.Class (liftIO)

main :: IO ()
main = scotty 3000 $ do
  post "/test.html" $ file "./static/test.html" >> setHeader "Content-Type" "text/html"
  post "/hoge" $ do
    (liftIO . putStrLn $ ("Access to /hoge. Headers: " :: String)) >> headers >>= liftIO . print
    (liftIO . putStrLn $ ("Params: " :: String)) >> params >>= liftIO . print
    text "hoge success"

If you do not override shouldInterceptRequest


Access to /hoge. Headers: 
[("Host","192.168.100.151:3000"),("Connection","keep-alive"),("Content-Length","25"),("Cache-Control","max-age=0"),("Origin","http://192.168.100.151:3000"),("Upgrade-Insecure-Requests","1"),("Content-Type","application/x-www-form-urlencoded"),("User-Agent","Mozilla/5.0 (Linux; Android 9; Android SDK built for x86 Build/PSR1.180720.012; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.158 Mobile Safari/537.36"),("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"),("Referer","http://192.168.100.151:3000/test.html"),("Accept-Encoding","gzip, deflate"),("Accept-Language","ja-JP,en-US;q=0.9"),("X-Requested-With","com.badlogic.masaki.webviewjsinterfacesample")]
Params: 
[("hoge","hogeVal"),("fuga","fugaVal")]

When shouldIntercept is overridden in the above implementation


Access to /hoge. Headers: 
[("Content-Length","4"),("Host","192.168.100.151:3000"),("Connection","Keep-Alive"),("Accept-Encoding","gzip"),("User-Agent","okhttp/3.10.0")]
Params: 
[]

The content defined in the hidden attribute of HTML is not POSTed. This will prevent the server from returning the intended response. In addition, header information such as User-Agent will also be replaced, so be careful about this when overriding shouldInterceptRequest.

HTML parsing is required to POST the value of the hidden attribute. Various information is already available around here.

The method of acquiring the HTML contents was helpful as follows. https://stackoverflow.com/questions/8200945/how-to-get-html-content-from-a-webview

How to set POST parameters of application / x-www-form-urlencoded with OkHttp It was helpful. https://stackoverflow.com/questions/35756264/how-do-i-post-data-using-okhttp-library-with-content-type-x-www-form-urlencoded

I said the same thing in Previous article, but since the purpose of WebView is HTML rendering, it is not like using it for WebAPI calls. ..

Recommended Posts

Things to forget when intercepting a request with Android's WebView # shouldInterceptRequest
Things to watch out for when creating a framework
Things to keep in mind when using Sidekiq with Rails
Things to think about when deciding on a new system architecture
Things to consider when running a specified job using Spring Batch
Send a pull request to GitHub
Throw a PATCH request with HttpURLConnection
Things to keep in mind when using Apache PDFBox® with AWS Lambda
Send a request to the backend after authenticating with Spring Cloud Gateway
Notes on what to do when a WebView ClassNotFoundException occurs in JavaFX 12