@@ -8,9 +8,11 @@ import com.openlayer.api.core.http.HttpMethod
88import com.openlayer.api.core.http.HttpRequest
99import com.openlayer.api.core.http.HttpRequestBody
1010import com.openlayer.api.core.http.HttpResponse
11+ import com.openlayer.api.core.http.ProxyAuthenticator
1112import com.openlayer.api.errors.OpenlayerIoException
1213import java.io.IOException
1314import java.io.InputStream
15+ import java.io.OutputStream
1416import java.net.Proxy
1517import java.time.Duration
1618import java.util.concurrent.CancellationException
@@ -20,10 +22,12 @@ import java.util.concurrent.TimeUnit
2022import javax.net.ssl.HostnameVerifier
2123import javax.net.ssl.SSLSocketFactory
2224import javax.net.ssl.X509TrustManager
25+ import kotlin.jvm.optionals.getOrNull
2326import okhttp3.Call
2427import okhttp3.Callback
2528import okhttp3.ConnectionPool
2629import okhttp3.Dispatcher
30+ import okhttp3.HttpUrl
2731import okhttp3.HttpUrl.Companion.toHttpUrl
2832import okhttp3.MediaType
2933import okhttp3.MediaType.Companion.toMediaType
@@ -33,6 +37,8 @@ import okhttp3.RequestBody.Companion.toRequestBody
3337import okhttp3.Response
3438import okhttp3.logging.HttpLoggingInterceptor
3539import okio.BufferedSink
40+ import okio.buffer
41+ import okio.sink
3642
3743class OkHttpClient
3844internal constructor (@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClient ) : HttpClient {
@@ -41,7 +47,7 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie
4147 val call = newCall(request, requestOptions)
4248
4349 return try {
44- call.execute().toResponse ()
50+ call.execute().toHttpResponse ()
4551 } catch (e: IOException ) {
4652 throw OpenlayerIoException (" Request failed" , e)
4753 } finally {
@@ -59,7 +65,7 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie
5965 call.enqueue(
6066 object : Callback {
6167 override fun onResponse (call : Call , response : Response ) {
62- future.complete(response.toResponse ())
68+ future.complete(response.toHttpResponse ())
6369 }
6470
6571 override fun onFailure (call : Call , e : IOException ) {
@@ -111,89 +117,6 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie
111117 return client.newCall(request.toRequest(client))
112118 }
113119
114- private fun HttpRequest.toRequest (client : okhttp3.OkHttpClient ): Request {
115- var body: RequestBody ? = body?.toRequestBody()
116- if (body == null && requiresBody(method)) {
117- body = " " .toRequestBody()
118- }
119-
120- val builder = Request .Builder ().url(toUrl()).method(method.name, body)
121- headers.names().forEach { name ->
122- headers.values(name).forEach { builder.addHeader(name, it) }
123- }
124-
125- if (
126- ! headers.names().contains(" X-Stainless-Read-Timeout" ) && client.readTimeoutMillis != 0
127- ) {
128- builder.addHeader(
129- " X-Stainless-Read-Timeout" ,
130- Duration .ofMillis(client.readTimeoutMillis.toLong()).seconds.toString(),
131- )
132- }
133- if (! headers.names().contains(" X-Stainless-Timeout" ) && client.callTimeoutMillis != 0 ) {
134- builder.addHeader(
135- " X-Stainless-Timeout" ,
136- Duration .ofMillis(client.callTimeoutMillis.toLong()).seconds.toString(),
137- )
138- }
139-
140- return builder.build()
141- }
142-
143- /* * `OkHttpClient` always requires a request body for some methods. */
144- private fun requiresBody (method : HttpMethod ): Boolean =
145- when (method) {
146- HttpMethod .POST ,
147- HttpMethod .PUT ,
148- HttpMethod .PATCH -> true
149- else -> false
150- }
151-
152- private fun HttpRequest.toUrl (): String {
153- val builder = baseUrl.toHttpUrl().newBuilder()
154- pathSegments.forEach(builder::addPathSegment)
155- queryParams.keys().forEach { key ->
156- queryParams.values(key).forEach { builder.addQueryParameter(key, it) }
157- }
158-
159- return builder.toString()
160- }
161-
162- private fun HttpRequestBody.toRequestBody (): RequestBody {
163- val mediaType = contentType()?.toMediaType()
164- val length = contentLength()
165-
166- return object : RequestBody () {
167- override fun contentType (): MediaType ? = mediaType
168-
169- override fun contentLength (): Long = length
170-
171- override fun isOneShot (): Boolean = ! repeatable()
172-
173- override fun writeTo (sink : BufferedSink ) = writeTo(sink.outputStream())
174- }
175- }
176-
177- private fun Response.toResponse (): HttpResponse {
178- val headers = headers.toHeaders()
179-
180- return object : HttpResponse {
181- override fun statusCode (): Int = code
182-
183- override fun headers (): Headers = headers
184-
185- override fun body (): InputStream = body!! .byteStream()
186-
187- override fun close () = body!! .close()
188- }
189- }
190-
191- private fun okhttp3.Headers.toHeaders (): Headers {
192- val headersBuilder = Headers .builder()
193- forEach { (name, value) -> headersBuilder.put(name, value) }
194- return headersBuilder.build()
195- }
196-
197120 companion object {
198121 @JvmStatic fun builder () = Builder ()
199122 }
@@ -202,6 +125,7 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie
202125
203126 private var timeout: Timeout = Timeout .default()
204127 private var proxy: Proxy ? = null
128+ private var proxyAuthenticator: ProxyAuthenticator ? = null
205129 private var maxIdleConnections: Int? = null
206130 private var keepAliveDuration: Duration ? = null
207131 private var dispatcherExecutorService: ExecutorService ? = null
@@ -215,6 +139,10 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie
215139
216140 fun proxy (proxy : Proxy ? ) = apply { this .proxy = proxy }
217141
142+ fun proxyAuthenticator (proxyAuthenticator : ProxyAuthenticator ? ) = apply {
143+ this .proxyAuthenticator = proxyAuthenticator
144+ }
145+
218146 /* *
219147 * Sets the maximum number of idle connections kept by the underlying [ConnectionPool].
220148 *
@@ -264,6 +192,19 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie
264192 .callTimeout(timeout.request())
265193 .proxy(proxy)
266194 .apply {
195+ proxyAuthenticator?.let { auth ->
196+ proxyAuthenticator { route, response ->
197+ auth
198+ .authenticate(
199+ route?.proxy ? : Proxy .NO_PROXY ,
200+ response.request.toHttpRequest(),
201+ response.toHttpResponse(),
202+ )
203+ .getOrNull()
204+ ?.toRequest(client = null )
205+ }
206+ }
207+
267208 dispatcherExecutorService?.let { dispatcher(Dispatcher (it)) }
268209
269210 val maxIdleConnections = maxIdleConnections
@@ -303,3 +244,126 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie
303244 )
304245 }
305246}
247+
248+ private fun HttpRequest.toRequest (client : okhttp3.OkHttpClient ? ): Request {
249+ var body: RequestBody ? = body?.toRequestBody()
250+ if (body == null && requiresBody(method)) {
251+ body = " " .toRequestBody()
252+ }
253+
254+ val builder = Request .Builder ().url(toUrl()).method(method.name, body)
255+ headers.names().forEach { name -> headers.values(name).forEach { builder.addHeader(name, it) } }
256+
257+ if (client != null ) {
258+ if (
259+ ! headers.names().contains(" X-Stainless-Read-Timeout" ) && client.readTimeoutMillis != 0
260+ ) {
261+ builder.addHeader(
262+ " X-Stainless-Read-Timeout" ,
263+ Duration .ofMillis(client.readTimeoutMillis.toLong()).seconds.toString(),
264+ )
265+ }
266+ if (! headers.names().contains(" X-Stainless-Timeout" ) && client.callTimeoutMillis != 0 ) {
267+ builder.addHeader(
268+ " X-Stainless-Timeout" ,
269+ Duration .ofMillis(client.callTimeoutMillis.toLong()).seconds.toString(),
270+ )
271+ }
272+ }
273+
274+ return builder.build()
275+ }
276+
277+ /* * `OkHttpClient` always requires a request body for some methods. */
278+ private fun requiresBody (method : HttpMethod ): Boolean =
279+ when (method) {
280+ HttpMethod .POST ,
281+ HttpMethod .PUT ,
282+ HttpMethod .PATCH -> true
283+ else -> false
284+ }
285+
286+ private fun HttpRequest.toUrl (): String {
287+ val builder = baseUrl.toHttpUrl().newBuilder()
288+ pathSegments.forEach(builder::addPathSegment)
289+ queryParams.keys().forEach { key ->
290+ queryParams.values(key).forEach { builder.addQueryParameter(key, it) }
291+ }
292+
293+ return builder.toString()
294+ }
295+
296+ private fun HttpRequestBody.toRequestBody (): RequestBody {
297+ val mediaType = contentType()?.toMediaType()
298+ val length = contentLength()
299+
300+ return object : RequestBody () {
301+ override fun contentType (): MediaType ? = mediaType
302+
303+ override fun contentLength (): Long = length
304+
305+ override fun isOneShot (): Boolean = ! repeatable()
306+
307+ override fun writeTo (sink : BufferedSink ) = writeTo(sink.outputStream())
308+ }
309+ }
310+
311+ private fun Request.toHttpRequest (): HttpRequest {
312+ val builder = HttpRequest .builder().method(HttpMethod .valueOf(method)).baseUrl(url.toBaseUrl())
313+ url.pathSegments.forEach(builder::addPathSegment)
314+ url.queryParameterNames.forEach { name ->
315+ url.queryParameterValues(name).filterNotNull().forEach { builder.putQueryParam(name, it) }
316+ }
317+ headers.forEach { (name, value) -> builder.putHeader(name, value) }
318+ body?.let { builder.body(it.toHttpRequestBody()) }
319+ return builder.build()
320+ }
321+
322+ private fun HttpUrl.toBaseUrl (): String = buildString {
323+ append(scheme).append(" ://" ).append(host)
324+ if (port != HttpUrl .defaultPort(scheme)) {
325+ append(" :" ).append(port)
326+ }
327+ }
328+
329+ private fun RequestBody.toHttpRequestBody (): HttpRequestBody {
330+ val mediaType = contentType()?.toString()
331+ val length = contentLength()
332+ val isOneShot = isOneShot()
333+ val source = this
334+ return object : HttpRequestBody {
335+ override fun contentType (): String? = mediaType
336+
337+ override fun contentLength (): Long = length
338+
339+ override fun repeatable (): Boolean = ! isOneShot
340+
341+ override fun writeTo (outputStream : OutputStream ) {
342+ val sink = outputStream.sink().buffer()
343+ source.writeTo(sink)
344+ sink.flush()
345+ }
346+
347+ override fun close () {}
348+ }
349+ }
350+
351+ private fun Response.toHttpResponse (): HttpResponse {
352+ val headers = headers.toHeaders()
353+
354+ return object : HttpResponse {
355+ override fun statusCode (): Int = code
356+
357+ override fun headers (): Headers = headers
358+
359+ override fun body (): InputStream = body!! .byteStream()
360+
361+ override fun close () = body!! .close()
362+ }
363+ }
364+
365+ private fun okhttp3.Headers.toHeaders (): Headers {
366+ val headersBuilder = Headers .builder()
367+ forEach { (name, value) -> headersBuilder.put(name, value) }
368+ return headersBuilder.build()
369+ }
0 commit comments