Expo 更新 v0
版本 0
🌐 Version 0
更新于 2021-12-01
🌐 Updated 2021-12-01
介绍
🌐 Introduction
这是 Expo Updates 的规范,该协议用于向在多个平台上运行的 Expo 应用提供更新。
🌐 This is the specification for Expo Updates, a protocol for delivering updates to Expo apps running on multiple platforms.
一致性
🌐 Conformance
符合规范的服务器和客户端库必须满足所有规范性要求。符合性要求在本文件中通过描述性断言和具有明确定义含义的关键字进行说明。
🌐 Conforming servers and client libraries must fulfill all normative requirements. Conformance requirements are described in this document by both descriptive assertions and key words with clearly defined meanings.
本文件规范部分中的关键词“必须(MUST)”、“不得(MUST NOT)”、“必需(REQUIRED)”、“应当(SHALL)”、“不应(SHALL NOT)”、“应该(SHOULD)”、“不应该(SHOULD NOT)”、“推荐(RECOMMENDED)”、“可以(MAY)”和“可选(OPTIONAL)”应按照 IETF RFC 2119 中的描述进行理解。这些关键词可以以小写形式出现,但仍保持其含义,除非明确声明为非规范性的。
🌐 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in the normative portions of this document are to be interpreted as described in IETF RFC 2119. These key words may appear in lowercase and still retain their meaning unless explicitly declared as non-normative.
该协议的一致实现可以提供附加功能,但不得明确禁止,否则会导致不一致性。
🌐 A conforming implementation of this protocol may provide additional functionality, but must not where explicitly disallowed or would otherwise result in non-conformance.
概述
🌐 Overview
兼容的服务器和客户端库必须遵循 RFC 7231 中描述的 HTTP 规范,以及本规范中描述的更为详细的指南。
🌐 Conforming servers and client libraries MUST follow the HTTP spec as described in RFC 7231 as well as the more precise guidance described in this spec.
“更新”被定义为一个清单(manifest)以及清单中引用的资源。Expo Updates 是一个用于组装和传送在多个平台上运行的应用更新的协议。
🌐 An update is defined as a manifest together with the assets referenced inside the manifest. Expo Updates is a protocol for assembling and delivering updates to apps running on multiple platforms.
运行符合规范的 Expo Updates 客户端库的应用必须加载客户端库缓存中保存的最新更新,可能会在根据清单的 metadata 内容进行过滤后加载。
🌐 An app running a conformant Expo Updates client library MUST load the most recent update saved in the client library's cache, possibly after filtering by the contents of the manifest's metadata.
下面描述了一致的 Expo Updates 客户端库必须如何从一致的服务器检索新的更新:
🌐 The following describes how a conformant Expo Updates client library MUST retrieve a new update from a conformant server:
- 客户端库将根据头部中指定的约束发送一个请求以获取最新的清单。
- 如果下载了新的清单,客户端库将继续发出额外的请求来下载和存储清单中指定的任何丢失的资源。
- 客户端库将编辑其本地状态以反映已向本地缓存添加了新更新。它还会用响应头中发现的新的
expo-manifest-filters和expo-server-defined-headers更新本地状态。
该规范的主要消费者是 Expo 应用服务和希望管理自己的更新服务器以满足内部需求的组织。
🌐 The primary consumers of this spec are Expo Application Services and organizations that wish to manage their own update server to satisfy internal requirements.
清单要求
🌐 Manifest request
符合要求的客户端库必须发出带有标头的 GET 请求:
🌐 A conformant client library MUST make a GET request with the headers:
expo-platform,用于指定客户端运行的平台类型。- iOS 必须是
expo-platform: ios。 - Android 必须是
expo-platform: android。 - 如果不是这些平台之一,服务器应该返回 400 或 404
- iOS 必须是
expo-runtime-version必须是与客户端兼容的运行时版本。运行时版本规定了客户端正在运行的本地代码设置。它应在客户端构建时设置。例如,在 iOS 客户端中,该值可能会在 plist 文件中设置。- 任何由之前响应的服务器定义的头规定的头信息。
符合规范的客户端库必须根据支持的响应结构至少发送 accept: application/expo+json、accept: application/json 或 accept: multipart/mixed 中的一种,尽管应该发送 accept: application/expo+json, application/json, multipart/mixed。符合规范的客户端库可以使用 RFC 7231 中规定的 "q" 参数来表达偏好,这些参数默认值为 1。
🌐 A conformant client library MUST send at least one of accept: application/expo+json, accept: application/json, or accept: multipart/mixed based on the supported response structures though SHOULD send accept: application/expo+json, application/json, multipart/mixed. A conformant client library MAY express preference using "q" parameters as specified in RFC 7231, which default to 1.
配置为执行代码签名验证的符合规范的客户端库必须发送 expo-expect-signature 头,以表明它期望符合规范的服务器在清单响应中包含 expo-signature 头。expo-expect-signature 是一个Expo SFV 字典,可以包含以下任意键值对:
🌐 A conformant client library configured to perform code signing verification MUST send a expo-expect-signature header to indicate that it expects the conformant server to include the expo-signature header in the manifest response. expo-expect-signature is an Expo SFV dictionary which MAY contain any of the following key value pairs:
sig应该包含布尔值true,以指示它需要兼容的服务器在sig键中返回签名。keyid应该包含客户端用于验证签名的公钥的 keyIdalg应包含客户端将用于验证签名的算法
示例:
🌐 Example:
accept: application/expo+json;q=0.9, application/json;q=0.8, multipart/mixed expo-platform: * expo-runtime-version: * expo-expect-signature: sig, keyid="root", alg="rsa-v1_5-sha256"
清单响应
🌐 Manifest response
符合规范的服务器必须至少以两种方式中的一种返回响应。符合规范的服务器可以支持其中一种或两种响应结构,当请求不支持的响应结构时,服务器应使用 HTTP 406 错误状态进行响应。
🌐 A conformant server MUST return a response structured in at least one of two ways. A conformant server MAY support either or both response structures, and when an unsupported response structure is requested the server SHOULD respond with an HTTP 406 error status.
- 对于包含
content-type: application/json或content-type: application/expo+json的响应,必须在响应头中发送清单头,并且必须在响应体中发送清单主体。 - 对于包含
content-type: multipart/mixed的响应,响应必须按照 多部分清单响应 部分中指定的结构进行。
清单和头信息的选择取决于请求头的值。符合规范的服务器必须响应包含最近更新的清单,按创建时间排序,并满足由请求头施加的所有参数和约束。服务器可以使用请求的任何属性,例如其头信息和源IP地址,在多个符合请求约束的更新中进行选择。
🌐 The choice of manifest and headers are dependent on the values of the request headers. A conformant server MUST respond with a manifest for the most recent update, ordered by creation time, satisfying all parameters and constraints imposed by the request headers. The server MAY use any properties of the request like its headers and source IP address to choose among several updates that all satisfy the request's constraints.
清单响应标头
🌐 Manifest response headers
expo-protocol-version: 0 expo-sfv-version: 0 expo-manifest-filters: <expo-sfv> expo-server-defined-headers: <expo-sfv> cache-control: * content-type: * expo-signature: *
expo-protocol-version描述了本规范中定义的协议版本,并且必须为0。expo-sfv-version必须是0。expo-manifest-filters是一个 Expo SFV 字典。它用于根据 manifest 中找到的metadata属性过滤客户端库存储的更新。如果过滤器中提到了某个字段,则为了包含该更新,元数据中相应的字段必须要么不存在,要么相等。客户端库必须存储 manifest 过滤器,直到被更新的响应覆盖。expo-server-defined-headers是一个 Expo SFV 字典。它定义了客户端库必须保存的标头,直到被更新的字典覆盖,并且这些标头必须包含在每个后续的 manifest 请求 中。cache-control必须设置为适当较短的时间。建议使用cache-control: private, max-age=0的值以确保返回最新的清单。设置较长的缓存时间可能会导致获取过时的更新。content-type必须通过 RFC 7231 中定义的 主动协商 来确定。由于客户端库 必须 在每个清单请求中发送accept头,这总是会是application/expo+json或application/json;否则请求将返回406错误。expo-signature应包含在 代码签名 验证步骤中使用的清单的签名,如果清单的请求包含expo-expect-signature头。它是一个 Expo SFV 字典,可能包含以下任意键值对:sig必须包含清单的签名。此字段的名称与expo-expect-signature相同。keyid可能包含服务器用来签署响应的密钥的 keyId。客户端应使用与此keyid匹配的证书来验证签名。alg可能包含服务器用来签署响应的算法。客户端仅在该字段与匹配keyid的证书所定义的算法一致时才应使用它。
清单响应体
🌐 Manifest response body
响应的主体必须是清单,该清单定义为符合以下 Manifest TypeScript 定义 [TypeScript](https://ts.nodejs.cn/) 以及每个字段的详细描述的 JSON 格式:
🌐 The body of the response MUST be a manifest, which is defined as JSON conforming to both the following Manifest definition expressed in TypeScript and the detailed descriptions for each field:
type Manifest = { id: string; createdAt: string; runtimeVersion: string; launchAsset: Asset; assets: Asset[]; metadata: { [key: string]: string }; extra: { [key: string]: any }; }; type Asset = { hash?: string; key: string; contentType: string; fileExtension?: string; url: string; };
id:ID 必须唯一指定清单。createdAt:更新创建的日期和时间非常重要,因为客户端库会选择最新的更新(受expo-manifest-filters头提供的任何约束条件限制)。日期时间应按照 ISO 8601 格式化。runtimeVersion:可以是开发者定义的任何字符串。它规定了运行相关更新所需的本地代码设置。launchAsset:一个特殊的资源,是应用代码的入口点。此资源将忽略fileExtension字段,应当省略该字段。assets:更新包使用的一系列资源,例如 JavaScript、图片和字体。所有资源(包括launchAsset)都应该在执行更新之前下载到本地磁盘,并且应向应用代码提供资源key到磁盘位置的映射。- 每个资源对象的属性:
hash:文件的 Base64URL 编码 SHA-256 哈希,以保证完整性。Base64URL 编码由 IETF RFC 4648 定义。key:用于在更新的应用代码中引用此资源的键。例如,这个键可能由处理应用代码的单独构建步骤生成,例如打包工具。contentType:根据 RFC 2045 定义的文件 MIME 类型。例如,application/javascript、image/jpeg。fileExtension:建议在客户端保存文件时使用的扩展名。一些平台,例如 iOS,需要某些文件类型以扩展名保存。扩展名必须以.为前缀。例如,.jpeg。在某些情况下,例如 launchAsset,这个字段将被忽略,而使用本地确定的扩展名。如果省略该字段且没有本地规定的扩展名,资源将保存为无扩展名的文件。例如,仅为./filename,末尾没有.。符合规范的客户端如果文件扩展名不为空且缺少.前缀,应在文件扩展名前加上.前缀。url:可以获取文件的位置。
metadata:与更新相关的元数据。它是一个字符串值的字典。服务器至少必须返回一个空对象,但可以在对象内返回任何它希望用于过滤更新的内容。元数据必须通过随附的expo-manifest-filters头中定义的过滤器。extra:用于指定可选的“额外”信息,例如第三方配置。服务器至少必须返回一个空对象。Expo Updates 不指定也不依赖此字段,但其他库可能会使用。例如,如果更新托管在 Expo 应用服务(EAS)上,可能会包含 EAS 项目 ID 和应用配置(许多 Expo 库通过expo-constants使用这些信息):
"extra": { "eas": { "projectId": "00000000-0000-0000-0000-000000000000" }, "expoConfig": { "name": "...", "version": "...", "iconUrl": "...", %%placeholder-start%%... %%placeholder-end%% }, }
多部分清单响应
🌐 Multipart manifest response
这种格式的清单响应由 RFC 2046 定义的 multipart/mixed MIME 类型定义。
🌐 A manifest response of this format is defined by the multipart/mixed MIME type as defined by RFC 2046.
此响应格式的标题与清单响应标题相同,但有以下例外:
🌐 Headers for this response format are the same as manifest response headers, with the following exceptions:
content-type应具有由 RFC 2046 定义的multipart/mixed值- 如果正在使用代码签名,
expo-signature应包含在下面的manifest部分标题中。
各部分定义如下:
🌐 Each part is defined as follows:
- 必需的
"manifest"部件:- 必须有部件标题
content-disposition: inline; name="manifest"。 - 必须具有部件头
content-type: application/json或application/expo+json。 - 如果使用了代码签名,则应具有在 manifest response headers 中定义的部分头
expo-signature。 - 清单主体 必须在部分主体中发送。
- 必须有部件标题
- 可选
"extensions"部分:- 必须有部件标题
content-disposition: inline; name="extensions"。 - 必须有部件标题
content-type: application/json。 - 清单扩展 必须在部分正文中发送。
- 必须有部件标题
清单扩展
🌐 Manifest extensions
定义为符合以下两个条件的 JSON:在 TypeScript 中表示的 ManifestExtensions 定义以及对每个字段的详细描述:
🌐 Defined as JSON conforming to both the following ManifestExtensions definition expressed in TypeScript and the detailed descriptions for each field:
type ManifestExtensions = { assetRequestHeaders: ExpoAssetHeaderDictionary; ... } type ExpoAssetHeaderDictionary = { [assetKey: string]: { [headerName: string]: string, }; }
assetRequestHeaders:可能包含用于资源请求的头部(键、值)对字典。键和值都必须是字符串。
资源请求
🌐 Asset request
符合规范的客户端库必须对清单中指定的资源 URL 发出 GET 请求。客户端库应包含一个接受清单中指定的资源内容类型的头。此外,客户端库还应指定其能够处理的压缩编码。
🌐 A conformant client library MUST make a GET request to the asset URLs specified by the manifest. The client library SHOULD include a header accepting the asset's content type as specified in the manifest. Additionally, the client library SHOULD specify the compression encoding the client library is capable of handling.
标题示例:
🌐 Example headers:
accept: image/jpeg, */* accept-encoding: br, gzip
符合规范的客户端库还必须包含为此资源键在assetRequestHeaders中包含的任何头(键,值)对。
🌐 A conformant client library MUST also include any header (key, value) pairs included in assetRequestHeaders for this asset key.
资源响应
🌐 Asset response
位于特定 URL 的资源不得更改或删除,因为客户端库可能随时获取资源进行任何更新。符合规范的客户端必须验证该资源的 base64url 编码的 SHA-256 哈希值是否与清单中该资源的 hash 字段匹配。
🌐 An asset located at a particular URL MUST NOT be changed or removed since client libraries may fetch assets for any update at any time. A conformant client MUST verify that the base64url-encoded SHA-256 hash of the asset matches the hash field for the asset from the manifest.
资源响应标头
🌐 Asset response headers
根据请求的 accept-encoding 头部,该资源必须使用客户端支持的压缩格式进行编码。服务器可以提供未压缩的资源。响应必须包含一个 content-type 头部,标明资源的 MIME 类型。例如:
🌐 The asset MUST be encoded using a compression format that the client supports according to the request's accept-encoding header. The server MAY serve uncompressed assets. The response MUST include a content-type header with the MIME type of the asset.
For example:
content-encoding: br content-type: application/javascript
建议将资源与设置为长时间的 cache-control 头一起提供,因为位于给定 URL 的资源不应更改。例如:
🌐 An asset is RECOMMENDED to be served with a cache-control header set to a long duration as an asset located at a given URL must not change. For example:
cache-control: public, max-age=31536000, immutable
压缩
🌐 Compression
🌐 Assets SHOULD be capable of being served with Gzip and Brotli compression.
代码签名
🌐 Code signing
Expo Updates 支持对清单进行代码签名。这也会间接对资源进行签名,因为它们的哈希值存在于清单中,并由合格的客户端进行验证。合格的客户端可以请求使用私钥对清单进行签名,然后必须在使用清单或下载任何相应资源之前,使用相应的代码签名证书验证清单的签名。客户端必须验证签名证书是否为自签名的受信任根证书,或是否在受信任根证书签署的证书链中。在任一情况下,根证书必须嵌入到应用或设备的操作系统中。
🌐 Expo Updates supports code signing the manifest. This also transitively signs the assets since their hashes are present in the manifest and verified by a conformant client. A conformant client MAY request the manifest be signed using a private key, and then MUST verify the signature of the manifest using the corresponding code signing certificate before it is used or any corresponding assets are downloaded. The client MUST verify that the signing certificate is either a self-signed, trusted root certificate or is in a certificate chain signed by a trusted root certificate. In either case, the root certificate MUST be embedded in the application or device's operating system.
客户端库
🌐 Client library
请参阅参考客户端库。
🌐 See the reference client library.