1
0
mirror of https://github.com/square/okhttp.git synced 2025-08-07 12:42:57 +03:00

Okio Filesystem (#6500)

* Testing okio

* Working tests

* Working tests

* Working tests

* Working tests

* okio 3

* Fix dependencies

* File system

* Cleanup

* Cleanup

* Cleanup

* Cache fixes

* Cache fixes

* Review comments

* Cleanup

* Cleanup

* Build fixes
This commit is contained in:
Yuri Schimke
2021-02-06 13:49:05 +00:00
committed by GitHub
parent c667a56468
commit 6e8aa12dd6
18 changed files with 710 additions and 1092 deletions

View File

@@ -1,116 +0,0 @@
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.io
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import java.util.IdentityHashMap
import okio.Buffer
import okio.ForwardingSink
import okio.ForwardingSource
import okio.Sink
import okio.Source
import okhttp3.TestUtil.isDescendentOf
import org.junit.jupiter.api.extension.AfterEachCallback
import org.junit.jupiter.api.extension.ExtensionContext
/** A simple file system where all files are held in memory. Not safe for concurrent use. */
class InMemoryFileSystem : FileSystem, AfterEachCallback {
val files = mutableMapOf<File, Buffer>()
private val openSources = IdentityHashMap<Source, File>()
private val openSinks = IdentityHashMap<Sink, File>()
override fun afterEach(context: ExtensionContext?) {
ensureResourcesClosed()
}
fun ensureResourcesClosed() {
val openResources = mutableListOf<String>()
for (file in openSources.values) {
openResources.add("Source for $file")
}
for (file in openSinks.values) {
openResources.add("Sink for $file")
}
check(openResources.isEmpty()) {
"Resources acquired but not closed:\n * ${openResources.joinToString(separator = "\n * ")}"
}
}
@Throws(FileNotFoundException::class)
override fun source(file: File): Source {
val result = files[file] ?: throw FileNotFoundException()
val source: Source = result.clone()
openSources[source] = file
return object : ForwardingSource(source) {
override fun close() {
openSources.remove(source)
super.close()
}
}
}
@Throws(FileNotFoundException::class)
override fun sink(file: File) = sink(file, false)
@Throws(FileNotFoundException::class)
override fun appendingSink(file: File) = sink(file, true)
private fun sink(file: File, appending: Boolean): Sink {
var result: Buffer? = null
if (appending) {
result = files[file]
}
if (result == null) {
result = Buffer()
}
files[file] = result
val sink: Sink = result
openSinks[sink] = file
return object : ForwardingSink(sink) {
override fun close() {
openSinks.remove(sink)
super.close()
}
}
}
@Throws(IOException::class)
override fun delete(file: File) {
files.remove(file)
}
override fun exists(file: File) = files.containsKey(file)
override fun size(file: File) = files[file]?.size ?: 0L
@Throws(IOException::class)
override fun rename(from: File, to: File) {
files[to] = files.remove(from) ?: throw FileNotFoundException()
}
@Throws(IOException::class)
override fun deleteContents(directory: File) {
val i = files.keys.iterator()
while (i.hasNext()) {
val file = i.next()
if (file.isDescendentOf(directory)) i.remove()
}
}
override fun toString() = "InMemoryFileSystem"
}

View File

@@ -1,104 +0,0 @@
/*
* Copyright (C) 2020 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.io
import java.io.File
import java.util.Collections
import okhttp3.TestUtil.isDescendentOf
import okio.ForwardingSink
import okio.ForwardingSource
import okio.IOException
import okio.Sink
import okio.Source
/**
* Emulate Windows file system limitations on any file system. In particular, Windows will throw an
* [IOException] when asked to delete or rename an open file.
*/
class WindowsFileSystem(val delegate: FileSystem) : FileSystem {
/** Guarded by itself. */
private val openFiles = Collections.synchronizedList(mutableListOf<File>())
override fun source(file: File): Source = FileSource(file, delegate.source(file))
override fun sink(file: File): Sink = FileSink(file, delegate.sink(file))
override fun appendingSink(file: File): Sink = FileSink(file, delegate.appendingSink(file))
override fun delete(file: File) {
val fileOpen = file in openFiles
if (fileOpen) throw IOException("file is open $file")
delegate.delete(file)
}
override fun exists(file: File) = delegate.exists(file)
override fun size(file: File) = delegate.size(file)
override fun rename(from: File, to: File) {
val fromOpen = from in openFiles
if (fromOpen) throw IOException("file is open $from")
val toOpen = to in openFiles
if (toOpen) throw IOException("file is open $to")
delegate.rename(from, to)
}
override fun deleteContents(directory: File) {
val openChild = synchronized(openFiles) {
openFiles.firstOrNull { it.isDescendentOf(directory) }
}
if (openChild != null) throw IOException("file is open $openChild")
delegate.deleteContents(directory)
}
private inner class FileSink(val file: File, delegate: Sink) : ForwardingSink(delegate) {
var closed = false
init {
openFiles += file
}
override fun close() {
if (!closed) {
closed = true
val removed = openFiles.remove(file)
check(removed)
}
delegate.close()
}
}
private inner class FileSource(val file: File, delegate: Source) : ForwardingSource(delegate) {
var closed = false
init {
openFiles += file
}
override fun close() {
if (!closed) {
closed = true
val removed = openFiles.remove(file)
check(removed)
}
delegate.close()
}
}
override fun toString() = "$delegate for Windows™"
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2020 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.okio
import okio.ExperimentalFileSystem
import okio.FileSystem
import okio.ForwardingFileSystem
import okio.Path
import okio.Sink
import okio.Source
@OptIn(ExperimentalFileSystem::class)
class LoggingFilesystem(fileSystem: FileSystem) : ForwardingFileSystem(fileSystem) {
fun log(line: String) {
println(line)
}
override fun appendingSink(file: Path): Sink {
log("appendingSink($file)")
return super.appendingSink(file)
}
override fun atomicMove(source: Path, target: Path) {
log("atomicMove($source, $target)")
super.atomicMove(source, target)
}
override fun createDirectory(dir: Path) {
log("createDirectory($dir)")
super.createDirectory(dir)
}
override fun delete(path: Path) {
log("delete($path)")
super.delete(path)
}
override fun sink(path: Path): Sink {
log("sink($path)")
return super.sink(path)
}
override fun source(file: Path): Source {
log("source($file)")
return super.source(file)
}
}