在 Mozilla Firefox 中调试 Web Push

简介

本文是关于使用和操作 Web Push 和 Mozilla 的 Web Push 服务的 系列文章 的一部分。本文并非旨在作为通用指南,而是提供关于如何最佳使用该服务的建议和见解。假设读者具备 Javascript、Python 或其他技术的相关知识。

Web Push 实现问题的一个挑战在于试图找出哪里出了错。Web Push 拥有大量的“移动部件”、系统和组件,它们需要协同工作才能成功发送和接收您的消息。虽然本文无法解决所有可能导致特定 Push 消息失败的原因,但我将尝试为一些常见问题提供工具和指导。

常见问题

错误报告

我们看到最大的问题来自那些没有注意我们提供的返回代码的网站。 之前的文章 提到了如何管理订阅以降低发送永远不会送达的消息的成本。关注返回的错误也很重要。

错误可能会返回如下所示的正文

{
  'errno': 102,
  'message': 'Request did not validate invalid token',
  'code': 404,
  'more_info': 'http://autopush.readthedocs.io/en/latest/http.html#error-codes',
  'error': 'Not Found'
}

errno 值的列表可以在 Autopush 文档 中找到。(Autopush 是我们运行的开源服务器的名称,用于处理接收和转发推送订阅更新。)服务器试图提供尽可能多的详细信息和帮助,因此,如果您的消息没有发送成功,Autopush 消息可能会提供很大的帮助。

VAPID 问题

另一个潜在问题是对 VAPID 的理解混淆。VAPID 是一个正在开发的规范,用于订阅提供网站进行“自我识别”。这意味着,如果存在重大问题,推送服务器操作员可以联系发送有问题的消息的人员。如果您看到大量“401”状态代码响应消息,则可能是您的 VAPID 身份验证存在问题。

VAPID 还允许网站创建“受限订阅”。这些订阅被锁定,因此只有拥有 VAPID 密钥的一方才能发送它们。这可能有点令人困惑。

简单来说,VAPID 是一块使用密钥进行加密签名的的数据块,该密钥有两部分:您永远不会共享的私钥,用于对 VAPID 令牌进行签名;以及可以安全共享的公钥,用于创建受限订阅。通常,您有一个持续相当长时间的 VAPID 密钥。最好不要让它永远有效,但它肯定可以持续一年左右。我稍后会谈谈其影响。

当您的应用 请求推送端点 时,您可以选择提供您的 VAPID 公钥作为applicationServerKey。这将创建一个锁定到该密钥的新订阅端点。为了发送成功的订阅请求,您必须使用相应的私钥对 VAPID 块进行签名,并在您的请求中包含公钥。(您在请求中包含公钥的方式在草案 01 和 草案 02 之间 发生了变化。您可能希望找到 一个库 来执行签名。您还需要查阅您选择的推送服务的文档,以查看它们接受哪些格式。大多数推送平台都接受草案 02。)

请注意,VAPID 公钥与您请求的 URL 相关联。这意味着每次要向该 URL 发送数据时,都需要使用相同的密钥对。如果您想更改 VAPID 密钥,则需要获取一个新的端点并丢弃旧的端点。当然,您可以决定如何操作。您的应用可以简单地从已知位置获取新的公钥,生成新的 URL 请求并将新的注册信息发送回您的服务器。这将执行您希望在 pushsubscriptionchange 事件中使用的许多相同代码。

因此,对于 VAPID 401 错误,您需要检查

  1. 您是否使用了与获取受限订阅时使用的相同的密钥对?
  2. 您是否已正确签署了您的 VAPID 授权密钥?
  3. 您是否已在您的请求中包含所有必需的 VAPID 标头?

数据加密

最后,还有实际的消息加密。这可能是最难调试的,因为推送服务器在接受要传递的消息时无法知道加密是否正确。此外,只有在用户代理 (UA) 成功解密消息时,才会由用户代理 (UA) 传递消息。这可以防止您的应用因虚假消息而唤醒,从而可能耗尽电池电量。

同样,解决此类问题的最佳方法是使用 一个库。但这并非总是可行,您可能需要“手动”创建自己的库。有一个 页面 可以帮助您逐步完成加密数据的过程,但它目前仅适用于 Firefox。

请注意,规范中有一些更改可能尚未得到支持。截至发布日期,大多数推送实现都将接受 ECE 草案 03“aesgcm”编码。有一个 提案 建议用户代理报告其支持的加密形式,但这尚未正式被接受。不幸的是,这是使用仍在开发中的技术的弊端之一。如果您希望关注正在提出的更改,则应关注工作组。

不幸的是,推送服务无法检测到许多加密错误。这是因为推送服务无法解密您的消息。幸运的是,可以“查看”客户端内部以查看是否存在错误。

调试桌面

使用 浏览器控制台 来帮助调试桌面。请注意,这与作为 开发者工具面板 一部分显示的“Web 控制台”不同。浏览器控制台显示更高级别的消息,这些消息可能无法在页面或 Web 控制台中看到。

好处是,您将能够看到服务工作者可能记录的消息以及其他推送消息。例如,如果您查看我们使用的测试页面,您可能会在浏览器中看到类似的内容

浏览器控制台输出示例

在该窗口的底部附近,您会注意到来自“sw.js”的四条消息。前三条是console.info()消息,表明已收到新消息、新消息的内容以及关联的客户端。请注意,第四条消息是console.error(),显示“服务工作者无法发送消息:错误:没有有效的客户端可发送。”。当服务工作者丢失与其父级声明的关联时,可能会发生这种情况。简而言之:好消息是消息已成功接收,坏消息是它无处可去。

另一个有用的工具是about:debugging#workers处的服务工作者调试页面。此页面显示所有已注册的服务工作者脚本,并允许您取消注册它们、选择性地发送推送事件以及调试服务工作者脚本。Mozilla Hacks 有更多关于 about:debugging页面 的详细信息。

调试 Android

调试 Android 上的推送与之类似。与桌面一样,您需要将dom.push.debug设置为true,并将dom.push.loglevel设置为debug。不幸的是,与桌面不同,没有简单的方法可以从 Android 查看浏览器控制台。消息被记录到 Android 错误日志中,因此可以使用类似<a href="https://developer.android.com.cn/studio/command-line/adb.html">adb</a> logcat的命令来跟踪错误日志。某些开发者可以使用grep仅查找包含"Gecko(Push|Console)"的记录。

遗憾的是,此视图并不那么美观,但显示了与之相同的内容,包括服务工作者的控制台消息,这可能有助于识别和解决错误。值得注意的是,adb 可能会截断非常长的消息。

adb 日志示例可能如下所示

04-24 15:18:04.432  7015  7037 I GeckoPushGCM: Cached GCM token exists: crWSbvAk4e4:APA91bHozW8bSTCrBwPerd7...
04-24 15:18:04.432  7015  7037 D GeckoPushManager: Existing uaid is fresh; no need to request from autopush endpoint.
04-24 15:18:04.488  7015  7037 I GeckoPushManager: Got chid: c0ef2... and endpoint: https://updates.push.services...
04-24 15:18:04.500  7015  7038 I GeckoPush: console.debug: PushServiceAndroidGCM:
04-24 15:18:04.520  7015  7038 I GeckoConsole: put() [object Object]
04-24 15:18:04.521  7015  7038 I Gecko   :   put()
04-24 15:18:04.522  7015  7038 I Gecko   : Object
04-24 15:18:04.522  7015  7038 I Gecko   :     - pushEndpoint = https://updates.push.services.mozilla.com/wpush/v1/...
04-24 15:18:04.522  7015  7038 I Gecko   :     - scope = https://jrconlin.github.io/Webpush_QA/
                                               ...
04-24 15:18:04.526  7015  7038 I Gecko   : console.debug: PushDB:
04-24 15:18:04.528  7015  7038 I GeckoConsole: put: Request successful. Updated record c0ef2...
04-24 15:18:04.528  7015  7038 I Gecko   :   put: Request successful. Updated record
04-24 15:18:04.528  7015  7038 I Gecko   :   c0ef2...
04-24 15:18:04.563  7015  7038 I GeckoConsole: receiverKey BNPFRn...
04-24 15:18:04.565  7015  7038 I GeckoConsole: Auth key:  0Gkt8...
04-24 15:18:04.567  7015  7038 I GeckoConsole: data: Amidst the mists and coldest frosts, I thrust my fists against ...
                                               ...
04-24 15:18:04.650  7015  7038 I GeckoConsole: echo -ne "\xa\xc6\xfb\xd3\x13\x0e\xa\xa\xa\xad\x59\xad\x71\xa\xa\...
04-24 15:18:04.652  7015  7038 I GeckoConsole: payload 191,40,74,54,29,62,188,190,133,70,86,70,120,194,173,100,62,...
04-24 15:18:04.653  7015  7038 I GeckoConsole: Fetching: https://updates.push.services.mozilla.com/wpush/v1/gAAAAA...
04-24 15:18:04.960  7015  7038 I GeckoConsole: Message sent 201
04-24 15:18:05.753  7015  9696 D GeckoPushGCM: Message received.  Processing on background thread.
04-24 15:18:05.754  7015  7037 I GeckoPushService: Google Play Services GCM message received; delivering.
                                              ...
04-24 15:18:05.774  7015  7038 I Gecko   : console.debug: PushService:
04-24 15:18:05.774  7015  7037 I GeckoPushService: Delivering dom/push message to Gecko!
04-24 15:18:05.777  7015  7038 I GeckoPush: console.debug: PushServiceAndroidGCM:
04-24 15:18:05.781  7015  7038 I GeckoPush:   ReceivedPushMessage with data
04-24 15:18:05.782  7015  7038 I GeckoPush: Object
04-24 15:18:05.782  7015  7038 I GeckoPush:     - channelID = c0ef2...
04-24 15:18:05.782  7015  7038 I GeckoPush:     - con = aesgcm
                                                ...
04-24 15:18:05.783  7015  7038 I GeckoPush: console.debug: PushServiceAndroidGCM:
04-24 15:18:05.784  7015  7038 I GeckoConsole: Delivering message to main PushService: [object ArrayBuffer] ...
                                           ...
04-24 15:18:05.786  7015  7038 I Gecko   : console.debug: PushService:
04-24 15:18:05.787  7015  7038 I GeckoConsole: receivedPushMessage()
04-24 15:18:05.787  7015  7038 I Gecko   :   receivedPushMessage()
04-24 15:18:05.788  7015  7038 I Gecko   : console.debug: PushDB:
04-24 15:18:05.789  7015  7038 I GeckoConsole: getByKeyID()
04-24 15:18:05.789  7015  7038 I Gecko   :   getByKeyID()
04-24 15:18:05.795  7015  7038 I Gecko   : console.debug: PushDB:
04-24 15:18:05.797  7015  7038 I GeckoConsole: getByKeyID: Got record [object Object]
04-24 15:18:05.810  7015  7038 I Gecko   : console.debug: PushDB:
04-24 15:18:05.811  7015  7038 I GeckoConsole: update: Update successful c0ef2...
                                               ...
04-24 15:18:05.840  7015  7038 I Gecko   : console.debug: PushService:
04-24 15:18:05.841  7015  7038 I GeckoConsole: notifyApp() https://jrconlin.github.io/Webpush_QA/
04-24 15:18:05.841  7015  7038 I Gecko   :   notifyApp()
04-24 15:18:05.841  7015  7038 I Gecko   :   https://jrconlin.github.io/Webpush_QA/
04-24 15:18:05.848  7015  7038 I GeckoConsole: **** Recv'd a push message:: {"isTrusted":true}
04-24 15:18:05.850  7015  7038 I GeckoConsole: Service worker just got: Amidst the mists and coldest frosts, I ...
04-24 15:18:05.853  7015  7038 I GeckoConsole: Service worker found clients {}
04-24 15:18:05.854  7015  7038 I GeckoConsole: Service worker sending to client... [object WindowClient]
04-24 15:18:05.857  7015  7038 I GeckoConsole: Service Worker sent: {"type":"content","content":"Amidst the mists ...
04-24 15:18:08.033   527 32517 E ResourceManagerService: Rejected removeResource call with invalid pid.
04-24 15:18:05.865  7015  7015 D GeckoToolbar: onTabChanged: LOCATION_CHANGE

请注意,您在这里获得更多信息,包括指示 GCM 接收消息并将其转发给 Gecko 的消息。许多日志消息已删除或修剪以节省发布空间,但这些消息可以帮助隔离问题是出在您的应用中还是网络中。

结论

使用 Push 很有意义,但请记住,它是一项新技术。这意味着事情可能会发生变化,并且可能存在一些需要注意的粗糙部分。与往常一样,我们感谢您发现的错误以及如何使事情变得更容易的建议。如果您有任何疑问,可以在 irc.mozilla.org 的 #push 频道中找到我们。

关于 JR Conlin

Web Push 服务器开发者,感谢您订阅猫事实。

更多 JR Conlin 的文章…


5 条评论

  1. Martin Gray

    如何在不关闭并重新启动的情况下解冻冻结的网页?

    2017 年 5 月 4 日 09:27

    1. JR Conlin

      这真的不是获取此类主题帮助的最佳场所。我建议您访问 https://support.mozilla.org/,您可以在那里找到答案、提出问题,并且许多人可以帮助您。

      2017 年 5 月 4 日 09:32

  2. Alfonso

    第一段中“系列文章”链接中缺少本文,因为它尚未归类为推送。

    2017 年 5 月 9 日 11:26

    1. Havi Hoffman [编辑]

      @Alfonso,感谢您的阅读和指出!实际上,情况稍微复杂一些——我们将关于推送的系列文章从 Mozilla Services 博客转移到了这里,因此没有单个类别 URL 可以包含所有文章!:-)

      未来面向开发者的关于推送的文章可能会出现在这里。此外,我们过去也曾介绍过推送。感谢您的建议,我们现在有了您可以关注的 Web Push 类别

      https://hacks.mozilla.ac.cn/category/web-push/

      2017 年 5 月 9 日 13:47

      1. jr conlin

        如果您有兴趣阅读我提到的较旧的 WebPush 文章,可以查看:https://blog.mozilla.org/services/category/push/

        抱歉造成混淆,谢谢!

        2017 年 5 月 9 日 13:50

本文评论已关闭。