This class is no longer needed now that Google provides WebViewAssetLoader (see https://developer.android.com/reference/kotlin/androidx/webkit/WebViewAssetLoader)
/* | |
MIT License | |
Copyright (c) 2019 Erik Hellman | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all | |
copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
SOFTWARE. | |
*/ | |
@file:Suppress("unused") | |
package se.hellsoft.webviewserver | |
import android.content.res.AssetManager | |
import android.net.Uri | |
import android.webkit.WebResourceRequest | |
import android.webkit.WebResourceResponse | |
import java.io.ByteArrayInputStream | |
import java.io.File | |
import java.io.FileInputStream | |
import java.io.InputStream | |
/** | |
* This class will act as a web server for intercepted request from a `WebView`. Simply implement your own | |
* `WebViewClientCompat` and assign it as the `webViewClient` of your `WebView` and call `handleWebViewRequest()` from | |
* `WebViewClient.shouldInterceptRequest()`. | |
* | |
* Just add your own `RequestHandler` for every response you want to return. Two ready-made implementations | |
* of `RequestHandler` are provided: `FileRequestHandler` and `AssetRequestHandler`. See respective class for details. | |
* | |
* If no matching `RequestHandler` is found, a default 404 response will be returned. | |
*/ | |
class WebViewServer { | |
private val requestHandlers = mutableListOf<RequestHandler>() | |
fun addRequestHandler(requestHandler: RequestHandler) { | |
requestHandlers += requestHandler | |
} | |
fun handleWebViewRequest(webResourceRequest: WebResourceRequest): WebResourceResponse { | |
val request = Request.fromWebResourceRequest(webResourceRequest) | |
for (requestHandler in requestHandlers) { | |
if (requestHandler.shouldHandleRequest(request)) { | |
return requestHandler.handleRequest(request).toWebResourceResponse() | |
} | |
} | |
return NOT_FOUND_RESPONSE.toWebResourceResponse() | |
} | |
companion object { | |
val NOT_FOUND_RESPONSE = Response( | |
404, | |
"Not found", | |
emptyMap(), | |
"text/plain", | |
"UTF-8", | |
ByteArrayInputStream(byteArrayOf()) | |
) | |
} | |
} | |
data class Request(val url: String, val method: String, val headers: Map<String, String>) { | |
companion object { | |
fun fromWebResourceRequest(webResourceRequest: WebResourceRequest): Request { | |
return Request( | |
webResourceRequest.url.toString(), | |
webResourceRequest.method, | |
webResourceRequest.requestHeaders | |
) | |
} | |
} | |
} | |
data class Response( | |
val statusCode: Int, | |
val reasonPhrase: String, | |
val responseHeaders: Map<String, String>, | |
val mimeType: String, | |
val encoding: String, | |
val data: InputStream | |
) { | |
fun toWebResourceResponse(): WebResourceResponse { | |
return WebResourceResponse(mimeType, encoding, statusCode, reasonPhrase, responseHeaders, data) | |
} | |
} | |
interface RequestHandler { | |
fun shouldHandleRequest(request: Request): Boolean | |
fun handleRequest(request: Request): Response | |
} | |
/** | |
* Returns a response containing the content of the specified `File`. Note that the content of the file | |
* is cached in memory, so beware of loading too large files using this class. For very large files, | |
* please provide your own `RequestHandler` that streams the content to the `WebView` instead. | |
*/ | |
open class FileRequestHandler( | |
file: File, | |
private val requestPath: String, | |
private val mimeType: String, | |
private val encoding: String | |
) : RequestHandler { | |
private val bytes: ByteArray | |
init { | |
bytes = FileInputStream(file).use { | |
return@use it.readBytes() | |
} | |
} | |
override fun shouldHandleRequest(request: Request): Boolean { | |
val uri = Uri.parse(requestPath) | |
return uri.path == requestPath | |
} | |
override fun handleRequest(request: Request): Response { | |
return Response(200, "OK", emptyMap(), mimeType, encoding, ByteArrayInputStream(bytes)) | |
} | |
} | |
/** | |
* Returns a response containing the content of the specified Android asset. Note that the content of the file | |
* is cached in memory, so beware of loading too large files using this class. For very large files, | |
* please provide your own `RequestHandler` that streams the content to the `WebView` instead. | |
*/ | |
open class AssetRequestHandler( | |
assetManager: AssetManager, | |
assetPath: String, | |
private val requestPath: String, | |
private val mimeType: String, | |
private val encoding: String | |
) : RequestHandler { | |
private val bytes: ByteArray | |
init { | |
val inputStream = assetManager.open(assetPath) | |
bytes = inputStream.use { | |
return@use it.readBytes() | |
} | |
} | |
override fun shouldHandleRequest(request: Request): Boolean { | |
val uri = Uri.parse(requestPath) | |
return uri.path == requestPath | |
} | |
override fun handleRequest(request: Request): Response { | |
return Response(200, "OK", emptyMap(), mimeType, encoding, ByteArrayInputStream(bytes)) | |
} | |
} |
This comment has been minimized.
This comment has been minimized.
Nice idea! Are there some usages of the web server with |
This comment has been minimized.
This comment has been minimized.
Great! |
This comment has been minimized.
This comment has been minimized.
Hey all! Note that this is no longer needed now that Google provides the WebViewAssetLoader (see https://developer.android.com/reference/kotlin/androidx/webkit/WebViewAssetLoader). |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
Shouldn't the shouldHandleRequest-method compare the private requestPath to the path of the incoming request-parameter (something like "return requestPath == request.url.path") rather than to itself?