Skip to content

Add custom headers to HttpsCallableReference #6587

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions firebase-functions/src/androidTest/backend/functions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ exports.timeoutTest = functions.https.onRequest((request, response) => {
setTimeout(() => response.send({data: true}), 500);
});

exports.headersTest = functions.https.onRequest((request, response) => {
response.status(200).send({data: request.headers});
});

const streamData = ['hello', 'world', 'this', 'is', 'cool'];

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,28 @@ class CallTests {

assertThat(actual).isNull()
}

@Test
fun testCustomHeaders() {
val functions = Firebase.functions(app)

val options = HttpsCallableOptions.Builder()
.addHeader("Header1", "value1")
.addHeader("Header2", "value2")
.build()

val function = functions.getHttpsCallable("headersTest", options)
.addHeader("Header2", "value3")
.addHeader("Header3", "value4")
.addHeader("Header4", "value5")
.addHeaders(mapOf("Header4" to "value6"))

val actual = Tasks.await(function.call()).getData() as? Map<*, *>

assertThat(actual).isNotNull()
assertThat(actual?.get("Header1")).isEqualTo("value1")
assertThat(actual?.get("Header2")).isEqualTo("value3")
assertThat(actual?.get("Header3")).isEqualTo("value4")
assertThat(actual?.get("Header4")).isEqualTo("value6")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import java.util.concurrent.Executor
import javax.inject.Named
import okhttp3.Call
import okhttp3.Callback
import okhttp3.Headers
import okhttp3.MediaType
import okhttp3.OkHttpClient
import okhttp3.Request
Expand Down Expand Up @@ -229,7 +230,9 @@ internal constructor(
val bodyJSON = JSONObject(body)
val contentType = MediaType.parse("application/json")
val requestBody = RequestBody.create(contentType, bodyJSON.toString())
var request = Request.Builder().url(url).post(requestBody)
// Add custom headers first so that internal headers cannot be overwritten
val customHeaders = Headers.of(options.headers)
var request = Request.Builder().url(url).post(requestBody).headers(customHeaders)
if (context!!.authToken != null) {
request = request.header("Authorization", "Bearer " + context.authToken)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,17 @@ internal class HttpsCallOptions {
private var timeout = DEFAULT_TIMEOUT
private var timeoutUnits = DEFAULT_TIMEOUT_UNITS
@JvmField public val limitedUseAppCheckTokens: Boolean
@JvmField val headers: MutableMap<String, String>

/** Creates an (internal) HttpsCallOptions from the (external) [HttpsCallableOptions]. */
internal constructor(publicCallableOptions: HttpsCallableOptions) {
limitedUseAppCheckTokens = publicCallableOptions.limitedUseAppCheckTokens
headers = publicCallableOptions.headers.toMutableMap()
}

internal constructor() {
limitedUseAppCheckTokens = false
headers = mutableMapOf()
}

internal fun getLimitedUseAppCheckTokens(): Boolean {
Expand All @@ -56,6 +59,31 @@ internal class HttpsCallOptions {
return timeoutUnits.toMillis(timeout)
}

/**
* Adds an HTTP header for calls from this instance of Functions.
*
* Note that an existing header with the same name will be overwritten.
*
* @param name Name of HTTP header
* @param value Value of HTTP header
*/
internal fun addHeader(name: String, value: String): HttpsCallOptions {
headers[name] = value
return this
}

/**
* Adds all HTTP headers of passed map for calls from this instance of Functions.
*
* Note that an existing header with the same name will be overwritten.
*
* @param headers Map of HTTP headers (name to value)
*/
internal fun addHeaders(headers: Map<String, String>): HttpsCallOptions {
this.headers.putAll(headers)
return this
}

/** Creates a new OkHttpClient with these options applied to it. */
internal fun apply(client: OkHttpClient): OkHttpClient {
return client
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,23 @@ private constructor(
* Returns the setting indicating if limited-use App Check tokens are enforced for this function.
*/
// If true, request a limited-use token from AppCheck.
@JvmField public val limitedUseAppCheckTokens: Boolean
@JvmField public val limitedUseAppCheckTokens: Boolean,
@JvmField internal val headers: Map<String, String>,
) {

public fun getLimitedUseAppCheckTokens(): Boolean {
return limitedUseAppCheckTokens
}

public fun getHeaders(): Map<String, String> {
// Returning a defensive copy
return headers.toMap()
}

/** A builder for creating [com.google.firebase.functions.HttpsCallableOptions]. */
public class Builder {
@JvmField public var limitedUseAppCheckTokens: Boolean = false
private val headers: MutableMap<String, String> = mutableMapOf()

/** Returns the setting indicating if limited-use App Check tokens are enforced. */
public fun getLimitedUseAppCheckTokens(): Boolean {
Expand All @@ -49,9 +56,34 @@ private constructor(
return this
}

/**
* Adds an HTTP header for callable functions.
*
* Note that an existing header with the same name will be overwritten.
*
* @param name Name of HTTP header
* @param value Value of HTTP header
*/
public fun addHeader(name: String, value: String): Builder {
headers[name] = value
return this
}

/**
* Adds all HTTP headers of passed map for callable functions.
*
* Note that an existing header with the same name will be overwritten.
*
* @param headers Map of HTTP headers (name to value)
*/
public fun addHeaders(headers: Map<String, String>): Builder {
this.headers.putAll(headers)
return this
}

/** Builds a new [com.google.firebase.functions.HttpsCallableOptions]. */
public fun build(): HttpsCallableOptions {
return HttpsCallableOptions(limitedUseAppCheckTokens)
return HttpsCallableOptions(limitedUseAppCheckTokens, headers.toMap())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,29 @@ public class HttpsCallableReference {
other.setTimeout(timeout, units)
return other
}

/**
* Adds an HTTP header for calls from this instance of Functions.
*
* Note that an existing header with the same name will be overwritten.
*
* @param name Name of HTTP header
* @param value Value of HTTP header
*/
public fun addHeader(name: String, value: String): HttpsCallableReference {
options.addHeader(name, value)
return this
}

/**
* Adds all HTTP headers of passed map for calls from this instance of Functions.
*
* Note that an existing header with the same name will be overwritten.
*
* @param headers Map of HTTP headers (name to value)
*/
public fun addHeaders(headers: Map<String, String>): HttpsCallableReference {
options.addHeaders(headers)
return this
}
}