前回の(1)はこちらから。
Perlでの実装
それでは、筆者が作成したPerl製の発信専用1ユーザーActivityPubサーバであるActubを例に、Perlでの実装について説明します。Actubは、WebフレームワークにMojoliciousを使い、Fediverseに参加するための最小限の機能を実装することを目標にしています。Actubは購読機能を持たないのでほかのユーザーをフォローしてタイムラインを読むことはできませんが、ほかのサーバのユーザーにフォローされることでFediverseに情報を発信できます。
ActivityPubはWeb APIベースのプロトコルですので、Actubも「リクエストを受け付けて、情報を加工し、レスポンスを返す」という一般的なWeb APIサーバと同様の動作をします。以降では、一般的な処理については説明せず、Actubで特徴的な処理を行っている部分について説明します。
AS2オブジェクトモデルの作成
Actubでは、ActivityPubのデータモデルであるAS2オブジェクトを表現するためのクラスを作成します。
個々のオブジェクトを表現するクラスを作成する前に、まずベースとなるクラスを作成します。
ここではClass::Tiny
モジュールを用いています。これは属性のゲッタとセッタを設定するだけのシンプルなモジュールですが、コアモジュール以外に依存のない軽量なモジュールです。これを使って属性名と同じ名前のメソッドでAS2の各属性にアクセスできるようにしたいところですが、AS2には@context
属性があり、@
はメソッド名に使えないため、この名前のままではメソッド名として使えません。この問題に対応するため、個々のクラス定義では@context
ではなくcontext
属性として定義し、JSON直列化の際にcontext
属性を@context
に変換して出力するためのTO_JSON
関数を定義しています。
この下準備により、たとえばFollow
クラスの定義は次のようにシンプルなものになっています。
MIME型の登録
GETリクエストに対する返却処理は通常のレスポンスなので実装上特筆することはあまりありませんが、前述のとおりMIME型としてapplication/ld+json; profile="https://www.w3.org/ns/activitystreams"
を使う必要があるため、あらかじめMojoliciousのstartup
フック内で次のようにしてこのMIME型を登録しておきます。
これにより、実際にレスポンスを出力するときには次のようなコードでMIME型を指定して出力できます。
送信キューの登録
Actubは送信処理を非同期で行う実装となっているので、購読アクターからFollow
オブジェクトが送信されると、対応するAccept
オブジェクトをジョブキューに追加します。ここでのジョブキューシステムはJonk
モジュールを使っています。これはデータベースをバックエンドに使う軽量なジョブキューシステムで、次のようなコードでキューに追加します。
送信処理
Actubでは、送信処理はcronで定期的に起動される別スクリプトに分離されています。本項ではその概要を示します。
データのキューからの取り出し
スクリプトが起動されると、次のようなコードでジョブキューをチェックし、キューにデータがあれば送信処理を呼び出します。
HTTP Signaturesによる署名
ActivityPubのオブジェクトを送信する際には、HTTPSignaturesによる署名が必要です。HTTP Signaturesで使用できる署名方法はいくつかありますが、Fediverseで使われているのはrsa-sha256
と呼ばれる方法です。rsa-sha256
では次の手順で署名します。
- 署名対象データをSHA256アルゴリズムでハッシュ化する
- ハッシュをRSAアルゴリズムで署名する
- 署名をBase64形式でエンコードする
HTTP Signaturesでは署名対象データも選択できますが、Actubでは仕様で最小限含めることが求められているDate
フィールドを対象としています。
通常、LWPライブラリを使ってPOST処理を行う場合は、post
メソッドに必要な引数を与える形で実装します。しかし今回はHTTPリクエストヘッダのDateフィールドの値に署名をする必要があるため、次のように手順を分割して処理を行います。
署名処理本体はCrypt::OpenSSL::RSA
モジュールを使って次のように行います。
通知を受け取ったアクターは、通知の送付元アクターのアクターオブジェクトに含まれているpublicKey
属性の値を使って、HTTP Signaturesの署名を検証します。
コンテントネゴシエーションによる情報の提供
前述のとおりActivityPubにおいてGETメソッドで情報を取得する際には、リクエストのAccept
ヘッダにapplication/ld+json; profile="https://www.w3.org/ns/activitystreams"
を指定することになっています。ただ、これ以外の値を指定された場合に、AS2以外の形式の情報を返すこと(コンテントネゴシエーション)も認められています。これを利用して、同じURLでも、ActivityPubの情報を要求された場合はAS2形式の情報を、それ以外の場合はHTML形式での情報を返すようにしています。
is_ap
関数は、リクエストで指定されたMIME型がActivityPubのものかを判定しています。ActivityPub仕様ではapplication/activity+json
が指定された場合もAS2形式の情報を返すべき(SHOULD)とされているため、ここではどちらを指定されてもActivityPubのものとして判定しています。
<続きの(3)はこちら。>
- 特集1
イミュータブルデータモデルで始める
実践データモデリング
業務の複雑さをシンプルに表現!
- 特集2
いまはじめるFlutter
iOS/Android両対応アプリを開発してみよう
- 特集3
作って学ぶWeb3
ブロックチェーン、スマートコントラクト、NFT