fix(rest): encode waku message payload in base64

This commit is contained in:
Lorenzo Delgado 2022-08-29 16:54:11 +02:00 committed by Lorenzo Delgado
parent cb3912fc64
commit 46de4fb3f6
7 changed files with 86 additions and 39 deletions

View File

@ -11,7 +11,7 @@ import
libp2p/protocols/pubsub/pubsub libp2p/protocols/pubsub/pubsub
import import
../../waku/v2/node/wakunode2, ../../waku/v2/node/wakunode2,
../../waku/v2/node/rest/[server, client, utils], ../../waku/v2/node/rest/[server, client, base64, utils],
../../waku/v2/node/rest/relay/[api_types, relay_api, topic_cache] ../../waku/v2/node/rest/relay/[api_types, relay_api, topic_cache]
@ -164,8 +164,8 @@ suite "REST API - Relay":
response.contentType == $MIMETYPE_JSON response.contentType == $MIMETYPE_JSON
response.data.len == 3 response.data.len == 3
response.data.all do (msg: RelayWakuMessage) -> bool: response.data.all do (msg: RelayWakuMessage) -> bool:
msg.payload == "TEST-1" and msg.payload == Base64String.encode("TEST-1") and
string(msg.contentTopic.get()) == "content-topic-x" and msg.contentTopic.get().string == "content-topic-x" and
msg.version.get() == Natural(1) and msg.version.get() == Natural(1) and
msg.timestamp.get() == int64(2022) msg.timestamp.get() == int64(2022)
@ -211,7 +211,7 @@ suite "REST API - Relay":
discard await client.relayPostSubscriptionsV1(newTopics) discard await client.relayPostSubscriptionsV1(newTopics)
let response = await client.relayPostMessagesV1(defaultTopic, RelayWakuMessage( let response = await client.relayPostMessagesV1(defaultTopic, RelayWakuMessage(
payload: "TEST-PAYLOAD", payload: Base64String.encode("TEST-PAYLOAD"),
contentTopic: some(ContentTopicString(defaultContentTopic)), contentTopic: some(ContentTopicString(defaultContentTopic)),
timestamp: some(int64(2022)) timestamp: some(int64(2022))
)) ))

View File

@ -1,12 +1,13 @@
{.used.} {.used.}
import std/typetraits import
import chronicles,
unittest2,
stew/[results, byteutils], stew/[results, byteutils],
chronicles,
unittest2,
json_serialization json_serialization
import import
../../waku/v2/node/rest/serdes, ../../waku/v2/node/rest/serdes,
../../waku/v2/node/rest/base64,
../../waku/v2/node/rest/relay/api_types ../../waku/v2/node/rest/relay/api_types
@ -15,25 +16,27 @@ suite "Relay API - serialization":
suite "RelayWakuMessage - decode": suite "RelayWakuMessage - decode":
test "optional fields are not provided": test "optional fields are not provided":
# Given # Given
let jsonBytes = toBytes("""{ "payload": "MESSAGE" }""") let payload = Base64String.encode("MESSAGE")
let jsonBytes = toBytes("{\"payload\":\"" & $payload & "\"}")
# When # When
let res = decodeFromJsonBytes(RelayWakuMessage, jsonBytes, requireAllFields = true) let res = decodeFromJsonBytes(RelayWakuMessage, jsonBytes, requireAllFields = true)
# Then # Then
require(res.isOk) require(res.isOk())
let value = res.get() let value = res.get()
check: check:
value.payload == "MESSAGE" value.payload == payload
value.contentTopic.isNone value.contentTopic.isNone()
value.version.isNone value.version.isNone()
value.timestamp.isNone value.timestamp.isNone()
suite "RelayWakuMessage - encode": suite "RelayWakuMessage - encode":
test "optional fields are none": test "optional fields are none":
# Given # Given
let payload = Base64String.encode("MESSAGE")
let data = RelayWakuMessage( let data = RelayWakuMessage(
payload: "MESSAGE", payload: payload,
contentTopic: none(ContentTopicString), contentTopic: none(ContentTopicString),
version: none(Natural), version: none(Natural),
timestamp: none(int64) timestamp: none(int64)
@ -43,7 +46,7 @@ suite "Relay API - serialization":
let res = encodeIntoJsonBytes(data) let res = encodeIntoJsonBytes(data)
# Then # Then
require(res.isOk) require(res.isOk())
let value = res.get() let value = res.get()
check: check:
value == toBytes("""{"payload":"MESSAGE"}""") value == toBytes("{\"payload\":\"" & $payload & "\"}")

View File

@ -0,0 +1,27 @@
{.push raises: [Defect].}
import stew/[results, byteutils, base64]
type Base64String* = distinct string
proc encode*(t: type Base64String, value: string|seq[byte]): Base64String =
let val = block:
when value is string:
toBytes(value)
else:
value
Base64String(base64.encode(Base64, val))
proc decode*(t: Base64String): Result[seq[byte], cstring] =
try:
ok(base64.decode(Base64, string(t)))
except:
err("failed to decode base64 string")
proc `$`*(t: Base64String): string {.inline.}=
string(t)
proc `==`*(lhs: Base64String|string, rhs: Base64String|string): bool {.inline.}=
string(lhs) == string(rhs)

View File

@ -1,24 +1,25 @@
{.push raises: [ Defect ].} {.push raises: [Defect].}
import import
std/[sets, strformat], std/[sets, strformat],
stew/byteutils,
chronicles, chronicles,
json_serialization, json_serialization,
json_serialization/std/options, json_serialization/std/options,
presto/[route, client, common] presto/[route, client, common]
import ".."/serdes import
import ../../wakunode2 ../../../protocol/waku_message,
../serdes,
../base64
#### Types #### Types
type type
PubSubTopicString* = distinct string PubSubTopicString* = distinct string
ContentTopicString* = distinct string ContentTopicString* = distinct string
type RelayWakuMessage* = object type RelayWakuMessage* = object
payload*: string payload*: Base64String
contentTopic*: Option[ContentTopicString] contentTopic*: Option[ContentTopicString]
version*: Option[Natural] version*: Option[Natural]
timestamp*: Option[int64] timestamp*: Option[int64]
@ -37,23 +38,29 @@ type
proc toRelayWakuMessage*(msg: WakuMessage): RelayWakuMessage = proc toRelayWakuMessage*(msg: WakuMessage): RelayWakuMessage =
RelayWakuMessage( RelayWakuMessage(
payload: string.fromBytes(msg.payload), payload: base64.encode(Base64String, msg.payload),
contentTopic: some(ContentTopicString(msg.contentTopic)), contentTopic: some(ContentTopicString(msg.contentTopic)),
version: some(Natural(msg.version)), version: some(Natural(msg.version)),
timestamp: some(msg.timestamp) timestamp: some(msg.timestamp)
) )
proc toWakuMessage*(msg: RelayWakuMessage, version = 0): WakuMessage = proc toWakuMessage*(msg: RelayWakuMessage, version = 0): Result[WakuMessage, cstring] =
const defaultContentTopic = ContentTopicString("/waku/2/default-content/proto") const defaultContentTopic = ContentTopicString("/waku/2/default-content/proto")
WakuMessage( let
payload: msg.payload.toBytes(), payload = ?msg.payload.decode()
contentTopic: ContentTopic(msg.contentTopic.get(defaultContentTopic)), contentTopic = ContentTopic(msg.contentTopic.get(defaultContentTopic))
version: uint32(msg.version.get(version)), version = uint32(msg.version.get(version))
timestamp: msg.timestamp.get(0) timestamp = msg.timestamp.get(0)
)
ok(WakuMessage(payload: payload, contentTopic: contentTopic, version: version, timestamp: timestamp))
#### Serialization and deserialization #### Serialization and deserialization
proc writeValue*(writer: var JsonWriter[RestJson], value: Base64String)
{.raises: [IOError, Defect].} =
writer.writeValue(string(value))
proc writeValue*(writer: var JsonWriter[RestJson], value: PubSubTopicString) proc writeValue*(writer: var JsonWriter[RestJson], value: PubSubTopicString)
{.raises: [IOError, Defect].} = {.raises: [IOError, Defect].} =
writer.writeValue(string(value)) writer.writeValue(string(value))
@ -74,6 +81,10 @@ proc writeValue*(writer: var JsonWriter[RestJson], value: RelayWakuMessage)
writer.writeField("timestamp", value.timestamp) writer.writeField("timestamp", value.timestamp)
writer.endRecord() writer.endRecord()
proc readValue*(reader: var JsonReader[RestJson], value: var Base64String)
{.raises: [SerializationError, IOError, Defect].} =
value = Base64String(reader.readValue(string))
proc readValue*(reader: var JsonReader[RestJson], value: var PubSubTopicString) proc readValue*(reader: var JsonReader[RestJson], value: var PubSubTopicString)
{.raises: [SerializationError, IOError, Defect].} = {.raises: [SerializationError, IOError, Defect].} =
value = PubSubTopicString(reader.readValue(string)) value = PubSubTopicString(reader.readValue(string))
@ -85,7 +96,7 @@ proc readValue*(reader: var JsonReader[RestJson], value: var ContentTopicString)
proc readValue*(reader: var JsonReader[RestJson], value: var RelayWakuMessage) proc readValue*(reader: var JsonReader[RestJson], value: var RelayWakuMessage)
{.raises: [SerializationError, IOError, Defect].} = {.raises: [SerializationError, IOError, Defect].} =
var var
payload = none(string) payload = none(Base64String)
contentTopic = none(ContentTopicString) contentTopic = none(ContentTopicString)
version = none(Natural) version = none(Natural)
timestamp = none(int64) timestamp = none(int64)
@ -100,7 +111,7 @@ proc readValue*(reader: var JsonReader[RestJson], value: var RelayWakuMessage)
case fieldName case fieldName
of "payload": of "payload":
payload = some(reader.readValue(string)) payload = some(reader.readValue(Base64String))
of "contentTopic": of "contentTopic":
contentTopic = some(reader.readValue(ContentTopicString)) contentTopic = some(reader.readValue(ContentTopicString))
of "version": of "version":

View File

@ -7,9 +7,12 @@ import
json_serialization, json_serialization,
json_serialization/std/options, json_serialization/std/options,
presto/[route, client, common] presto/[route, client, common]
import ".."/[serdes, utils] import
import ../../wakunode2 ../../wakunode2,
import "."/[api_types, topic_cache] ../serdes,
../utils,
./api_types,
./topic_cache
logScope: topics = "rest_api_relay" logScope: topics = "rest_api_relay"
@ -130,9 +133,11 @@ proc installRelayPostMessagesV1Handler*(router: var RestRouter, node: WakuNode)
if reqResult.isErr(): if reqResult.isErr():
return RestApiResponse.badRequest() return RestApiResponse.badRequest()
let message: RelayPostMessagesRequest = reqResult.get() let resMessage = reqResult.value.toWakuMessage(version = 0)
if resMessage.isErr():
return RestApiResponse.badRequest()
if not (waitFor node.publish(pubSubTopic, message.toWakuMessage(version = 0)).withTimeout(futTimeout)): if not (waitFor node.publish(pubSubTopic, resMessage.value).withTimeout(futTimeout)):
error "Failed to publish message to topic", topic=pubSubTopic error "Failed to publish message to topic", topic=pubSubTopic
return RestApiResponse.internalServerError() return RestApiResponse.internalServerError()

View File

@ -118,6 +118,7 @@ components:
properties: properties:
payload: payload:
type: string type: string
format: byte
contentTopic: contentTopic:
$ref: '#/components/schemas/ContentTopic' $ref: '#/components/schemas/ContentTopic'
version: version:

View File

@ -1,7 +1,7 @@
{.push raises: [Defect].} {.push raises: [Defect].}
import std/typetraits
import import
std/typetraits,
stew/results, stew/results,
chronicles, chronicles,
serialization, serialization,