mirror of
https://github.com/square/okhttp.git
synced 2026-01-12 10:23:16 +03:00
Port all common documentations to jvm (#8146)
This commit is contained in:
@@ -32,13 +32,32 @@ import okhttp3.internal.commonOnlyIfCached
|
||||
import okhttp3.internal.commonParse
|
||||
import okhttp3.internal.commonToString
|
||||
|
||||
/**
|
||||
* A Cache-Control header with cache directives from a server or client. These directives set policy
|
||||
* on what responses can be stored, and which requests can be satisfied by those stored responses.
|
||||
*
|
||||
* See [RFC 7234, 5.2](https://tools.ietf.org/html/rfc7234#section-5.2).
|
||||
*/
|
||||
actual class CacheControl internal actual constructor(
|
||||
/**
|
||||
* In a response, this field's name "no-cache" is misleading. It doesn't prevent us from caching
|
||||
* the response; it only means we have to validate the response with the origin server before
|
||||
* returning it. We can do this with a conditional GET.
|
||||
*
|
||||
* In a request, it means do not use a cache to satisfy the request.
|
||||
*/
|
||||
@get:JvmName("noCache") actual val noCache: Boolean,
|
||||
|
||||
/** If true, this response should not be cached. */
|
||||
@get:JvmName("noStore") actual val noStore: Boolean,
|
||||
|
||||
/** The duration past the response's served date that it can be served without validation. */
|
||||
@get:JvmName("maxAgeSeconds") actual val maxAgeSeconds: Int,
|
||||
|
||||
/**
|
||||
* The "s-maxage" directive is the max age for shared caches. Not to be confused with "max-age"
|
||||
* for non-shared caches, As in Firefox and Chrome, this directive is not honored by this cache.
|
||||
*/
|
||||
@get:JvmName("sMaxAgeSeconds") actual val sMaxAgeSeconds: Int,
|
||||
|
||||
actual val isPrivate: Boolean,
|
||||
@@ -50,6 +69,12 @@ actual class CacheControl internal actual constructor(
|
||||
|
||||
@get:JvmName("minFreshSeconds") actual val minFreshSeconds: Int,
|
||||
|
||||
/**
|
||||
* This field's name "only-if-cached" is misleading. It actually means "do not use the network".
|
||||
* It is set by a client who only wants to make a request if it can be fully satisfied by the
|
||||
* cache. Cached responses that would require validation (ie. conditional gets) are not permitted
|
||||
* if this header is set.
|
||||
*/
|
||||
@get:JvmName("onlyIfCached") actual val onlyIfCached: Boolean,
|
||||
|
||||
@get:JvmName("noTransform") actual val noTransform: Boolean,
|
||||
@@ -130,6 +155,7 @@ actual class CacheControl internal actual constructor(
|
||||
|
||||
actual override fun toString(): String = commonToString()
|
||||
|
||||
/** Builds a `Cache-Control` request header. */
|
||||
actual class Builder {
|
||||
internal actual var noCache: Boolean = false
|
||||
internal actual var noStore: Boolean = false
|
||||
@@ -140,16 +166,30 @@ actual class CacheControl internal actual constructor(
|
||||
internal actual var noTransform: Boolean = false
|
||||
internal actual var immutable: Boolean = false
|
||||
|
||||
/** Don't accept an unvalidated cached response. */
|
||||
actual fun noCache() = commonNoCache()
|
||||
|
||||
/** Don't store the server's response in any cache. */
|
||||
actual fun noStore() = commonNoStore()
|
||||
|
||||
/**
|
||||
* Only accept the response if it is in the cache. If the response isn't cached, a `504
|
||||
* Unsatisfiable Request` response will be returned.
|
||||
*/
|
||||
actual fun onlyIfCached() = commonOnlyIfCached()
|
||||
|
||||
/** Don't accept a transformed response. */
|
||||
actual fun noTransform() = commonNoTransform()
|
||||
|
||||
actual fun immutable() = commonImmutable()
|
||||
|
||||
/**
|
||||
* Sets the maximum age of a cached response. If the cache response's age exceeds [maxAge], it
|
||||
* will not be used and a network request will be made.
|
||||
*
|
||||
* @param maxAge a non-negative integer. This is stored and transmitted with [TimeUnit.SECONDS]
|
||||
* precision; finer precision will be lost.
|
||||
*/
|
||||
actual fun maxAge(maxAge: Int, timeUnit: DurationUnit) = commonMaxAge(maxAge, timeUnit)
|
||||
|
||||
actual fun maxStale(maxStale: Int, timeUnit: DurationUnit) = commonMaxStale(maxStale, timeUnit)
|
||||
@@ -200,12 +240,25 @@ actual class CacheControl internal actual constructor(
|
||||
}
|
||||
|
||||
actual companion object {
|
||||
/**
|
||||
* Cache control request directives that require network validation of responses. Note that such
|
||||
* requests may be assisted by the cache via conditional GET requests.
|
||||
*/
|
||||
@JvmField
|
||||
actual val FORCE_NETWORK = commonForceNetwork()
|
||||
|
||||
/**
|
||||
* Cache control request directives that uses the cache only, even if the cached response is
|
||||
* stale. If the response isn't available in the cache or requires server validation, the call
|
||||
* will fail with a `504 Unsatisfiable Request`.
|
||||
*/
|
||||
@JvmField
|
||||
actual val FORCE_CACHE = commonForceCache()
|
||||
|
||||
/**
|
||||
* Returns the cache directives of [headers]. This honors both Cache-Control and Pragma headers
|
||||
* if they are present.
|
||||
*/
|
||||
@JvmStatic
|
||||
actual fun parse(headers: Headers): CacheControl = commonParse(headers)
|
||||
}
|
||||
|
||||
@@ -12,14 +12,18 @@
|
||||
* 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
|
||||
|
||||
import okio.IOException
|
||||
import okio.Timeout
|
||||
|
||||
/**
|
||||
* A call is a request that has been prepared for execution. A call can be canceled. As this object
|
||||
* represents a single request/response pair (stream), it cannot be executed twice.
|
||||
*/
|
||||
actual interface Call : Cloneable {
|
||||
/** Returns the original request that initiated this call. */
|
||||
actual fun request(): Request
|
||||
|
||||
/**
|
||||
@@ -50,10 +54,26 @@ actual interface Call : Cloneable {
|
||||
@Throws(IOException::class)
|
||||
fun execute(): Response
|
||||
|
||||
/**
|
||||
* Schedules the request to be executed at some point in the future.
|
||||
*
|
||||
* The [dispatcher][OkHttpClient.dispatcher] defines when the request will run: usually
|
||||
* immediately unless there are several other requests currently being executed.
|
||||
*
|
||||
* This client will later call back `responseCallback` with either an HTTP response or a failure
|
||||
* exception.
|
||||
*
|
||||
* @throws IllegalStateException when the call has already been executed.
|
||||
*/
|
||||
actual fun enqueue(responseCallback: Callback)
|
||||
|
||||
/** Cancels the request, if possible. Requests that are already complete cannot be canceled. */
|
||||
actual fun cancel()
|
||||
|
||||
/**
|
||||
* Returns true if this call has been either [executed][execute] or [enqueued][enqueue]. It is an
|
||||
* error to execute a call more than once.
|
||||
*/
|
||||
actual fun isExecuted(): Boolean
|
||||
|
||||
actual fun isCanceled(): Boolean
|
||||
@@ -67,6 +87,10 @@ actual interface Call : Cloneable {
|
||||
*/
|
||||
fun timeout(): Timeout
|
||||
|
||||
/**
|
||||
* Create a new, identical call to this one which can be enqueued or executed even if this call
|
||||
* has already been.
|
||||
*/
|
||||
public actual override fun clone(): Call
|
||||
|
||||
actual fun interface Factory {
|
||||
|
||||
@@ -24,13 +24,24 @@ import okhttp3.internal.commonEquals
|
||||
import okhttp3.internal.commonHashCode
|
||||
import okhttp3.internal.commonToString
|
||||
|
||||
/**
|
||||
* An [RFC 7235][rfc_7235] challenge.
|
||||
*
|
||||
* [rfc_7235]: https://tools.ietf.org/html/rfc7235
|
||||
*/
|
||||
actual class Challenge actual constructor(
|
||||
/** Returns the authentication scheme, like `Basic`. */
|
||||
@get:JvmName("scheme") actual val scheme: String,
|
||||
|
||||
authParams: Map<String?, String>
|
||||
) {
|
||||
/**
|
||||
* Returns the auth params, including [realm] and [charset] if present, but as
|
||||
* strings. The map's keys are lowercase and should be treated case-insensitively.
|
||||
*/
|
||||
@get:JvmName("authParams") actual val authParams: Map<String?, String>
|
||||
|
||||
/** Returns the protection space. */
|
||||
@get:JvmName("realm") actual val realm: String?
|
||||
get() = authParams["realm"]
|
||||
|
||||
|
||||
@@ -46,10 +46,27 @@ import okhttp3.internal.http.toHttpDateOrNull
|
||||
import okhttp3.internal.http.toHttpDateString
|
||||
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
|
||||
|
||||
/**
|
||||
* The header fields of a single HTTP message. Values are uninterpreted strings; use `Request` and
|
||||
* `Response` for interpreted headers. This class maintains the order of the header fields within
|
||||
* the HTTP message.
|
||||
*
|
||||
* This class tracks header values line-by-line. A field with multiple comma- separated values on
|
||||
* the same line will be treated as a field with a single value by this class. It is the caller's
|
||||
* responsibility to detect and split on commas if their field permits multiple values. This
|
||||
* simplifies use of single-valued fields whose values routinely contain commas, such as cookies or
|
||||
* dates.
|
||||
*
|
||||
* This class trims whitespace from values. It never returns values with leading or trailing
|
||||
* whitespace.
|
||||
*
|
||||
* Instances of this class are immutable. Use [Builder] to create instances.
|
||||
*/
|
||||
@Suppress("NAME_SHADOWING")
|
||||
actual class Headers internal actual constructor(
|
||||
internal actual val namesAndValues: Array<String>
|
||||
) : Iterable<Pair<String, String>> {
|
||||
/** Returns the last value corresponding to the specified field, or null. */
|
||||
actual operator fun get(name: String): String? = commonHeadersGet(namesAndValues, name)
|
||||
|
||||
/**
|
||||
@@ -67,6 +84,7 @@ actual class Headers internal actual constructor(
|
||||
return getDate(name)?.toInstant()
|
||||
}
|
||||
|
||||
/** Returns the number of field values. */
|
||||
@get:JvmName("size") actual val size: Int
|
||||
get() = namesAndValues.size / 2
|
||||
|
||||
@@ -77,10 +95,13 @@ actual class Headers internal actual constructor(
|
||||
level = DeprecationLevel.ERROR)
|
||||
fun size(): Int = size
|
||||
|
||||
/** Returns the field at `position`. */
|
||||
actual fun name(index: Int): String = commonName(index)
|
||||
|
||||
/** Returns the value at `index`. */
|
||||
actual fun value(index: Int): String = commonValue(index)
|
||||
|
||||
/** Returns an immutable case-insensitive set of header names. */
|
||||
actual fun names(): Set<String> {
|
||||
val result = TreeSet(String.CASE_INSENSITIVE_ORDER)
|
||||
for (i in 0 until size) {
|
||||
@@ -89,6 +110,7 @@ actual class Headers internal actual constructor(
|
||||
return Collections.unmodifiableSet(result)
|
||||
}
|
||||
|
||||
/** Returns an immutable list of the header values for `name`. */
|
||||
actual fun values(name: String): List<String> = commonValues(name)
|
||||
|
||||
/**
|
||||
@@ -112,10 +134,57 @@ actual class Headers internal actual constructor(
|
||||
|
||||
actual fun newBuilder(): Builder = commonNewBuilder()
|
||||
|
||||
/**
|
||||
* Returns true if `other` is a `Headers` object with the same headers, with the same casing, in
|
||||
* the same order. Note that two headers instances may be *semantically* equal but not equal
|
||||
* according to this method. In particular, none of the following sets of headers are equal
|
||||
* according to this method:
|
||||
*
|
||||
* 1. Original
|
||||
* ```
|
||||
* Content-Type: text/html
|
||||
* Content-Length: 50
|
||||
* ```
|
||||
*
|
||||
* 2. Different order
|
||||
*
|
||||
* ```
|
||||
* Content-Length: 50
|
||||
* Content-Type: text/html
|
||||
* ```
|
||||
*
|
||||
* 3. Different case
|
||||
*
|
||||
* ```
|
||||
* content-type: text/html
|
||||
* content-length: 50
|
||||
* ```
|
||||
*
|
||||
* 4. Different values
|
||||
*
|
||||
* ```
|
||||
* Content-Type: text/html
|
||||
* Content-Length: 050
|
||||
* ```
|
||||
*
|
||||
* Applications that require semantically equal headers should convert them into a canonical form
|
||||
* before comparing them for equality.
|
||||
*/
|
||||
actual override fun equals(other: Any?): Boolean = commonEquals(other)
|
||||
|
||||
override fun hashCode(): Int = commonHashCode()
|
||||
|
||||
/**
|
||||
* Returns header names and values. The names and values are separated by `: ` and each pair is
|
||||
* followed by a newline character `\n`.
|
||||
*
|
||||
* Since OkHttp 5 this redacts these sensitive headers:
|
||||
*
|
||||
* * `Authorization`
|
||||
* * `Cookie`
|
||||
* * `Proxy-Authorization`
|
||||
* * `Set-Cookie`
|
||||
*/
|
||||
actual override fun toString(): String = commonToString()
|
||||
|
||||
fun toMultimap(): Map<String, List<String>> {
|
||||
@@ -164,6 +233,9 @@ actual class Headers internal actual constructor(
|
||||
add(line.substring(0, index).trim(), line.substring(index + 1))
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a header with the specified name and value. Does validation of header names and values.
|
||||
*/
|
||||
actual fun add(name: String, value: String) = commonAdd(name, value)
|
||||
|
||||
/**
|
||||
@@ -175,6 +247,9 @@ actual class Headers internal actual constructor(
|
||||
addLenient(name, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all headers from an existing collection.
|
||||
*/
|
||||
actual fun addAll(headers: Headers) = commonAddAll(headers)
|
||||
|
||||
/**
|
||||
@@ -224,6 +299,10 @@ actual class Headers internal actual constructor(
|
||||
}
|
||||
|
||||
actual companion object {
|
||||
/**
|
||||
* Returns headers for the alternating header names and values. There must be an even number of
|
||||
* arguments, and they must alternate between header names and values.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmName("of")
|
||||
actual fun headersOf(vararg namesAndValues: String): Headers = commonHeadersOf(*namesAndValues)
|
||||
@@ -237,6 +316,7 @@ actual class Headers internal actual constructor(
|
||||
return headersOf(*namesAndValues)
|
||||
}
|
||||
|
||||
/** Returns headers for the header names and values in the [Map]. */
|
||||
@JvmStatic
|
||||
@JvmName("of")
|
||||
actual fun Map<String, String>.toHeaders(): Headers = commonToHeaders()
|
||||
|
||||
@@ -70,21 +70,347 @@ import okhttp3.internal.HttpUrlCommon.canonicalize
|
||||
import okhttp3.internal.canParseAsIpAddress
|
||||
import okhttp3.internal.publicsuffix.PublicSuffixDatabase
|
||||
|
||||
/**
|
||||
* A uniform resource locator (URL) with a scheme of either `http` or `https`. Use this class to
|
||||
* compose and decompose Internet addresses. For example, this code will compose and print a URL for
|
||||
* Google search:
|
||||
*
|
||||
* ```java
|
||||
* HttpUrl url = new HttpUrl.Builder()
|
||||
* .scheme("https")
|
||||
* .host("www.google.com")
|
||||
* .addPathSegment("search")
|
||||
* .addQueryParameter("q", "polar bears")
|
||||
* .build();
|
||||
* System.out.println(url);
|
||||
* ```
|
||||
*
|
||||
* which prints:
|
||||
*
|
||||
* ```
|
||||
* https://www.google.com/search?q=polar%20bears
|
||||
* ```
|
||||
*
|
||||
* As another example, this code prints the human-readable query parameters of a Twitter search:
|
||||
*
|
||||
* ```java
|
||||
* HttpUrl url = HttpUrl.parse("https://twitter.com/search?q=cute%20%23puppies&f=images");
|
||||
* for (int i = 0, size = url.querySize(); i < size; i++) {
|
||||
* System.out.println(url.queryParameterName(i) + ": " + url.queryParameterValue(i));
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* which prints:
|
||||
*
|
||||
* ```
|
||||
* q: cute #puppies
|
||||
* f: images
|
||||
* ```
|
||||
*
|
||||
* In addition to composing URLs from their component parts and decomposing URLs into their
|
||||
* component parts, this class implements relative URL resolution: what address you'd reach by
|
||||
* clicking a relative link on a specified page. For example:
|
||||
*
|
||||
* ```java
|
||||
* HttpUrl base = HttpUrl.parse("https://www.youtube.com/user/WatchTheDaily/videos");
|
||||
* HttpUrl link = base.resolve("../../watch?v=cbP2N1BQdYc");
|
||||
* System.out.println(link);
|
||||
* ```
|
||||
*
|
||||
* which prints:
|
||||
*
|
||||
* ```
|
||||
* https://www.youtube.com/watch?v=cbP2N1BQdYc
|
||||
* ```
|
||||
*
|
||||
* ## What's in a URL?
|
||||
*
|
||||
* A URL has several components.
|
||||
*
|
||||
* ### Scheme
|
||||
*
|
||||
* Sometimes referred to as *protocol*, A URL's scheme describes what mechanism should be used to
|
||||
* retrieve the resource. Although URLs have many schemes (`mailto`, `file`, `ftp`), this class only
|
||||
* supports `http` and `https`. Use [java.net.URI][URI] for URLs with arbitrary schemes.
|
||||
*
|
||||
* ### Username and Password
|
||||
*
|
||||
* Username and password are either present, or the empty string `""` if absent. This class offers
|
||||
* no mechanism to differentiate empty from absent. Neither of these components are popular in
|
||||
* practice. Typically HTTP applications use other mechanisms for user identification and
|
||||
* authentication.
|
||||
*
|
||||
* ### Host
|
||||
*
|
||||
* The host identifies the webserver that serves the URL's resource. It is either a hostname like
|
||||
* `square.com` or `localhost`, an IPv4 address like `192.168.0.1`, or an IPv6 address like `::1`.
|
||||
*
|
||||
* Usually a webserver is reachable with multiple identifiers: its IP addresses, registered
|
||||
* domain names, and even `localhost` when connecting from the server itself. Each of a web server's
|
||||
* names is a distinct URL and they are not interchangeable. For example, even if
|
||||
* `http://square.github.io/dagger` and `http://google.github.io/dagger` are served by the same IP
|
||||
* address, the two URLs identify different resources.
|
||||
*
|
||||
* ### Port
|
||||
*
|
||||
* The port used to connect to the web server. By default this is 80 for HTTP and 443 for HTTPS.
|
||||
* This class never returns -1 for the port: if no port is explicitly specified in the URL then the
|
||||
* scheme's default is used.
|
||||
*
|
||||
* ### Path
|
||||
*
|
||||
* The path identifies a specific resource on the host. Paths have a hierarchical structure like
|
||||
* "/square/okhttp/issues/1486" and decompose into a list of segments like `["square", "okhttp",
|
||||
* "issues", "1486"]`.
|
||||
*
|
||||
* This class offers methods to compose and decompose paths by segment. It composes each path
|
||||
* from a list of segments by alternating between "/" and the encoded segment. For example the
|
||||
* segments `["a", "b"]` build "/a/b" and the segments `["a", "b", ""]` build "/a/b/".
|
||||
*
|
||||
* If a path's last segment is the empty string then the path ends with "/". This class always
|
||||
* builds non-empty paths: if the path is omitted it defaults to "/". The default path's segment
|
||||
* list is a single empty string: `[""]`.
|
||||
*
|
||||
* ### Query
|
||||
*
|
||||
* The query is optional: it can be null, empty, or non-empty. For many HTTP URLs the query string
|
||||
* is subdivided into a collection of name-value parameters. This class offers methods to set the
|
||||
* query as the single string, or as individual name-value parameters. With name-value parameters
|
||||
* the values are optional and names may be repeated.
|
||||
*
|
||||
* ### Fragment
|
||||
*
|
||||
* The fragment is optional: it can be null, empty, or non-empty. Unlike host, port, path, and
|
||||
* query the fragment is not sent to the webserver: it's private to the client.
|
||||
*
|
||||
* ## Encoding
|
||||
*
|
||||
* Each component must be encoded before it is embedded in the complete URL. As we saw above, the
|
||||
* string `cute #puppies` is encoded as `cute%20%23puppies` when used as a query parameter value.
|
||||
*
|
||||
* ### Percent encoding
|
||||
*
|
||||
* Percent encoding replaces a character (like `\ud83c\udf69`) with its UTF-8 hex bytes (like
|
||||
* `%F0%9F%8D%A9`). This approach works for whitespace characters, control characters, non-ASCII
|
||||
* characters, and characters that already have another meaning in a particular context.
|
||||
*
|
||||
* Percent encoding is used in every URL component except for the hostname. But the set of
|
||||
* characters that need to be encoded is different for each component. For example, the path
|
||||
* component must escape all of its `?` characters, otherwise it could be interpreted as the
|
||||
* start of the URL's query. But within the query and fragment components, the `?` character
|
||||
* doesn't delimit anything and doesn't need to be escaped.
|
||||
*
|
||||
* ```java
|
||||
* HttpUrl url = HttpUrl.parse("http://who-let-the-dogs.out").newBuilder()
|
||||
* .addPathSegment("_Who?_")
|
||||
* .query("_Who?_")
|
||||
* .fragment("_Who?_")
|
||||
* .build();
|
||||
* System.out.println(url);
|
||||
* ```
|
||||
*
|
||||
* This prints:
|
||||
*
|
||||
* ```
|
||||
* http://who-let-the-dogs.out/_Who%3F_?_Who?_#_Who?_
|
||||
* ```
|
||||
*
|
||||
* When parsing URLs that lack percent encoding where it is required, this class will percent encode
|
||||
* the offending characters.
|
||||
*
|
||||
* ### IDNA Mapping and Punycode encoding
|
||||
*
|
||||
* Hostnames have different requirements and use a different encoding scheme. It consists of IDNA
|
||||
* mapping and Punycode encoding.
|
||||
*
|
||||
* In order to avoid confusion and discourage phishing attacks, [IDNA Mapping][idna] transforms
|
||||
* names to avoid confusing characters. This includes basic case folding: transforming shouting
|
||||
* `SQUARE.COM` into cool and casual `square.com`. It also handles more exotic characters. For
|
||||
* example, the Unicode trademark sign (™) could be confused for the letters "TM" in
|
||||
* `http://ho™ail.com`. To mitigate this, the single character (™) maps to the string (tm). There
|
||||
* is similar policy for all of the 1.1 million Unicode code points. Note that some code points such
|
||||
* as "\ud83c\udf69" are not mapped and cannot be used in a hostname.
|
||||
*
|
||||
* [Punycode](http://ietf.org/rfc/rfc3492.txt) converts a Unicode string to an ASCII string to make
|
||||
* international domain names work everywhere. For example, "σ" encodes as "xn--4xa". The encoded
|
||||
* string is not human readable, but can be used with classes like [InetAddress] to establish
|
||||
* connections.
|
||||
*
|
||||
* ## Why another URL model?
|
||||
*
|
||||
* Java includes both [java.net.URL][URL] and [java.net.URI][URI]. We offer a new URL
|
||||
* model to address problems that the others don't.
|
||||
*
|
||||
* ### Different URLs should be different
|
||||
*
|
||||
* Although they have different content, `java.net.URL` considers the following two URLs
|
||||
* equal, and the [equals()][Object.equals] method between them returns true:
|
||||
*
|
||||
* * https://example.net/
|
||||
*
|
||||
* * https://example.com/
|
||||
*
|
||||
* This is because those two hosts share the same IP address. This is an old, bad design decision
|
||||
* that makes `java.net.URL` unusable for many things. It shouldn't be used as a [Map] key or in a
|
||||
* [Set]. Doing so is both inefficient because equality may require a DNS lookup, and incorrect
|
||||
* because unequal URLs may be equal because of how they are hosted.
|
||||
*
|
||||
* ### Equal URLs should be equal
|
||||
*
|
||||
* These two URLs are semantically identical, but `java.net.URI` disagrees:
|
||||
*
|
||||
* * http://host:80/
|
||||
*
|
||||
* * http://host
|
||||
*
|
||||
* Both the unnecessary port specification (`:80`) and the absent trailing slash (`/`) cause URI to
|
||||
* bucket the two URLs separately. This harms URI's usefulness in collections. Any application that
|
||||
* stores information-per-URL will need to either canonicalize manually, or suffer unnecessary
|
||||
* redundancy for such URLs.
|
||||
*
|
||||
* Because they don't attempt canonical form, these classes are surprisingly difficult to use
|
||||
* securely. Suppose you're building a webservice that checks that incoming paths are prefixed
|
||||
* "/static/images/" before serving the corresponding assets from the filesystem.
|
||||
*
|
||||
* ```java
|
||||
* String attack = "http://example.com/static/images/../../../../../etc/passwd";
|
||||
* System.out.println(new URL(attack).getPath());
|
||||
* System.out.println(new URI(attack).getPath());
|
||||
* System.out.println(HttpUrl.parse(attack).encodedPath());
|
||||
* ```
|
||||
*
|
||||
* By canonicalizing the input paths, they are complicit in directory traversal attacks. Code that
|
||||
* checks only the path prefix may suffer!
|
||||
*
|
||||
* ```
|
||||
* /static/images/../../../../../etc/passwd
|
||||
* /static/images/../../../../../etc/passwd
|
||||
* /etc/passwd
|
||||
* ```
|
||||
*
|
||||
* ### If it works on the web, it should work in your application
|
||||
*
|
||||
* The `java.net.URI` class is strict around what URLs it accepts. It rejects URLs like
|
||||
* `http://example.com/abc|def` because the `|` character is unsupported. This class is more
|
||||
* forgiving: it will automatically percent-encode the `|'` yielding `http://example.com/abc%7Cdef`.
|
||||
* This kind behavior is consistent with web browsers. `HttpUrl` prefers consistency with major web
|
||||
* browsers over consistency with obsolete specifications.
|
||||
*
|
||||
* ### Paths and Queries should decompose
|
||||
*
|
||||
* Neither of the built-in URL models offer direct access to path segments or query parameters.
|
||||
* Manually using `StringBuilder` to assemble these components is cumbersome: do '+' characters get
|
||||
* silently replaced with spaces? If a query parameter contains a '&', does that get escaped?
|
||||
* By offering methods to read and write individual query parameters directly, application
|
||||
* developers are saved from the hassles of encoding and decoding.
|
||||
*
|
||||
* ### Plus a modern API
|
||||
*
|
||||
* The URL (JDK1.0) and URI (Java 1.4) classes predate builders and instead use telescoping
|
||||
* constructors. For example, there's no API to compose a URI with a custom port without also
|
||||
* providing a query and fragment.
|
||||
*
|
||||
* Instances of [HttpUrl] are well-formed and always have a scheme, host, and path. With
|
||||
* `java.net.URL` it's possible to create an awkward URL like `http:/` with scheme and path but no
|
||||
* hostname. Building APIs that consume such malformed values is difficult!
|
||||
*
|
||||
* This class has a modern API. It avoids punitive checked exceptions: [toHttpUrl] throws
|
||||
* [IllegalArgumentException] on invalid input or [toHttpUrlOrNull] returns null if the input is an
|
||||
* invalid URL. You can even be explicit about whether each component has been encoded already.
|
||||
*
|
||||
* [idna]: http://www.unicode.org/reports/tr46/#ToASCII
|
||||
*/
|
||||
actual class HttpUrl internal actual constructor(
|
||||
/** Either "http" or "https". */
|
||||
@get:JvmName("scheme") actual val scheme: String,
|
||||
|
||||
/**
|
||||
* The decoded username, or an empty string if none is present.
|
||||
*
|
||||
* | URL | `username()` |
|
||||
* | :------------------------------- | :----------- |
|
||||
* | `http://host/` | `""` |
|
||||
* | `http://username@host/` | `"username"` |
|
||||
* | `http://username:password@host/` | `"username"` |
|
||||
* | `http://a%20b:c%20d@host/` | `"a b"` |
|
||||
*/
|
||||
@get:JvmName("username") actual val username: String,
|
||||
|
||||
/**
|
||||
* Returns the decoded password, or an empty string if none is present.
|
||||
*
|
||||
* | URL | `password()` |
|
||||
* | :------------------------------- | :----------- |
|
||||
* | `http://host/` | `""` |
|
||||
* | `http://username@host/` | `""` |
|
||||
* | `http://username:password@host/` | `"password"` |
|
||||
* | `http://a%20b:c%20d@host/` | `"c d"` |
|
||||
*/
|
||||
@get:JvmName("password") actual val password: String,
|
||||
|
||||
/**
|
||||
* The host address suitable for use with [InetAddress.getAllByName]. May be:
|
||||
*
|
||||
* * A regular host name, like `android.com`.
|
||||
*
|
||||
* * An IPv4 address, like `127.0.0.1`.
|
||||
*
|
||||
* * An IPv6 address, like `::1`. Note that there are no square braces.
|
||||
*
|
||||
* * An encoded IDN, like `xn--n3h.net`.
|
||||
*
|
||||
* | URL | `host()` |
|
||||
* | :-------------------- | :-------------- |
|
||||
* | `http://android.com/` | `"android.com"` |
|
||||
* | `http://127.0.0.1/` | `"127.0.0.1"` |
|
||||
* | `http://[::1]/` | `"::1"` |
|
||||
* | `http://xn--n3h.net/` | `"xn--n3h.net"` |
|
||||
*/
|
||||
@get:JvmName("host") actual val host: String,
|
||||
|
||||
/**
|
||||
* The explicitly-specified port if one was provided, or the default port for this URL's scheme.
|
||||
* For example, this returns 8443 for `https://square.com:8443/` and 443 for
|
||||
* `https://square.com/`. The result is in `[1..65535]`.
|
||||
*
|
||||
* | URL | `port()` |
|
||||
* | :------------------ | :------- |
|
||||
* | `http://host/` | `80` |
|
||||
* | `http://host:8000/` | `8000` |
|
||||
* | `https://host/` | `443` |
|
||||
*/
|
||||
@get:JvmName("port") actual val port: Int,
|
||||
|
||||
/**
|
||||
* A list of path segments like `["a", "b", "c"]` for the URL `http://host/a/b/c`. This list is
|
||||
* never empty though it may contain a single empty string.
|
||||
*
|
||||
* | URL | `pathSegments()` |
|
||||
* | :----------------------- | :------------------ |
|
||||
* | `http://host/` | `[""]` |
|
||||
* | `http://host/a/b/c"` | `["a", "b", "c"]` |
|
||||
* | `http://host/a/b%20c/d"` | `["a", "b c", "d"]` |
|
||||
*/
|
||||
@get:JvmName("pathSegments") actual val pathSegments: List<String>,
|
||||
|
||||
/**
|
||||
* Alternating, decoded query names and values, or null for no query. Names may be empty or
|
||||
* non-empty, but never null. Values are null if the name has no corresponding '=' separator, or
|
||||
* empty, or non-empty.
|
||||
*/
|
||||
internal actual val queryNamesAndValues: List<String?>?,
|
||||
|
||||
/**
|
||||
* This URL's fragment, like `"abc"` for `http://host/#abc`. This is null if the URL has no
|
||||
* fragment.
|
||||
*
|
||||
* | URL | `fragment()` |
|
||||
* | :--------------------- | :----------- |
|
||||
* | `http://host/` | null |
|
||||
* | `http://host/#` | `""` |
|
||||
* | `http://host/#abc` | `"abc"` |
|
||||
* | `http://host/#abc|def` | `"abc|def"` |
|
||||
*/
|
||||
@get:JvmName("fragment") actual val fragment: String?,
|
||||
|
||||
/** Canonical URL. */
|
||||
@@ -130,52 +456,230 @@ actual class HttpUrl internal actual constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The username, or an empty string if none is set.
|
||||
*
|
||||
* | URL | `encodedUsername()` |
|
||||
* | :------------------------------- | :------------------ |
|
||||
* | `http://host/` | `""` |
|
||||
* | `http://username@host/` | `"username"` |
|
||||
* | `http://username:password@host/` | `"username"` |
|
||||
* | `http://a%20b:c%20d@host/` | `"a%20b"` |
|
||||
*/
|
||||
@get:JvmName("encodedUsername") actual val encodedUsername: String
|
||||
get() = commonEncodedUsername
|
||||
|
||||
/**
|
||||
* The password, or an empty string if none is set.
|
||||
*
|
||||
* | URL | `encodedPassword()` |
|
||||
* | :--------------------------------| :------------------ |
|
||||
* | `http://host/` | `""` |
|
||||
* | `http://username@host/` | `""` |
|
||||
* | `http://username:password@host/` | `"password"` |
|
||||
* | `http://a%20b:c%20d@host/` | `"c%20d"` |
|
||||
*/
|
||||
@get:JvmName("encodedPassword") actual val encodedPassword: String
|
||||
get() = commonEncodedPassword
|
||||
|
||||
/**
|
||||
* The number of segments in this URL's path. This is also the number of slashes in this URL's
|
||||
* path, like 3 in `http://host/a/b/c`. This is always at least 1.
|
||||
*
|
||||
* | URL | `pathSize()` |
|
||||
* | :------------------- | :----------- |
|
||||
* | `http://host/` | `1` |
|
||||
* | `http://host/a/b/c` | `3` |
|
||||
* | `http://host/a/b/c/` | `4` |
|
||||
*/
|
||||
@get:JvmName("pathSize")
|
||||
actual val pathSize: Int
|
||||
get() = commonPathSize
|
||||
|
||||
/**
|
||||
* The entire path of this URL encoded for use in HTTP resource resolution. The returned path will
|
||||
* start with `"/"`.
|
||||
*
|
||||
* | URL | `encodedPath()` |
|
||||
* | :---------------------- | :-------------- |
|
||||
* | `http://host/` | `"/"` |
|
||||
* | `http://host/a/b/c` | `"/a/b/c"` |
|
||||
* | `http://host/a/b%20c/d` | `"/a/b%20c/d"` |
|
||||
*/
|
||||
@get:JvmName("encodedPath") actual val encodedPath: String
|
||||
get() = commonEncodedPath
|
||||
|
||||
/**
|
||||
* A list of encoded path segments like `["a", "b", "c"]` for the URL `http://host/a/b/c`. This
|
||||
* list is never empty though it may contain a single empty string.
|
||||
*
|
||||
* | URL | `encodedPathSegments()` |
|
||||
* | :---------------------- | :---------------------- |
|
||||
* | `http://host/` | `[""]` |
|
||||
* | `http://host/a/b/c` | `["a", "b", "c"]` |
|
||||
* | `http://host/a/b%20c/d` | `["a", "b%20c", "d"]` |
|
||||
*/
|
||||
@get:JvmName("encodedPathSegments") actual val encodedPathSegments: List<String>
|
||||
get() = commonEncodedPathSegments
|
||||
|
||||
/**
|
||||
* The query of this URL, encoded for use in HTTP resource resolution. This string may be null
|
||||
* (for URLs with no query), empty (for URLs with an empty query) or non-empty (all other URLs).
|
||||
*
|
||||
* | URL | `encodedQuery()` |
|
||||
* | :-------------------------------- | :--------------------- |
|
||||
* | `http://host/` | null |
|
||||
* | `http://host/?` | `""` |
|
||||
* | `http://host/?a=apple&k=key+lime` | `"a=apple&k=key+lime"` |
|
||||
* | `http://host/?a=apple&a=apricot` | `"a=apple&a=apricot"` |
|
||||
* | `http://host/?a=apple&b` | `"a=apple&b"` |
|
||||
*/
|
||||
@get:JvmName("encodedQuery") actual val encodedQuery: String?
|
||||
get() = commonEncodedQuery
|
||||
|
||||
/**
|
||||
* This URL's query, like `"abc"` for `http://host/?abc`. Most callers should prefer
|
||||
* [queryParameterName] and [queryParameterValue] because these methods offer direct access to
|
||||
* individual query parameters.
|
||||
*
|
||||
* | URL | `query()` |
|
||||
* | :-------------------------------- | :--------------------- |
|
||||
* | `http://host/` | null |
|
||||
* | `http://host/?` | `""` |
|
||||
* | `http://host/?a=apple&k=key+lime` | `"a=apple&k=key lime"` |
|
||||
* | `http://host/?a=apple&a=apricot` | `"a=apple&a=apricot"` |
|
||||
* | `http://host/?a=apple&b` | `"a=apple&b"` |
|
||||
*/
|
||||
@get:JvmName("query") actual val query: String?
|
||||
get() = commonQuery
|
||||
|
||||
/**
|
||||
* The number of query parameters in this URL, like 2 for `http://host/?a=apple&b=banana`. If this
|
||||
* URL has no query this is 0. Otherwise it is one more than the number of `"&"` separators in the
|
||||
* query.
|
||||
*
|
||||
* | URL | `querySize()` |
|
||||
* | :-------------------------------- | :------------ |
|
||||
* | `http://host/` | `0` |
|
||||
* | `http://host/?` | `1` |
|
||||
* | `http://host/?a=apple&k=key+lime` | `2` |
|
||||
* | `http://host/?a=apple&a=apricot` | `2` |
|
||||
* | `http://host/?a=apple&b` | `2` |
|
||||
*/
|
||||
@get:JvmName("querySize") actual val querySize: Int
|
||||
get() = commonQuerySize
|
||||
|
||||
/**
|
||||
* The first query parameter named `name` decoded using UTF-8, or null if there is no such query
|
||||
* parameter.
|
||||
*
|
||||
* | URL | `queryParameter("a")` |
|
||||
* | :-------------------------------- | :-------------------- |
|
||||
* | `http://host/` | null |
|
||||
* | `http://host/?` | null |
|
||||
* | `http://host/?a=apple&k=key+lime` | `"apple"` |
|
||||
* | `http://host/?a=apple&a=apricot` | `"apple"` |
|
||||
* | `http://host/?a=apple&b` | `"apple"` |
|
||||
*/
|
||||
actual fun queryParameter(name: String): String? = commonQueryParameter(name)
|
||||
|
||||
/**
|
||||
* The distinct query parameter names in this URL, like `["a", "b"]` for
|
||||
* `http://host/?a=apple&b=banana`. If this URL has no query this is the empty set.
|
||||
*
|
||||
* | URL | `queryParameterNames()` |
|
||||
* | :-------------------------------- | :---------------------- |
|
||||
* | `http://host/` | `[]` |
|
||||
* | `http://host/?` | `[""]` |
|
||||
* | `http://host/?a=apple&k=key+lime` | `["a", "k"]` |
|
||||
* | `http://host/?a=apple&a=apricot` | `["a"]` |
|
||||
* | `http://host/?a=apple&b` | `["a", "b"]` |
|
||||
*/
|
||||
actual @get:JvmName("queryParameterNames") val queryParameterNames: Set<String>
|
||||
get() = commonQueryParameterNames
|
||||
|
||||
/**
|
||||
* Returns all values for the query parameter `name` ordered by their appearance in this
|
||||
* URL. For example this returns `["banana"]` for `queryParameterValue("b")` on
|
||||
* `http://host/?a=apple&b=banana`.
|
||||
*
|
||||
* | URL | `queryParameterValues("a")` | `queryParameterValues("b")` |
|
||||
* | :-------------------------------- | :-------------------------- | :-------------------------- |
|
||||
* | `http://host/` | `[]` | `[]` |
|
||||
* | `http://host/?` | `[]` | `[]` |
|
||||
* | `http://host/?a=apple&k=key+lime` | `["apple"]` | `[]` |
|
||||
* | `http://host/?a=apple&a=apricot` | `["apple", "apricot"]` | `[]` |
|
||||
* | `http://host/?a=apple&b` | `["apple"]` | `[null]` |
|
||||
*/
|
||||
actual fun queryParameterValues(name: String): List<String?> = commonQueryParameterValues(name)
|
||||
|
||||
/**
|
||||
* Returns the name of the query parameter at `index`. For example this returns `"a"`
|
||||
* for `queryParameterName(0)` on `http://host/?a=apple&b=banana`. This throws if
|
||||
* `index` is not less than the [query size][querySize].
|
||||
*
|
||||
* | URL | `queryParameterName(0)` | `queryParameterName(1)` |
|
||||
* | :-------------------------------- | :---------------------- | :---------------------- |
|
||||
* | `http://host/` | exception | exception |
|
||||
* | `http://host/?` | `""` | exception |
|
||||
* | `http://host/?a=apple&k=key+lime` | `"a"` | `"k"` |
|
||||
* | `http://host/?a=apple&a=apricot` | `"a"` | `"a"` |
|
||||
* | `http://host/?a=apple&b` | `"a"` | `"b"` |
|
||||
*/
|
||||
actual fun queryParameterName(index: Int): String = commonQueryParameterName(index)
|
||||
|
||||
/**
|
||||
* Returns the value of the query parameter at `index`. For example this returns `"apple"` for
|
||||
* `queryParameterName(0)` on `http://host/?a=apple&b=banana`. This throws if `index` is not less
|
||||
* than the [query size][querySize].
|
||||
*
|
||||
* | URL | `queryParameterValue(0)` | `queryParameterValue(1)` |
|
||||
* | :-------------------------------- | :----------------------- | :----------------------- |
|
||||
* | `http://host/` | exception | exception |
|
||||
* | `http://host/?` | null | exception |
|
||||
* | `http://host/?a=apple&k=key+lime` | `"apple"` | `"key lime"` |
|
||||
* | `http://host/?a=apple&a=apricot` | `"apple"` | `"apricot"` |
|
||||
* | `http://host/?a=apple&b` | `"apple"` | null |
|
||||
*/
|
||||
actual fun queryParameterValue(index: Int): String? = commonQueryParameterValue(index)
|
||||
|
||||
/**
|
||||
* This URL's encoded fragment, like `"abc"` for `http://host/#abc`. This is null if the URL has
|
||||
* no fragment.
|
||||
*
|
||||
* | URL | `encodedFragment()` |
|
||||
* | :--------------------- | :------------------ |
|
||||
* | `http://host/` | null |
|
||||
* | `http://host/#` | `""` |
|
||||
* | `http://host/#abc` | `"abc"` |
|
||||
* | `http://host/#abc|def` | `"abc|def"` |
|
||||
*/
|
||||
@get:JvmName("encodedFragment")
|
||||
actual val encodedFragment: String?
|
||||
get() = commonEncodedFragment
|
||||
|
||||
/**
|
||||
* Returns a string with containing this URL with its username, password, query, and fragment
|
||||
* stripped, and its path replaced with `/...`. For example, redacting
|
||||
* `http://username:password@example.com/path` returns `http://example.com/...`.
|
||||
*/
|
||||
actual fun redact(): String = commonRedact()
|
||||
|
||||
/**
|
||||
* Returns the URL that would be retrieved by following `link` from this URL, or null if the
|
||||
* resulting URL is not well-formed.
|
||||
*/
|
||||
actual fun resolve(link: String): HttpUrl? = commonResolve(link)
|
||||
|
||||
/**
|
||||
* Returns a builder based on this URL.
|
||||
*/
|
||||
actual fun newBuilder(): Builder = commonNewBuilder()
|
||||
|
||||
/**
|
||||
* Returns a builder for the URL that would be retrieved by following `link` from this URL,
|
||||
* or null if the resulting URL is not well-formed.
|
||||
*/
|
||||
actual fun newBuilder(link: String): Builder? = commonNewBuilder(link)
|
||||
|
||||
override fun equals(other: Any?): Boolean = commonEquals(other)
|
||||
@@ -352,6 +856,9 @@ actual class HttpUrl internal actual constructor(
|
||||
internal actual var encodedQueryNamesAndValues: MutableList<String?>? = null
|
||||
internal actual var encodedFragment: String? = null
|
||||
|
||||
/**
|
||||
* @param scheme either "http" or "https".
|
||||
*/
|
||||
actual fun scheme(scheme: String) = commonScheme(scheme)
|
||||
|
||||
actual fun username(username: String) = commonUsername(username)
|
||||
@@ -362,16 +869,28 @@ actual class HttpUrl internal actual constructor(
|
||||
|
||||
actual fun encodedPassword(encodedPassword: String) = commonEncodedPassword(encodedPassword)
|
||||
|
||||
/**
|
||||
* @param host either a regular hostname, International Domain Name, IPv4 address, or IPv6
|
||||
* address.
|
||||
*/
|
||||
actual fun host(host: String) = commonHost(host)
|
||||
|
||||
actual fun port(port: Int) = commonPort(port)
|
||||
|
||||
actual fun addPathSegment(pathSegment: String) = commonAddPathSegment(pathSegment)
|
||||
|
||||
/**
|
||||
* Adds a set of path segments separated by a slash (either `\` or `/`). If `pathSegments`
|
||||
* starts with a slash, the resulting URL will have empty path segment.
|
||||
*/
|
||||
actual fun addPathSegments(pathSegments: String): Builder = commonAddPathSegments(pathSegments)
|
||||
|
||||
actual fun addEncodedPathSegment(encodedPathSegment: String) = commonAddEncodedPathSegment(encodedPathSegment)
|
||||
|
||||
/**
|
||||
* Adds a set of encoded path segments separated by a slash (either `\` or `/`). If
|
||||
* `encodedPathSegments` starts with a slash, the resulting URL will have empty path segment.
|
||||
*/
|
||||
actual fun addEncodedPathSegments(encodedPathSegments: String): Builder = commonAddEncodedPathSegments(encodedPathSegments)
|
||||
|
||||
actual fun setPathSegment(index: Int, pathSegment: String) = commonSetPathSegment(index, pathSegment)
|
||||
@@ -386,8 +905,10 @@ actual class HttpUrl internal actual constructor(
|
||||
|
||||
actual fun encodedQuery(encodedQuery: String?) = commonEncodedQuery(encodedQuery)
|
||||
|
||||
/** Encodes the query parameter using UTF-8 and adds it to this URL's query string. */
|
||||
actual fun addQueryParameter(name: String, value: String?) = commonAddQueryParameter(name, value)
|
||||
|
||||
/** Adds the pre-encoded query parameter to this URL's query string. */
|
||||
actual fun addEncodedQueryParameter(encodedName: String, encodedValue: String?) = commonAddEncodedQueryParameter(encodedName, encodedValue)
|
||||
|
||||
actual fun setQueryParameter(name: String, value: String?) = commonSetQueryParameter(name, value)
|
||||
@@ -448,9 +969,18 @@ actual class HttpUrl internal actual constructor(
|
||||
@JvmStatic
|
||||
actual fun defaultPort(scheme: String): Int = commonDefaultPort(scheme)
|
||||
|
||||
/**
|
||||
* Returns a new [HttpUrl] representing this.
|
||||
*
|
||||
* @throws IllegalArgumentException If this is not a well-formed HTTP or HTTPS URL.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmName("get") actual fun String.toHttpUrl(): HttpUrl = commonToHttpUrl()
|
||||
|
||||
/**
|
||||
* Returns a new `HttpUrl` representing `url` if it is a well-formed HTTP or HTTPS URL, or null
|
||||
* if it isn't.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmName("parse")
|
||||
actual fun String.toHttpUrlOrNull(): HttpUrl? = commonToHttpUrlOrNull()
|
||||
|
||||
@@ -23,13 +23,26 @@ import okhttp3.internal.commonToMediaType
|
||||
import okhttp3.internal.commonToMediaTypeOrNull
|
||||
import okhttp3.internal.commonToString
|
||||
|
||||
/**
|
||||
* An [RFC 2045][rfc_2045] Media Type, appropriate to describe the content type of an HTTP request
|
||||
* or response body.
|
||||
*
|
||||
* [rfc_2045]: http://tools.ietf.org/html/rfc2045
|
||||
*/
|
||||
actual class MediaType internal actual constructor(
|
||||
internal actual val mediaType: String,
|
||||
|
||||
/**
|
||||
* Returns the high-level media type, such as "text", "image", "audio", "video", or "application".
|
||||
*/
|
||||
@get:JvmName("type") actual val type: String,
|
||||
|
||||
/**
|
||||
* Returns a specific media subtype, such as "plain" or "png", "mpeg", "mp4" or "xml".
|
||||
*/
|
||||
@get:JvmName("subtype") actual val subtype: String,
|
||||
|
||||
/** Alternating parameter names with their values, like `["charset", "utf-8"]`. */
|
||||
internal actual val parameterNamesAndValues: Array<String>
|
||||
) {
|
||||
|
||||
@@ -47,6 +60,10 @@ actual class MediaType internal actual constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parameter [name] of this media type, or null if this media type does not define
|
||||
* such a parameter.
|
||||
*/
|
||||
actual fun parameter(name: String): String? = commonParameter(name)
|
||||
|
||||
@JvmName("-deprecated_type")
|
||||
@@ -65,6 +82,10 @@ actual class MediaType internal actual constructor(
|
||||
)
|
||||
fun subtype(): String = subtype
|
||||
|
||||
/**
|
||||
* Returns the encoded media type, like "text/plain; charset=utf-8", appropriate for use in a
|
||||
* Content-Type header.
|
||||
*/
|
||||
actual override fun toString(): String = commonToString()
|
||||
|
||||
override fun equals(other: Any?): Boolean = commonEquals(other)
|
||||
@@ -72,10 +93,16 @@ actual class MediaType internal actual constructor(
|
||||
override fun hashCode(): Int = commonHashCode()
|
||||
|
||||
actual companion object {
|
||||
/**
|
||||
* Returns a media type for this string.
|
||||
*
|
||||
* @throws IllegalArgumentException if this is not a well-formed media type.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmName("get")
|
||||
actual fun String.toMediaType(): MediaType = commonToMediaType()
|
||||
|
||||
/** Returns a media type for this, or null if this is not a well-formed media type. */
|
||||
@JvmStatic
|
||||
@JvmName("parse")
|
||||
actual fun String.toMediaTypeOrNull(): MediaType? = commonToMediaTypeOrNull()
|
||||
|
||||
@@ -35,6 +35,10 @@ import okhttp3.internal.commonRemoveHeader
|
||||
import okhttp3.internal.commonTag
|
||||
import okhttp3.internal.commonToString
|
||||
|
||||
/**
|
||||
* An HTTP request. Instances of this class are immutable if their [body] is null or itself
|
||||
* immutable.
|
||||
*/
|
||||
actual class Request internal actual constructor(builder: Builder) {
|
||||
@get:JvmName("url")
|
||||
actual val url: HttpUrl = checkNotNull(builder.url) { "url == null" }
|
||||
@@ -85,9 +89,11 @@ actual class Request internal actual constructor(builder: Builder) {
|
||||
|
||||
actual fun headers(name: String): List<String> = commonHeaders(name)
|
||||
|
||||
/** Returns the tag attached with [T] as a key, or null if no tag is attached with that key. */
|
||||
@JvmName("reifiedTag")
|
||||
actual inline fun <reified T : Any> tag(): T? = tag(T::class)
|
||||
|
||||
/** Returns the tag attached with [type] as a key, or null if no tag is attached with that key. */
|
||||
actual fun <T : Any> tag(type: KClass<T>): T? = type.java.cast(tags[type])
|
||||
|
||||
/**
|
||||
@@ -110,6 +116,10 @@ actual class Request internal actual constructor(builder: Builder) {
|
||||
|
||||
actual fun newBuilder(): Builder = Builder(this)
|
||||
|
||||
/**
|
||||
* Returns the cache control directives for this response. This is never null, even if this
|
||||
* response contains no `Cache-Control` header.
|
||||
*/
|
||||
@get:JvmName("cacheControl") actual val cacheControl: CacheControl
|
||||
get() {
|
||||
var result = lazyCacheControl
|
||||
@@ -162,6 +172,7 @@ actual class Request internal actual constructor(builder: Builder) {
|
||||
internal actual var method: String
|
||||
internal actual var headers: Headers.Builder
|
||||
internal actual var body: RequestBody? = null
|
||||
/** A mutable map of tags, or an immutable empty map if we don't have any. */
|
||||
internal actual var tags = mapOf<KClass<*>, Any>()
|
||||
|
||||
actual constructor() {
|
||||
@@ -184,6 +195,12 @@ actual class Request internal actual constructor(builder: Builder) {
|
||||
this.url = url
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the URL target of this request.
|
||||
*
|
||||
* @throws IllegalArgumentException if [url] is not a valid HTTP or HTTPS URL. Avoid this
|
||||
* exception by calling [HttpUrl.parse]; it returns null for invalid URLs.
|
||||
*/
|
||||
actual open fun url(url: String): Builder {
|
||||
return url(canonicalUrl(url).toHttpUrl())
|
||||
}
|
||||
@@ -195,14 +212,32 @@ actual class Request internal actual constructor(builder: Builder) {
|
||||
*/
|
||||
open fun url(url: URL) = url(url.toString().toHttpUrl())
|
||||
|
||||
/**
|
||||
* Sets the header named [name] to [value]. If this request already has any headers
|
||||
* with that name, they are all replaced.
|
||||
*/
|
||||
actual open fun header(name: String, value: String) = commonHeader(name, value)
|
||||
|
||||
/**
|
||||
* Adds a header with [name] and [value]. Prefer this method for multiply-valued
|
||||
* headers like "Cookie".
|
||||
*
|
||||
* Note that for some headers including `Content-Length` and `Content-Encoding`,
|
||||
* OkHttp may replace [value] with a header derived from the request body.
|
||||
*/
|
||||
actual open fun addHeader(name: String, value: String) = commonAddHeader(name, value)
|
||||
|
||||
/** Removes all headers named [name] on this builder. */
|
||||
actual open fun removeHeader(name: String) = commonRemoveHeader(name)
|
||||
|
||||
/** Removes all headers on this builder and adds [headers]. */
|
||||
actual open fun headers(headers: Headers) = commonHeaders(headers)
|
||||
|
||||
/**
|
||||
* Sets this request's `Cache-Control` header, replacing any cache control headers already
|
||||
* present. If [cacheControl] doesn't define any directives, this clears this request's
|
||||
* cache-control headers.
|
||||
*/
|
||||
actual open fun cacheControl(cacheControl: CacheControl): Builder = commonCacheControl(cacheControl)
|
||||
|
||||
actual open fun get(): Builder = commonGet()
|
||||
@@ -220,9 +255,23 @@ actual class Request internal actual constructor(builder: Builder) {
|
||||
|
||||
actual open fun method(method: String, body: RequestBody?): Builder = commonMethod(method, body)
|
||||
|
||||
/**
|
||||
* Attaches [tag] to the request using [T] as a key. Tags can be read from a request using
|
||||
* [Request.tag]. Use null to remove any existing tag assigned for [T].
|
||||
*
|
||||
* Use this API to attach timing, debugging, or other application data to a request so that
|
||||
* you may read it in interceptors, event listeners, or callbacks.
|
||||
*/
|
||||
@JvmName("reifiedTag")
|
||||
actual inline fun <reified T : Any> tag(tag: T?): Builder = tag(T::class, tag)
|
||||
|
||||
/**
|
||||
* Attaches [tag] to the request using [type] as a key. Tags can be read from a request using
|
||||
* [Request.tag]. Use null to remove any existing tag assigned for [type].
|
||||
*
|
||||
* Use this API to attach timing, debugging, or other application data to a request so that
|
||||
* you may read it in interceptors, event listeners, or callbacks.
|
||||
*/
|
||||
actual fun <T : Any> tag(type: KClass<T>, tag: T?): Builder = commonTag(type, type.cast(tag))
|
||||
|
||||
/** Attaches [tag] to the request using `Object.class` as a key. */
|
||||
|
||||
@@ -34,16 +34,71 @@ import okio.source
|
||||
|
||||
actual abstract class RequestBody {
|
||||
|
||||
/** Returns the Content-Type header for this body. */
|
||||
actual abstract fun contentType(): MediaType?
|
||||
|
||||
/**
|
||||
* Returns the number of bytes that will be written to sink in a call to [writeTo],
|
||||
* or -1 if that count is unknown.
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
actual open fun contentLength(): Long = commonContentLength()
|
||||
|
||||
/** Writes the content of this request to [sink]. */
|
||||
@Throws(IOException::class)
|
||||
actual abstract fun writeTo(sink: BufferedSink)
|
||||
|
||||
/**
|
||||
* A duplex request body is special in how it is **transmitted** on the network and
|
||||
* in the **API contract** between OkHttp and the application.
|
||||
*
|
||||
* This method returns false unless it is overridden by a subclass.
|
||||
*
|
||||
* ### Duplex Transmission
|
||||
*
|
||||
* With regular HTTP calls the request always completes sending before the response may begin
|
||||
* receiving. With duplex the request and response may be interleaved! That is, request body bytes
|
||||
* may be sent after response headers or body bytes have been received.
|
||||
*
|
||||
* Though any call may be initiated as a duplex call, only web servers that are specially
|
||||
* designed for this nonstandard interaction will use it. As of 2019-01, the only widely-used
|
||||
* implementation of this pattern is [gRPC][grpc].
|
||||
*
|
||||
* Because the encoding of interleaved data is not well-defined for HTTP/1, duplex request
|
||||
* bodies may only be used with HTTP/2. Calls to HTTP/1 servers will fail before the HTTP request
|
||||
* is transmitted. If you cannot ensure that your client and server both support HTTP/2, do not
|
||||
* use this feature.
|
||||
*
|
||||
* ### Duplex APIs
|
||||
*
|
||||
* With regular request bodies it is not legal to write bytes to the sink passed to
|
||||
* [RequestBody.writeTo] after that method returns. For duplex requests bodies that condition is
|
||||
* lifted. Such writes occur on an application-provided thread and may occur concurrently with
|
||||
* reads of the [ResponseBody]. For duplex request bodies, [writeTo] should return
|
||||
* quickly, possibly by handing off the provided request body to another thread to perform
|
||||
* writing.
|
||||
*
|
||||
* [grpc]: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md
|
||||
*/
|
||||
actual open fun isDuplex(): Boolean = commonIsDuplex()
|
||||
|
||||
/**
|
||||
* Returns true if this body expects at most one call to [writeTo] and can be transmitted
|
||||
* at most once. This is typically used when writing the request body is destructive and it is not
|
||||
* possible to recreate the request body after it has been sent.
|
||||
*
|
||||
* This method returns false unless it is overridden by a subclass.
|
||||
*
|
||||
* By default OkHttp will attempt to retransmit request bodies when the original request fails
|
||||
* due to any of:
|
||||
*
|
||||
* * A stale connection. The request was made on a reused connection and that reused connection
|
||||
* has since been closed by the server.
|
||||
* * A client timeout (HTTP 408).
|
||||
* * A authorization challenge (HTTP 401 and 407) that is satisfied by the [Authenticator].
|
||||
* * A retryable server failure (HTTP 503 with a `Retry-After: 0` response header).
|
||||
* * A misdirected request (HTTP 421) on a coalesced connection.
|
||||
*/
|
||||
actual open fun isOneShot(): Boolean = commonIsOneShot()
|
||||
|
||||
actual companion object {
|
||||
@@ -81,6 +136,7 @@ actual abstract class RequestBody {
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a new request body that transmits this. */
|
||||
@JvmOverloads
|
||||
@JvmStatic
|
||||
@JvmName("create")
|
||||
|
||||
@@ -44,13 +44,36 @@ import okhttp3.internal.connection.Exchange
|
||||
import okhttp3.internal.http.parseChallenges
|
||||
import okio.Buffer
|
||||
|
||||
/**
|
||||
* An HTTP response. Instances of this class are not immutable: the response body is a one-shot
|
||||
* value that may be consumed only once and then closed. All other properties are immutable.
|
||||
*
|
||||
* This class implements [Closeable]. Closing it simply closes its response body. See
|
||||
* [ResponseBody] for an explanation and examples.
|
||||
*/
|
||||
actual class Response internal constructor(
|
||||
/**
|
||||
* The request that initiated this HTTP response. This is not necessarily the same request issued
|
||||
* by the application:
|
||||
*
|
||||
* * It may be transformed by the user's interceptors. For example, an application interceptor
|
||||
* may add headers like `User-Agent`.
|
||||
* * It may be the request generated in response to an HTTP redirect or authentication
|
||||
* challenge. In this case the request URL may be different than the initial request URL.
|
||||
*
|
||||
* Use the `request` of the [networkResponse] field to get the wire-level request that was
|
||||
* transmitted. In the case of follow-ups and redirects, also look at the `request` of the
|
||||
* [priorResponse] objects, which have its own [priorResponse].
|
||||
*/
|
||||
@get:JvmName("request") actual val request: Request,
|
||||
|
||||
/** Returns the HTTP protocol, such as [Protocol.HTTP_1_1] or [Protocol.HTTP_1_0]. */
|
||||
@get:JvmName("protocol") actual val protocol: Protocol,
|
||||
|
||||
/** Returns the HTTP status message. */
|
||||
@get:JvmName("message") actual val message: String,
|
||||
|
||||
/** Returns the HTTP status code. */
|
||||
@get:JvmName("code") actual val code: Int,
|
||||
|
||||
/**
|
||||
@@ -62,12 +85,36 @@ actual class Response internal constructor(
|
||||
/** Returns the HTTP headers. */
|
||||
@get:JvmName("headers") actual val headers: Headers,
|
||||
|
||||
/**
|
||||
* Returns a non-null value if this response was passed to [Callback.onResponse] or returned
|
||||
* from [Call.execute]. Response bodies must be [closed][ResponseBody] and may
|
||||
* be consumed only once.
|
||||
*
|
||||
* This always returns null on responses returned from [cacheResponse], [networkResponse],
|
||||
* and [priorResponse].
|
||||
*/
|
||||
@get:JvmName("body") actual val body: ResponseBody,
|
||||
|
||||
/**
|
||||
* Returns the raw response received from the network. Will be null if this response didn't use
|
||||
* the network, such as when the response is fully cached. The body of the returned response
|
||||
* should not be read.
|
||||
*/
|
||||
@get:JvmName("networkResponse") actual val networkResponse: Response?,
|
||||
|
||||
/**
|
||||
* Returns the raw response received from the cache. Will be null if this response didn't use
|
||||
* the cache. For conditional get requests the cache response and network response may both be
|
||||
* non-null. The body of the returned response should not be read.
|
||||
*/
|
||||
@get:JvmName("cacheResponse") actual val cacheResponse: Response?,
|
||||
|
||||
/**
|
||||
* Returns the response for the HTTP redirect or authorization challenge that triggered this
|
||||
* response, or null if this response wasn't triggered by an automatic retry. The body of the
|
||||
* returned response should not be read because it has already been consumed by the redirecting
|
||||
* client.
|
||||
*/
|
||||
@get:JvmName("priorResponse") actual val priorResponse: Response?,
|
||||
|
||||
/**
|
||||
@@ -112,6 +159,10 @@ actual class Response internal constructor(
|
||||
level = DeprecationLevel.ERROR)
|
||||
fun code(): Int = code
|
||||
|
||||
/**
|
||||
* Returns true if the code is in [200..300), which means the request was successfully received,
|
||||
* understood, and accepted.
|
||||
*/
|
||||
actual val isSuccessful: Boolean = commonIsSuccessful
|
||||
|
||||
@JvmName("-deprecated_message")
|
||||
@@ -147,6 +198,17 @@ actual class Response internal constructor(
|
||||
@Throws(IOException::class)
|
||||
actual fun trailers(): Headers = trailersFn()
|
||||
|
||||
/**
|
||||
* Peeks up to [byteCount] bytes from the response body and returns them as a new response
|
||||
* body. If fewer than [byteCount] bytes are in the response body, the full response body is
|
||||
* returned. If more than [byteCount] bytes are in the response body, the returned value
|
||||
* will be truncated to [byteCount] bytes.
|
||||
*
|
||||
* It is an error to call this method after the body has been consumed.
|
||||
*
|
||||
* **Warning:** this method loads the requested bytes into memory. Most applications should set
|
||||
* a modest limit on `byteCount`, such as 1 MiB.
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
actual fun peekBody(byteCount: Long): ResponseBody {
|
||||
val peeked = body.source().peek()
|
||||
@@ -210,6 +272,10 @@ actual class Response internal constructor(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache control directives for this response. This is never null, even if this
|
||||
* response contains no `Cache-Control` header.
|
||||
*/
|
||||
@get:JvmName("cacheControl") actual val cacheControl: CacheControl
|
||||
get() = commonCacheControl
|
||||
|
||||
@@ -234,6 +300,12 @@ actual class Response internal constructor(
|
||||
level = DeprecationLevel.ERROR)
|
||||
fun receivedResponseAtMillis(): Long = receivedResponseAtMillis
|
||||
|
||||
/**
|
||||
* Closes the response body. Equivalent to `body().close()`.
|
||||
*
|
||||
* Prior to OkHttp 5.0, it was an error to close a response that is not eligible for a body. This
|
||||
* includes the responses returned from [cacheResponse], [networkResponse], and [priorResponse].
|
||||
*/
|
||||
actual override fun close() = commonClose()
|
||||
|
||||
actual override fun toString(): String = commonToString()
|
||||
@@ -287,12 +359,22 @@ actual class Response internal constructor(
|
||||
this.handshake = handshake
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the header named [name] to [value]. If this request already has any headers
|
||||
* with that name, they are all replaced.
|
||||
*/
|
||||
actual open fun header(name: String, value: String) = commonHeader(name, value)
|
||||
|
||||
/**
|
||||
* Adds a header with [name] to [value]. Prefer this method for multiply-valued
|
||||
* headers like "Set-Cookie".
|
||||
*/
|
||||
actual open fun addHeader(name: String, value: String) = commonAddHeader(name, value)
|
||||
|
||||
/** Removes all headers named [name] on this builder. */
|
||||
actual open fun removeHeader(name: String) = commonRemoveHeader(name)
|
||||
|
||||
/** Removes all headers on this builder and adds [headers]. */
|
||||
actual open fun headers(headers: Headers) = commonHeaders(headers)
|
||||
|
||||
actual open fun body(body: ResponseBody) = commonBody(body)
|
||||
|
||||
@@ -33,21 +33,107 @@ import okio.Buffer
|
||||
import okio.BufferedSource
|
||||
import okio.ByteString
|
||||
|
||||
/**
|
||||
* A one-shot stream from the origin server to the client application with the raw bytes of the
|
||||
* response body. Each response body is supported by an active connection to the webserver. This
|
||||
* imposes both obligations and limits on the client application.
|
||||
*
|
||||
* ### The response body must be closed.
|
||||
*
|
||||
* Each response body is backed by a limited resource like a socket (live network responses) or
|
||||
* an open file (for cached responses). Failing to close the response body will leak resources and
|
||||
* may ultimately cause the application to slow down or crash.
|
||||
*
|
||||
* Both this class and [Response] implement [Closeable]. Closing a response simply
|
||||
* closes its response body. If you invoke [Call.execute] or implement [Callback.onResponse] you
|
||||
* must close this body by calling any of the following methods:
|
||||
*
|
||||
* * `Response.close()`
|
||||
* * `Response.body().close()`
|
||||
* * `Response.body().source().close()`
|
||||
* * `Response.body().charStream().close()`
|
||||
* * `Response.body().byteStream().close()`
|
||||
* * `Response.body().bytes()`
|
||||
* * `Response.body().string()`
|
||||
*
|
||||
* There is no benefit to invoking multiple `close()` methods for the same response body.
|
||||
*
|
||||
* For synchronous calls, the easiest way to make sure a response body is closed is with a `try`
|
||||
* block. With this structure the compiler inserts an implicit `finally` clause that calls
|
||||
* [close()][Response.close] for you.
|
||||
*
|
||||
* ```java
|
||||
* Call call = client.newCall(request);
|
||||
* try (Response response = call.execute()) {
|
||||
* ... // Use the response.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* You can use a similar block for asynchronous calls:
|
||||
*
|
||||
* ```java
|
||||
* Call call = client.newCall(request);
|
||||
* call.enqueue(new Callback() {
|
||||
* public void onResponse(Call call, Response response) throws IOException {
|
||||
* try (ResponseBody responseBody = response.body()) {
|
||||
* ... // Use the response.
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* public void onFailure(Call call, IOException e) {
|
||||
* ... // Handle the failure.
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* These examples will not work if you're consuming the response body on another thread. In such
|
||||
* cases the consuming thread must call [close] when it has finished reading the response
|
||||
* body.
|
||||
*
|
||||
* ### The response body can be consumed only once.
|
||||
*
|
||||
* This class may be used to stream very large responses. For example, it is possible to use this
|
||||
* class to read a response that is larger than the entire memory allocated to the current process.
|
||||
* It can even stream a response larger than the total storage on the current device, which is a
|
||||
* common requirement for video streaming applications.
|
||||
*
|
||||
* Because this class does not buffer the full response in memory, the application may not
|
||||
* re-read the bytes of the response. Use this one shot to read the entire response into memory with
|
||||
* [bytes] or [string]. Or stream the response with either [source], [byteStream], or [charStream].
|
||||
*/
|
||||
actual abstract class ResponseBody : Closeable {
|
||||
/** Multiple calls to [charStream] must return the same instance. */
|
||||
private var reader: Reader? = null
|
||||
|
||||
actual abstract fun contentType(): MediaType?
|
||||
|
||||
/**
|
||||
* Returns the number of bytes in that will returned by [bytes], or [byteStream], or -1 if
|
||||
* unknown.
|
||||
*/
|
||||
actual abstract fun contentLength(): Long
|
||||
|
||||
fun byteStream(): InputStream = source().inputStream()
|
||||
|
||||
actual abstract fun source(): BufferedSource
|
||||
|
||||
/**
|
||||
* Returns the response as a byte array.
|
||||
*
|
||||
* This method loads entire response body into memory. If the response body is very large this
|
||||
* may trigger an [OutOfMemoryError]. Prefer to stream the response body if this is a
|
||||
* possibility for your response.
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
actual fun bytes() = commonBytes()
|
||||
|
||||
/**
|
||||
* Returns the response as a [ByteString].
|
||||
*
|
||||
* This method loads entire response body into memory. If the response body is very large this
|
||||
* may trigger an [OutOfMemoryError]. Prefer to stream the response body if this is a
|
||||
* possibility for your response.
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
actual fun byteString() = commonByteString()
|
||||
|
||||
@@ -67,6 +153,22 @@ actual abstract class ResponseBody : Closeable {
|
||||
reader = it
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the response as a string.
|
||||
*
|
||||
* If the response starts with a
|
||||
* [Byte Order Mark (BOM)](https://en.wikipedia.org/wiki/Byte_order_mark), it is consumed and
|
||||
* used to determine the charset of the response bytes.
|
||||
*
|
||||
* Otherwise if the response has a `Content-Type` header that specifies a charset, that is used
|
||||
* to determine the charset of the response bytes.
|
||||
*
|
||||
* Otherwise the response bytes are decoded as UTF-8.
|
||||
*
|
||||
* This method loads entire response body into memory. If the response body is very large this
|
||||
* may trigger an [OutOfMemoryError]. Prefer to stream the response body if this is a
|
||||
* possibility for your response.
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
actual fun string(): String = source().use { source ->
|
||||
source.readString(charset = source.readBomAsCharset(charset()))
|
||||
@@ -104,6 +206,14 @@ actual abstract class ResponseBody : Closeable {
|
||||
}
|
||||
|
||||
actual companion object {
|
||||
/**
|
||||
* Returns a new response body that transmits this string. If [contentType] is non-null and
|
||||
* has a charset other than utf-8 the behaviour differs by platform.
|
||||
*
|
||||
* On the JVM the encoding will be used instead of utf-8.
|
||||
*
|
||||
* On non JVM platforms, this method will fail for encodings other than utf-8.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmName("create")
|
||||
actual fun String.toResponseBody(contentType: MediaType?): ResponseBody {
|
||||
@@ -112,14 +222,17 @@ actual abstract class ResponseBody : Closeable {
|
||||
return buffer.asResponseBody(finalContentType, buffer.size)
|
||||
}
|
||||
|
||||
/** Returns a new response body that transmits this byte array. */
|
||||
@JvmStatic
|
||||
@JvmName("create")
|
||||
actual fun ByteArray.toResponseBody(contentType: MediaType?): ResponseBody = commonToResponseBody(contentType)
|
||||
|
||||
/** Returns a new response body that transmits this byte string. */
|
||||
@JvmStatic
|
||||
@JvmName("create")
|
||||
actual fun ByteString.toResponseBody(contentType: MediaType?): ResponseBody = commonToResponseBody(contentType)
|
||||
|
||||
/** Returns a new response body that transmits this source. */
|
||||
@JvmStatic
|
||||
@JvmName("create")
|
||||
actual fun BufferedSource.asResponseBody(
|
||||
|
||||
Reference in New Issue
Block a user