diff --git a/README_zh.md b/README_zh.md index 6a1c6e9c906f5d65046a3f13c983c4208c9369e8..8b76775286076888bbeab7f1465f51063fe943bc 100644 --- a/README_zh.md +++ b/README_zh.md @@ -16,12 +16,20 @@ ohos_coap是基于libcoap v4.3.1版本,封装napi接口,给上层ts提供coa - 不支持tls/dtls的消息 - 不支持发起 delete请求 +目前服务端实现的能力: + +-支持coap服务端处理get,post数据 + ## 使用本工程 + 有两种方式可以下载本工程: + 1. 开发者如果想要使用本工程,可以使用git命令 + ``` git clone https://gitee.com/openharmony-tpc/ohos_coap.git --recurse-submodules ``` + 2. 点击下载按钮,把本工程下到本地,再把libcoap v4.3.1版本代码放入libcoap/src/cpp/libcoapSource目录下,这样才可以编译通过。 3. 漏洞修复,进入到libcoap\src\main\cpp\thirdModule 目录下,执行 modify.sh 脚本,将本目录下的 patch 文件合入到 libcoap 源码中。 @@ -85,6 +93,40 @@ coapPut.then((data) => { } }) ``` + +## 服务端使用 + +### 服务器注册get,post请求 + +``` +let server = new CoapServer(); +let resource = new CoapResource(); +let exchange = new CoapExchange(); +//服务器本地IP,端口号目前固定 +server.setAddr('125.0.10.5', '0'); +// 打开native log开关 +CoapClient.setNativeLogOpen(true); +//添加id +let resourceId = this.resource.createResource('test'); +//添加资源属性 +resource.addAttributes(resourceId, 'title', 'Dynamic'); +//处理get 数据 +resource.registerGetHandler(resourceId, (responseId: string) => { + let getQuesOptions = exchange.getRequestOptions(responseId); + exchange.response(responseId, CoapResourceCode.COAP_RESPONSE_CODE_CONTENT, 'hello arkts'); +}); +//处理post 数据 +resource.registerPostHandler(resourceId, (responseId: string) => { +let postQuesOptions = exchange.getRequestOptions(responseId); +let postQuesText = exchange.getRequestText(responseId); + exchange.response(responseId, CoapResourceCode.COAP_RESPONSE_CODE_CONTENT, 'hello arkts'); +}); +//添加资源 +server.addResource(resourceId); +//coap server 启动服务器 +server.start(); +``` + ## 接口说明 | 方法名 | 入参 | 接口描述 | @@ -98,11 +140,31 @@ coapPut.then((data) => { | setPayload(payload: string) | payload: string | 设置客服端透传的内容 | | setToken(token: Uint8Array) | token: Uint8Array | 设置token | | setNativeLogOpen(isOpen: boolean) | isOpen: boolean | 是否打开native侧的log | -| addOption(optionNum: number, value: string) | optionNum: number, value: string | 客户端自定义新增一个option选项 | -| setMid(mid: number) | mid: number | 客户端自定义报文的message id | -| setPayloadBinary(payload: Uint8Array) | payload: Uint8Array | 客户端透传的字节内容 +| addOption(optionNum: number, value: string) | optionNum: number, value: string | 客户端自定义新增一个option选项 | +| setMid(mid: number) | mid: number | 客户端自定义报文的message id | +| setPayloadBinary(payload: Uint8Array) | payload: Uint8Array | 客户端透传的字节内容 单元测试用例详情见[TEST.md](https://gitee.com/openharmony-tpc/ohos_coap/blob/master/TEST.md) + +## 接口说明 + +| 方法名 | 入参 | 接口描述 | +|-----------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------|--------------| +| start(classId: number) | classId: number | 启动服务器 | + stop(classId: number) | classId: number | 关闭服务器 | +| addResource(resourceID: string) | resourceID: string | 添加资源 | +| setAddr(ip: string, port: string) | ip: string, port: string | 设置服务IP和端口 | +| getStatus(classId: number) | classId: number | 判断是否启动状态码 | +| registerGetHandler(resourceID: string, cb: Function) | resourceID: string, cb: Function | 添加get请求处理器 | +| registerPostHandler(resourceID: string, cb: Function) | resourceID: string, cb: Function | 添加post请求处理器 | +| addAttributes: (resourceId: string, attributesOptionOne: string, attributesOptionTwo: string) | (resourceId: string, attributesOptionOne: string, attributesOptionTwo: string) | 添加资源属性 | +| createResource: (resourceId: string) | resourceId: string | 添加Id | +| destroy() | | 注销资源 | +| response: (responseId: string, code: number, data: string) | (responseId: string, code: number, data: string) | 发送响应到客户端 | +| getRequestOptions: (responseId: string) | responseId: string | 获取请求选项 +| getRequestText: (responseId: string) | responseId: string | 获取请求负载 | +| getSourceAddress: (responseId: string) | responseId: string | 发起请求的对端IP和接口 | + ## 约束与限制 在下述版本验证通过: diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets index cc1c51e759cbd70c8ca6d265adfffdb9fec1f33a..c54003ac6107488c981eeef4b5de049a2c694784 100644 --- a/entry/src/main/ets/pages/Index.ets +++ b/entry/src/main/ets/pages/Index.ets @@ -12,189 +12,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { CoapClient, CoapRequestMethod, CoapRequestType, CoapResponseCode, ContentFormat } from '@ohos/coap'; -import connection from '@ohos.net.connection'; -import cryptoFramework from '@ohos.security.cryptoFramework'; -const CHARS_PER_BYTE: number = 2; -const BITS_PER_CHAR: number = 4; -const MAX_FOUR_BIT_VALUE: number = 0x0F; -const MAX_BYTE_VALUE: number = 0xFF; -const HEX_ARRAY: Array = '0123456789abcdef'.split(""); - -function uint8ArrayToHexString(bytes: Uint8Array): string { - if (!bytes || bytes.length == 0) { - return ""; - } - let hexChars: Array = new Array(bytes.length + bytes.length); - for (let index = 0; index < bytes.length; index++) { - let value: number = bytes[index] & MAX_BYTE_VALUE; - // byte的高4bit - hexChars[index * CHARS_PER_BYTE] = HEX_ARRAY[value >>> BITS_PER_CHAR]; - // byte的低4bit - hexChars[index * CHARS_PER_BYTE + 1] = HEX_ARRAY[value & MAX_FOUR_BIT_VALUE]; - } - return hexChars.join(""); -} -interface OptionType { - optionNum: number, - value: string -} +import router from '@ohos.router'; @Entry @Component struct Index { - @State coapGet: string = 'coapGet'; - @State getIp: string = 'getIp'; - @State coapPut: string = 'coapPut_token'; - @State coapDelete: string = 'coapDelete'; - @State coapUri: string = ''; - @State option: OptionType = { - optionNum: 11, - value: 'this. is a message' - }; - build() { Row() { Column() { - TextInput().fontSize(18).fontWeight(FontWeight.Bold).onChange((value) => { - this.coapUri = value; - }); - - Text(this.coapGet) + Text("跳转页面server") .fontSize(50) .fontWeight(FontWeight.Bold) + .margin(10) .onClick(() => { - CoapClient.setNativeLogOpen(true); - let coapClient = new CoapClient(); - let coapGet = coapClient.request(this.coapUri, CoapRequestMethod.GET, CoapRequestType.COAP_MESSAGE_CON); - console.log("libcoap get test"); - coapGet.then((data) => { - if (data.code == CoapResponseCode.SUCCESS) { - console.log("libcoap get:" + data.message[0]); - } else { - console.log("libcoap get code:" + data.code); - console.log("libcoap get error message:" + data.message[0]); - } - }) - }); - - Text('添加Option') - .fontSize(30) - .fontWeight(FontWeight.Bold) - .onClick(async () => { - let coapClient = new CoapClient(); - coapClient.setPort(5683); - coapClient.setFormat(ContentFormat.PLAIN); - coapClient.addOption(this.option.optionNum, this.option.value); - coapClient.setWaitSecond(3); - let coapPut = coapClient.request(this.coapUri, CoapRequestMethod.PUT, CoapRequestType.COAP_MESSAGE_CON); - coapPut.then((data) => { - if (data.code == CoapResponseCode.SUCCESS) { - console.log("libcoap put message:" + data.message[0]); - } - }) - }); - - Text('发送报文messageId') - .fontSize(30) - .fontWeight(FontWeight.Bold) - .onClick(async () => { - let coapClient = new CoapClient(); - coapClient.setPort(5683); - coapClient.setFormat(ContentFormat.PLAIN); - coapClient.setMid(0xffff); - coapClient.setWaitSecond(3); - let coapPut = coapClient.request(this.coapUri, CoapRequestMethod.PUT, CoapRequestType.COAP_MESSAGE_CON); - coapPut.then((data) => { - if (data.code == CoapResponseCode.SUCCESS) { - console.log("libcoap put message:" + data.message[0]); - } - }) - }); - - Text('发送字节的payload') - .fontSize(30) - .fontWeight(FontWeight.Bold) - .onClick(async () => { - let coapClient = new CoapClient(); - coapClient.setPort(5683); - coapClient.setFormat(ContentFormat.PLAIN); - let buffer = new Uint8Array(10); - coapClient.setPayloadBinary(buffer); - coapClient.setPayload("22") - coapClient.setWaitSecond(3); - let coapPut = coapClient.request(this.coapUri, CoapRequestMethod.PUT, CoapRequestType.COAP_MESSAGE_CON); - coapPut.then((data) => { - if (data.code == CoapResponseCode.SUCCESS) { - console.log("libcoap put message:" + data.message[0]); - } - }) - }); - - Text('发送注册信息') - .fontSize(30) - .fontWeight(FontWeight.Bold) - .onClick(async () => { - let coapClient = new CoapClient(); - coapClient.setPort(5683); - coapClient.setFormat(ContentFormat.PLAIN); - coapClient.registerOption(2051); - coapClient.setWaitSecond(3); - let coapPut = coapClient.request(this.coapUri, CoapRequestMethod.PUT, CoapRequestType.COAP_MESSAGE_CON); - coapPut.then((data) => { - if (data.code == CoapResponseCode.SUCCESS) { - console.log("libcoap put message:" + data.message[0]); - } - }) - }); - - Text(this.coapPut) - .fontSize(50) - .fontWeight(FontWeight.Bold) - .onClick(async () => { - let coapClient = new CoapClient(); - coapClient.setPort(5683); - coapClient.setFormat(ContentFormat.PLAIN); - //payload test - coapClient.setPayload("22"); - coapClient.setWaitSecond(3); - let rand: cryptoFramework.Random = cryptoFramework.createRandom(); - let bytesBlob: cryptoFramework.DataBlob = await rand.generateRandom(8); - console.log("libcoap bytes:" + bytesBlob.data.join()); - let tokenStr: string = uint8ArrayToHexString(bytesBlob.data); - console.log("libcoap token:" + tokenStr); - coapClient.setToken(bytesBlob.data); - let coapPut = coapClient.request(this.coapUri, CoapRequestMethod.PUT, CoapRequestType.COAP_MESSAGE_CON); - coapPut.then((data) => { - if (data.code == CoapResponseCode.SUCCESS) { - console.log("libcoap put message:" + data.message[0]); - } - }) - }); - - Text(this.getIp) + router.pushUrl({ url: "pages/server" }) + }) + Text("跳转页面client") .fontSize(50) .fontWeight(FontWeight.Bold) + .margin(10) .onClick(() => { - let that = this; - connection.getDefaultNet( (error, netHandle)=> { - connection.getConnectionProperties(netHandle).then( (info)=> { - if (info && info.linkAddresses.length > 0) { - for (let i = 0; i < info.linkAddresses.length; i++) { - if (info.linkAddresses[i].address.address.includes(".")) { - that.getIp = info.linkAddresses[i].address.address; - break; - } - } - } - }) - }); - }); + router.pushUrl({ url: "pages/client" }) + }) + .width('100%') } - - .width('100%') + .height('100%') } - .height('100%') } } \ No newline at end of file diff --git a/entry/src/main/ets/pages/client.ets b/entry/src/main/ets/pages/client.ets new file mode 100644 index 0000000000000000000000000000000000000000..59536d557cd8c48d65cb8a94ae06e43691aa5a81 --- /dev/null +++ b/entry/src/main/ets/pages/client.ets @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CoapClient, CoapRequestMethod, CoapRequestType, CoapResponseCode, ContentFormat } from '@ohos/coap'; +import cryptoFramework from '@ohos.security.cryptoFramework'; + +const CHARS_PER_BYTE: number = 2; +const BITS_PER_CHAR: number = 4; +const MAX_FOUR_BIT_VALUE: number = 0x0F; +const MAX_BYTE_VALUE: number = 0xFF; +const HEX_ARRAY: Array = '0123456789abcdef'.split(""); + +function uint8ArrayToHexString(bytes: Uint8Array): string { + if (!bytes || bytes.length == 0) { + return ""; + } + let hexChars: Array = new Array(bytes.length + bytes.length); + for (let index = 0; index < bytes.length; index++) { + let value: number = bytes[index] & MAX_BYTE_VALUE; + // byte的高4bit + hexChars[index * CHARS_PER_BYTE] = HEX_ARRAY[value >>> BITS_PER_CHAR]; + // byte的低4bit + hexChars[index * CHARS_PER_BYTE + 1] = HEX_ARRAY[value & MAX_FOUR_BIT_VALUE]; + } + return hexChars.join(""); +} + +interface OptionType { + optionNum: number, + value: string +} + +@Entry +@Component +struct Client { + @State coapUri: string = ''; + @State coapGet: string = 'coapGet'; + @State coapPut: string = 'coapPut_token'; + @State coapDelete: string = 'coapDelete'; + @State getData: string = ''; + @State postData: string = ''; + @State customPopup: boolean = false; + @State option: OptionType = { + optionNum: 11, + value: 'this. is a message' + }; + + @Builder + popupBuilder() { + Row() { + Text('接收成功') + .fontSize(15) + } + } + + build() { + Row() { + Column() { + TextInput() + .fontSize(18) + .fontWeight(FontWeight.Bold) + .onChange((value) => { + this.coapUri = value; + }); + + Text(this.coapGet) + .fontSize(50) + .fontWeight(FontWeight.Bold) + .onClick(() => { + CoapClient.setNativeLogOpen(true); + let coapClient = new CoapClient(); + let coapGet = coapClient.request(this.coapUri, CoapRequestMethod.GET, CoapRequestType.COAP_MESSAGE_CON); + console.log("libcoap get test"); + coapGet.then((data) => { + if (data.code == CoapResponseCode.SUCCESS) { + console.log("libcoap get:" + data.message[0]); + this.getData = data.message[0] + } else { + console.log("libcoap get code:" + data.code); + console.log("libcoap get error message:" + data.message[0]); + } + }) + if (this.getData) { + this.customPopup = !this.customPopup; + } + }) + .bindPopup(this.customPopup, { + builder: this.popupBuilder + }) + Text('coapPost') + .fontSize(50) + .fontWeight(FontWeight.Bold) + .onClick(() => { + CoapClient.setNativeLogOpen(true); + let coapClient = new CoapClient(); + let coapPost = coapClient.request(this.coapUri, CoapRequestMethod.POST, CoapRequestType.COAP_MESSAGE_CON); + console.log("libcoap post test"); + coapPost.then((data) => { + if (data.code == CoapResponseCode.SUCCESS) { + console.log("libcoap post:" + data.message[0]); + this.postData = data.message[0]; + } else { + console.log("libcoap post code:" + data.code); + console.log("libcoap post error message:" + data.message[0]); + } + }) + if (this.postData) { + this.customPopup = !this.customPopup; + } + }) + .bindPopup(this.customPopup, { + builder: this.popupBuilder + }) + Text('添加Option') + .fontSize(30) + .fontWeight(FontWeight.Bold) + .onClick(async () => { + let coapClient = new CoapClient(); + coapClient.setPort(5683); + coapClient.setFormat(ContentFormat.PLAIN); + coapClient.addOption(this.option.optionNum, this.option.value); + coapClient.setWaitSecond(3); + let coapPut = coapClient.request(this.coapUri, CoapRequestMethod.PUT, CoapRequestType.COAP_MESSAGE_CON); + coapPut.then((data) => { + if (data.code == CoapResponseCode.SUCCESS) { + console.log("libcoap put message:" + data.message[0]); + } + }) + }); + Text('发送报文messageId') + .fontSize(30) + .fontWeight(FontWeight.Bold) + .onClick(async () => { + let coapClient = new CoapClient(); + coapClient.setPort(5683); + coapClient.setFormat(ContentFormat.PLAIN); + coapClient.setMid(0xffff); + coapClient.setWaitSecond(3); + let coapPut = coapClient.request(this.coapUri, CoapRequestMethod.PUT, CoapRequestType.COAP_MESSAGE_CON); + coapPut.then((data) => { + if (data.code == CoapResponseCode.SUCCESS) { + console.log("libcoap put message:" + data.message[0]); + } + }) + }); + + Text('发送字节的payload') + .fontSize(30) + .fontWeight(FontWeight.Bold) + .onClick(async () => { + let coapClient = new CoapClient(); + coapClient.setPort(5683); + coapClient.setFormat(ContentFormat.PLAIN); + let buffer = new Uint8Array(10); + coapClient.setPayloadBinary(buffer); + coapClient.setPayload("22") + coapClient.setWaitSecond(3); + let coapPut = coapClient.request(this.coapUri, CoapRequestMethod.PUT, CoapRequestType.COAP_MESSAGE_CON); + coapPut.then((data) => { + if (data.code == CoapResponseCode.SUCCESS) { + console.log("libcoap put message:" + data.message[0]); + } + }) + }); + Text('发送注册信息') + .fontSize(30) + .fontWeight(FontWeight.Bold) + .onClick(async () => { + let coapClient = new CoapClient(); + coapClient.setPort(5683); + coapClient.setFormat(ContentFormat.PLAIN); + coapClient.registerOption(2051); + coapClient.setWaitSecond(3); + let coapPut = coapClient.request(this.coapUri, CoapRequestMethod.PUT, CoapRequestType.COAP_MESSAGE_CON); + coapPut.then((data) => { + if (data.code == CoapResponseCode.SUCCESS) { + console.log("libcoap put message:" + data.message[0]); + } + }) + }); + + Text(this.coapPut) + .fontSize(50) + .fontWeight(FontWeight.Bold) + .onClick(async () => { + let coapClient = new CoapClient(); + coapClient.setPort(5683); + coapClient.setFormat(ContentFormat.PLAIN); + //payload test + coapClient.setPayload("22"); + coapClient.setWaitSecond(3); + let rand: cryptoFramework.Random = cryptoFramework.createRandom(); + let bytesBlob: cryptoFramework.DataBlob = await rand.generateRandom(8); + console.log("libcoap bytes:" + bytesBlob.data.join()); + let tokenStr: string = uint8ArrayToHexString(bytesBlob.data); + console.log("libcoap token:" + tokenStr); + coapClient.setToken(bytesBlob.data); + let coapPut = coapClient.request(this.coapUri, CoapRequestMethod.PUT, CoapRequestType.COAP_MESSAGE_CON); + coapPut.then((data) => { + if (data.code == CoapResponseCode.SUCCESS) { + console.log("libcoap put message:" + data.message[0]); + } + }) + }); + } + .width('100%') + } + .height('100%') + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/server.ets b/entry/src/main/ets/pages/server.ets new file mode 100644 index 0000000000000000000000000000000000000000..759d660502238b93598844098123f561855f5f92 --- /dev/null +++ b/entry/src/main/ets/pages/server.ets @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CoapServer, CoapClient, CoapResource, CoapExchange, CoapResourceCode } from '@ohos/coap'; +import connection from '@ohos.net.connection'; + +@Entry +@Component +struct Server { + @State resourceId: string = ''; + @State getIp: string = 'getIp'; + @State isCouldStart: boolean = false; + @State isCouldStop: boolean = false; + @State startMessage: string = ''; + @State stopMessage: string = ''; + @State getQuesOptions: string = ''; + @State postQuesOptions: string = ''; + @State postQuesText: string = ''; + @State getStatus: number = 0; + //启动server与关闭server是同一个server对象 + private server: CoapServer = new CoapServer(); + private resource: CoapResource = new CoapResource(); + private timer: number | undefined = undefined; + + build() { + Row() { + Column() { + Text(this.getIp) + .fontSize(50) + .fontWeight(FontWeight.Bold) + .onClick(() => { + if (this.getIp !== 'getIp') { + return; + } + let that = this; + connection.getDefaultNet((error, netHandle) => { + connection.getConnectionProperties(netHandle).then((info) => { + if (info && info.linkAddresses.length > 0) { + for (let i = 0; i < info.linkAddresses.length; i++) { + if (info.linkAddresses[i].address.address.includes(".")) { + that.getIp = info.linkAddresses[i].address.address; + this.isCouldStart = true; + break; + } + + } + } + }) + }) + }) + Button('startServer') + .fontSize(50) + .fontWeight(FontWeight.Bold) + .enabled(this.getStatus === 0 && this.isCouldStart) + .onClick(() => { + try { + let exchange = new CoapExchange(); + //服务器本地IP,端口号目前固定 + this.server.setAddr(this.getIp, '0'); + console.log('ohos_coap server getIp' + this.getIp); + CoapClient.setNativeLogOpen(true); + let resourceId = this.resource.createResource('test'); + this.resource.addAttributes(resourceId, 'title', 'Dynamic'); + this.resource.registerGetHandler(resourceId, (responseId: string) => { + this.getQuesOptions = exchange.getRequestOptions(responseId); + console.log('ohos_coap server get question options:==' + this.getQuesOptions); + exchange.response(responseId, CoapResourceCode.COAP_RESPONSE_CODE_CONTENT, 'hello arkts'); + }); + this.resource.registerPostHandler(resourceId, (responseId: string) => { + this.postQuesOptions = exchange.getRequestOptions(responseId); + console.log('ohos_coap server post question options:==' + this.postQuesOptions); + this.postQuesText = exchange.getRequestText(responseId); + console.log('ohos_coap server post question reqText: ==' + this.postQuesText) + exchange.response(responseId, CoapResourceCode.COAP_RESPONSE_CODE_CONTENT, 'hello arkts'); + }); + this.server.addResource(resourceId); + this.server.start(); + this.isCouldStart = false; + this.startMessage = "启动中......"; + this.getServerStatus(() => { + if (this.getStatus === 1) { + this.isCouldStop = true; + this.startMessage = "启动成功"; + clearInterval(this.timer); + } + }) + } catch (err) { + this.startMessage = '启动失败'; + this.isCouldStart = true; + console.error('start server fail: ' + err); + } + }) + Text(this.startMessage) + .fontSize(24) + .fontWeight(FontWeight.Bold) + Divider() + .strokeWidth(2) + .color('#007DFF') + Text('post question options: ' + this.postQuesOptions) + .fontSize(24) + .fontWeight(FontWeight.Bold) + Divider() + .strokeWidth(2) + .color('#007DFF') + Text('post question quesText: ' + this.postQuesText) + .fontSize(24) + .fontWeight(FontWeight.Bold) + Divider() + .strokeWidth(2) + .color('#007DFF') + Text('get question getQuesOptions: ' + this.getQuesOptions) + .fontSize(24) + .fontWeight(FontWeight.Bold) + Divider() + .strokeWidth(2) + .color('#007DFF') + Button('stopServer') + .fontSize(50) + .fontWeight(FontWeight.Bold) + .enabled(this.getStatus === 1 && this.isCouldStop) + .onClick(() => { + this.stopServer(); + this.isCouldStart = true; + }) + Text(this.stopMessage) + .fontSize(24) + .fontWeight(FontWeight.Bold) + } + .width('100%') + } + .height('100%') + } + + aboutToDisappear(): void { + this.stopServer(); + clearTimeout(this.timer); + } + + private stopServer() { + this.getQuesOptions = ''; + this.postQuesOptions = ''; + this.postQuesText = ''; + try { + CoapClient.setNativeLogOpen(true); + // stop 前调用释放资源 resource.destroy(); + this.resource.destroy(); + this.server.stop(); + this.stopMessage = '关闭中......'; + this.startMessage = ''; + this.isCouldStop = false; + this.getServerStatus(() => { + if (this.getStatus === 0) { + this.stopMessage = "关闭成功"; + clearInterval(this.timer); + } + }); + } catch (err) { + this.stopMessage = '关闭失败'; + console.error('stop server fail:' + err); + } + } + + private getServerStatus(callBack: Function) { + this.timer = setInterval(() => { + this.getStatus = this.server.getStatus(); + console.log('ohos_coap server status: ' + this.getStatus); + callBack(); + }, 50) + } +} \ No newline at end of file diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json index 1898d94f58d6128ab712be2c68acc7c98e9ab9ce..6fd068d2f722d308f545ed71d2c86e9759616659 100644 --- a/entry/src/main/resources/base/profile/main_pages.json +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -1,5 +1,7 @@ { "src": [ - "pages/Index" + "pages/Index", + "pages/client", + "pages/server" ] } diff --git a/entry/src/ohosTest/ets/test/CoapServe.test.ets b/entry/src/ohosTest/ets/test/CoapServe.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..3019fa5d82c6f4347a7af49d10824e03a731f16e --- /dev/null +++ b/entry/src/ohosTest/ets/test/CoapServe.test.ets @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { describe, it, expect } from '@ohos/hypium'; +import { CoapClient, CoapResource, CoapServer, CoapExchange, CoapResourceCode } from '@ohos/coap'; + +export default function abilityTest() { + describe('coapServeTest', () => { + it('coapServeResourceTest', 0, async () => { + const resource = new CoapResource(); + CoapClient.setNativeLogOpen(true); + const resourceId = resource.createResource('test'); + resource.addAttributes(resourceId, 'title', 'Dynamic'); + console.log('coapServeTest------->coapServeResourceTest' + resourceId); + expect(resourceId).not().assertUndefined(); + }) + it('coapServeResisterGetTest', 0, async () => { + const resourceGet = true; + const exchange = new CoapExchange(); + const resource = new CoapResource(); + CoapClient.setNativeLogOpen(true); + const resourceId = resource.createResource('test'); + resource.addAttributes(resourceId, 'title', 'Dynamic'); + resource.registerGetHandler(resourceId, (responseId: string, str: number) => { + console.log('KAMI responseId' + responseId + '+' + str); + exchange.response(responseId, CoapResourceCode.COAP_RESPONSE_CODE_CONTENT, 'get request'); + console.log('getRequestOptions' + exchange.getRequestOptions(responseId)); + }) + console.log('coapServeTest------->coapServeResisterGetTest success'); + expect(resourceGet).assertTrue(); + }) + it('coapServeResisterPostTest', 0, async () => { + const resourcePost = true; + const exchange = new CoapExchange(); + const resource = new CoapResource(); + CoapClient.setNativeLogOpen(true); + const resourceId = resource.createResource('test'); + resource.addAttributes(resourceId, 'title', 'Dynamic'); + resource.registerPostHandler(resourceId, (responseId: string, str: number) => { + console.log('KAMI responseId' + responseId + '+' + str); + exchange.response(responseId, CoapResourceCode.COAP_RESPONSE_CODE_CONTENT, 'post request'); + console.log('getRequestOptions' + exchange.getRequestOptions(responseId)); + }) + console.log('coapServeTest------->coapServeResisterPostTest success'); + expect(resourcePost).assertTrue(); + }) + it('coapServeStartTest', 0, async () => { + const serveStartTest = true; + const server = new CoapServer(); + server.setAddr('10.0.2.15', '0'); + CoapClient.setNativeLogOpen(true); + server.start(); + console.log('coapServeTest------->coapServeStartTest success'); + expect(serveStartTest).assertTrue(); + }) + it('coapServeStopTest', 0, async () => { + const serveStopTest = true; + const server = new CoapServer(); + server.setAddr('10.0.2.15', '0'); + CoapClient.setNativeLogOpen(true); + server.start(); + console.log('coapServeTest------->serveStart success'); + server.stop(); + expect(serveStopTest).assertTrue(); + }) + }) +} \ No newline at end of file diff --git a/entry/src/ohosTest/ets/test/List.test.ets b/entry/src/ohosTest/ets/test/List.test.ets index 4c9d8f0ecb015bc737a03fd26823e38d9c73779f..d741c257ca27e324d14df8408820c4f92295614b 100644 --- a/entry/src/ohosTest/ets/test/List.test.ets +++ b/entry/src/ohosTest/ets/test/List.test.ets @@ -12,12 +12,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import abilityTest from './Ability.test' -import CoapRootJsunit from './Coap.test' -import telephonyPerfJsunit from './TestInterfaceResponseTime.test' +import abilityTest from './Ability.test'; +import CoapRootJsunit from './Coap.test'; +import telephonyPerfJsunit from './TestInterfaceResponseTime.test'; +import coapServerTest from './CoapServe.test' export default function testsuite() { - abilityTest() - CoapRootJsunit() - telephonyPerfJsunit() + abilityTest(); + CoapRootJsunit(); + telephonyPerfJsunit(); + coapServerTest(); } \ No newline at end of file diff --git a/entry/src/ohosTest/ets/test/TestInterfaceResponseTime.test.ets b/entry/src/ohosTest/ets/test/TestInterfaceResponseTime.test.ets index fe4d24df12303449b9cab7d6dfa11433da79b2c8..82f8aac324d9163fb0a62f69df36842cb2fd4467 100644 --- a/entry/src/ohosTest/ets/test/TestInterfaceResponseTime.test.ets +++ b/entry/src/ohosTest/ets/test/TestInterfaceResponseTime.test.ets @@ -1,4 +1,4 @@ -/** + /** * Copyright (C) 2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, TestType } from '@ohos/hypium' +import { describe, expect, it, TestType } from '@ohos/hypium' import { CoapClient, CoapRequestMethod, CoapRequestType, CoapResponseCode, ContentFormat } from '@ohos/coap'; import cryptoFramework from '@ohos.security.cryptoFramework'; diff --git a/libcoap/index.ets b/libcoap/index.ets index 430891ba2c96b39ff46841e9bc9783b6d4ec1080..b8f4a2722eced1b8a781460c048d06fbefa40994 100644 --- a/libcoap/index.ets +++ b/libcoap/index.ets @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -12,5 +12,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { CoapClient, CoapRequestMethod, CoapRequestType, ContentFormat } from './src/main/ets/components/mainpage/CoapClient' -export { CoapResponse, CoapResponseCode } from './src/main/ets/components/mainpage/CoapResponse' \ No newline at end of file +export { CoapClient, + CoapRequestMethod, + CoapRequestType, + ContentFormat } from './src/main/ets/components/mainpage/CoapClient'; + +export { CoapResponse, CoapResponseCode, CoapResourceCode } from './src/main/ets/components/mainpage/CoapResponse'; + +export { CoapServer } from './src/main/ets/components/mainpage/coapServer'; + +export { CoapResource } from './src/main/ets/components/mainpage/coapResource'; + +export { CoapExchange } from './src/main/ets/components/mainpage/CoapExchange'; \ No newline at end of file diff --git a/libcoap/src/main/cpp/CMakeLists.txt b/libcoap/src/main/cpp/CMakeLists.txt index 53794eb37b6df6f0d9a2cf19e712247880283fb9..f72681e587aca18206aec24f2cbf9f10c7d53566 100644 --- a/libcoap/src/main/cpp/CMakeLists.txt +++ b/libcoap/src/main/cpp/CMakeLists.txt @@ -81,11 +81,15 @@ include_directories(${NATIBERENDER_ROOT_PATH} ${NATIBERENDER_ROOT_PATH}/libcoapSource/include ${NATIBERENDER_ROOT_PATH}/include ${NATIBERENDER_ROOT_PATH}/log + ${NATIBERENDER_ROOT_PATH}/server ${NATIBERENDER_ROOT_PATH}/boundscheck) add_library(coap SHARED native-bridge.cpp coap-client.cpp tools.cpp + ${NATIBERENDER_ROOT_PATH}/server/server.cpp + ${NATIBERENDER_ROOT_PATH}/server/coap_napi_resource.cpp + ${NATIBERENDER_ROOT_PATH}/server/coap_exchange.cpp ${NATIBERENDER_ROOT_PATH}/log/ohos_log.cpp ${NATIBERENDER_ROOT_PATH}/libcoapSource/src/coap_address.c ${NATIBERENDER_ROOT_PATH}/libcoapSource/src/coap_async.c diff --git a/libcoap/src/main/cpp/native-bridge.cpp b/libcoap/src/main/cpp/native-bridge.cpp index 3f23367aad7ef437abc2df8c259c6026bad8faec..e2ab894ec381d439edfa23771161bc349a7fcd68 100644 --- a/libcoap/src/main/cpp/native-bridge.cpp +++ b/libcoap/src/main/cpp/native-bridge.cpp @@ -13,12 +13,16 @@ * limitations under the License. */ #include "client.h" +#include "coap_exchange.h" #include "ohos_log.h" +#include "server.h" + EXTERN_C_START static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor desc[] = { {"setNativeLogOpen", nullptr, OhosLog::SetLogOpen, nullptr, nullptr, nullptr, napi_default, nullptr}, +// {"serverCreate", nullptr, Server::ServerCreate, nullptr, nullptr, nullptr, napi_default, nullptr}, }; napi_property_descriptor classProp[] = { {"request", nullptr, Client::Request, nullptr, nullptr, nullptr, napi_default, nullptr}, @@ -34,6 +38,31 @@ static napi_value Init(napi_env env, napi_value exports) {"setPayloadBinary", nullptr, Client::SetPayloadBinary, nullptr, nullptr, nullptr, napi_default, nullptr}, {"registerOption", nullptr, Client::RegisterOption, nullptr, nullptr, nullptr, napi_default, nullptr}, }; + + napi_property_descriptor classResourceProp[] = { + {"addAttributes", nullptr, CoapResource::AddAttributes, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"registerGetHandler", nullptr, CoapResource::RegisterGetHandler, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"registerPostHandler", nullptr, CoapResource::RegisterPostHandler, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"createResource", nullptr, CoapResource::CreateResource, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"destroy", nullptr, CoapResource::Destroy, nullptr, nullptr, nullptr, napi_default, nullptr}, + }; + + napi_property_descriptor classServerProp[] = { + {"start", nullptr, Server::Start, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"stop", nullptr, Server::Stop, nullptr, nullptr, nullptr, napi_default, nullptr}, +//TODO {"destory", nullptr, Server::Destory, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"addResource", nullptr, Server::AddResource, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"setAddr", nullptr, Server::SetAddr, nullptr, nullptr, nullptr, napi_default, nullptr}, + }; + + napi_property_descriptor classExchangeProp[] = { + {"response", nullptr, CoapExchange::Response, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"getRequestOptions", nullptr, CoapExchange::GetRequestOptions, nullptr, nullptr, nullptr, napi_default, nullptr}, + // TODO {"destory", nullptr, CoapExchange::Destory, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"getRequestText", nullptr, CoapExchange::GetRequestText, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"getSourceAddress", nullptr, CoapExchange::GetSourceAddress, nullptr, nullptr, nullptr, napi_default, nullptr}, + }; + napi_value client = nullptr; const char *classBindName = "client"; int methodSize = std::end(classProp) - std::begin(classProp); @@ -42,6 +71,29 @@ static napi_value Init(napi_env env, napi_value exports) napi_set_named_property(env, exports, "newCoapClient", client); napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); + napi_value server = nullptr; + const char *classServerName = "server"; + methodSize = std::end(classServerProp) - std::begin(classServerProp); + napi_define_class(env, classServerName, strlen(classServerName), Server::JsConstructor, nullptr, methodSize, + classServerProp, &server); + napi_set_named_property(env, exports, "newCoapServer", server); + napi_define_properties(env, exports, sizeof(classServerProp) / sizeof(classServerProp[0]), classServerProp); + + napi_value resource = nullptr; + const char *classResourceName = "coapResource"; + methodSize = std::end(classResourceProp) - std::begin(classResourceProp); + napi_define_class(env, classResourceName, strlen(classResourceName), CoapResource::JsConstructor, nullptr, + methodSize, classResourceProp, &resource); + napi_set_named_property(env, exports, "newCoapResource", resource); + napi_define_properties(env, exports, sizeof(classResourceProp) / sizeof(classResourceProp[0]), classResourceProp); + + napi_value exchange = nullptr; + const char *classExchangeName = "coapExchange"; + methodSize = std::end(classExchangeProp) - std::begin(classExchangeProp); + napi_define_class(env, classExchangeName, strlen(classExchangeName), CoapExchange::JsConstructor, nullptr, + methodSize, classExchangeProp, &exchange); + napi_set_named_property(env, exports, "newCoapExchange", exchange); + napi_define_properties(env, exports, sizeof(classExchangeProp) / sizeof(classExchangeProp[0]), classExchangeProp); return exports; } EXTERN_C_END diff --git a/libcoap/src/main/cpp/server/coap_exchange.cpp b/libcoap/src/main/cpp/server/coap_exchange.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aa0daef61f199f070512bdd7f4129ff0dd0d476f --- /dev/null +++ b/libcoap/src/main/cpp/server/coap_exchange.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coap_exchange.h" + +#include +#include + +#include + +#include "coap3/coap_internal.h" + +#define DEFAULT_BUFF_SIZE 1024 +#define OPTIONS_BUFF_SIZE 10240 // 10KB +#define DATA_BUFF_SIZE 524288 // 5000KB + +CoapExchange::ResponseTool CoapExchange::SetResponseTool(coap_resource_t *resource, coap_session_t *session, + const coap_pdu_t *request, const coap_string_t *query, + coap_pdu_t *response) { + CoapExchange::ResponseTool responseTool; + responseTool.resource = resource; + responseTool.session = session; + responseTool.request = request; + responseTool.query = query; + responseTool.response = response; + responseTool.sem; + sem_init(&(responseTool.sem), 0, 0); + return responseTool; +} + +void CoapExchange::AddResponse(std::string responseId, CoapExchange::ResponseTool responseTool) { + g_responseMap.insert(std::pair(responseId, responseTool)); +} + +void CoapExchange::Destroy(std::string responseId) { + auto iter = g_responseMap.find(responseId); + if (iter != g_responseMap.end()) { + g_responseMap.erase(iter); + } +} + +void CoapExchange::Wait(std::string responseId) { + sem_wait(&g_responseMap[responseId].sem); +} + +napi_value CoapExchange::Response(napi_env env, napi_callback_info info) { + size_t argc = 3; + napi_value args[3]; + napi_status status; + + //获取回调函数的参数 + status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (status != napi_ok || argc < 3) { + napi_throw_type_error(env, nullptr, "Expected at least 3 argument for Response."); + return nullptr; + } + + char responseId[DEFAULT_BUFF_SIZE] = {0}; + size_t charLen = sizeof(responseId) - 1; + status = napi_get_value_string_utf8(env, args[0], responseId, charLen, &charLen); + if (status != napi_ok) { + napi_throw_type_error(env, nullptr, "Failed to convert argument 1 to UTF-8 string."); + return nullptr; + } + + int code; + status = napi_get_value_int32(env, args[1], &code); + if (status != napi_ok) { + napi_throw_type_error(env, nullptr, "Failed to convert argument 2 to int32."); + return nullptr; + } + + char data[DATA_BUFF_SIZE] = {0}; + charLen = sizeof(data) - 1; + status = napi_get_value_string_utf8(env, args[2], data, charLen, &charLen); + if (status != napi_ok || charLen >= DATA_BUFF_SIZE) { + napi_throw_type_error(env, nullptr, "Failed to convert argument 3 to UTF-8 string (must < 0.5M)."); + return nullptr; + } + + if (g_responseMap.find(responseId) == g_responseMap.end()) { + LOGI("response failed: not found id"); + return nullptr; + } + CoapExchange::ResponseTool tmp = g_responseMap[responseId]; + coap_pdu_set_code(tmp.response, (coap_pdu_code_t)COAP_RESPONSE_CODE(code)); + coap_add_data_large_response(tmp.resource, tmp.session, tmp.request, tmp.response, tmp.query, + COAP_MEDIATYPE_TEXT_PLAIN, 0x2ffff, 0, strlen(data), (const uint8_t *)data, NULL, + NULL); + + sem_post(&g_responseMap[responseId].sem); + return nullptr; +} + +napi_value CoapExchange::GetRequestOptions(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + napi_status status; + + // 获取回调函数的参数 + status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (status != napi_ok || argc < 1) { + napi_throw_type_error(env, nullptr, "Expected at least 1 argument for GetRequestOptions."); + return nullptr; + } + + char responseId[DEFAULT_BUFF_SIZE] = {0}; + size_t charLen = sizeof(responseId) - 1; + status = napi_get_value_string_utf8(env, args[0], responseId, charLen, &charLen); + if (status != napi_ok) { + napi_throw_type_error(env, nullptr, "Failed to convert argument to UTF-8 string."); + return nullptr; + } + + if (g_responseMap.find(responseId) == g_responseMap.end()) { + LOGI("response failed: not found id"); + return nullptr; + } + + CoapExchange::ResponseTool tmp = g_responseMap[responseId]; + coap_opt_t *option; + coap_opt_iterator_t opt_iter; + std::string retStr("["); + coap_option_iterator_init(tmp.request, &opt_iter, COAP_OPT_ALL); + int backupNum = 0; + while ((option = coap_option_next(&opt_iter))) { + char item[OPTIONS_BUFF_SIZE] = {0}; + char optionData[OPTIONS_BUFF_SIZE] = {0}; + int len = coap_opt_length(option); + const char *pOption = (const char *)coap_opt_value(option); + memcpy(optionData, pOption, len > OPTIONS_BUFF_SIZE ? OPTIONS_BUFF_SIZE : len); + snprintf(item, OPTIONS_BUFF_SIZE, "{\"number\": %d,\"length\": %d,\"value\": \"%s\"},", opt_iter.number, len, optionData); + retStr += item; + backupNum = 1; + } + retStr = retStr.substr(0, retStr.length() - backupNum); + retStr += ']'; + + napi_value ret; + napi_create_string_utf8(env, retStr.c_str(), NAPI_AUTO_LENGTH, &ret); + return ret; +} + +napi_value CoapExchange::GetRequestText(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + napi_status status; + + //获取回调函数的参数 + status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (status != napi_ok || argc < 1) { + napi_throw_type_error(env, nullptr, "Expected at least 1 argument for GetRequestText."); + return nullptr; + } + + char responseId[DEFAULT_BUFF_SIZE] = {0}; + size_t charLen = sizeof(responseId) - 1; + status = napi_get_value_string_utf8(env, args[0], responseId, charLen, &charLen); + if (status != napi_ok) { + napi_throw_type_error(env, nullptr, "Failed to convert argument to UTF-8 string."); + return nullptr; + } + + if (g_responseMap.find(responseId) == g_responseMap.end()) { + LOGI("response failed: not found id"); + return nullptr; + } + + const uint8_t *data; + size_t size; + CoapExchange::ResponseTool tmp = g_responseMap[responseId]; + coap_get_data(tmp.request, &size, &data); + + LOGI(">KAMI %d %s",size, data); + + if(!data){ + return nullptr; + } + napi_value ret; + napi_create_string_utf8(env, (char *)data, NAPI_AUTO_LENGTH, &ret); + + return ret; +} + +napi_value CoapExchange::GetSourceAddress(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + napi_status status; + + //获取回调函数的参数 + status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (status != napi_ok || argc < 1) { + napi_throw_type_error(env, nullptr, "Expected at least 1 argument for GetRequestText."); + return nullptr; + } + + char responseId[DEFAULT_BUFF_SIZE] = {0}; + size_t charLen = sizeof(responseId) - 1; + status = napi_get_value_string_utf8(env, args[0], responseId, charLen, &charLen); + if (status != napi_ok) { + napi_throw_type_error(env, nullptr, "Failed to convert argument to UTF-8 string."); + return nullptr; + } + + if (g_responseMap.find(responseId) == g_responseMap.end()) { + LOGI("response failed: not found id"); + return nullptr; + } + + char addr[DEFAULT_BUFF_SIZE] = {0}; + CoapExchange::ResponseTool tmp = g_responseMap[responseId]; + coap_print_ip_addr(&tmp.session->addr_info.remote, addr, sizeof(addr)); + + napi_value ret; + napi_create_string_utf8(env, (char *)addr, NAPI_AUTO_LENGTH, &ret); + + return ret; +} + +napi_value CoapExchange::JsConstructor(napi_env env, napi_callback_info info) { + napi_value targetObj = nullptr; + void *data = nullptr; + size_t argsNum = 0; + napi_value args[2] = {nullptr}; + + napi_status status = napi_get_cb_info(env, info, &argsNum, args, &targetObj, &data); + if (status != napi_ok) { + napi_throw_error(env, nullptr, "Failed to retrieve callback info"); + return nullptr; + } + + CoapExchange *classBind = new CoapExchange(); + napi_wrap( + env, nullptr, classBind, + [](napi_env env, void *data, void *hint) { + CoapExchange *bind = (CoapExchange *)data; + delete bind; + bind = nullptr; + }, + nullptr, nullptr); + return targetObj; +} \ No newline at end of file diff --git a/libcoap/src/main/cpp/server/coap_exchange.h b/libcoap/src/main/cpp/server/coap_exchange.h new file mode 100644 index 0000000000000000000000000000000000000000..677faaa1f20e8e44803f92a0fb3f3f719e34fc49 --- /dev/null +++ b/libcoap/src/main/cpp/server/coap_exchange.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBCOAP_EXCHANGE_H +#define LIBCOAP_EXCHANGE_H +#include + +#include + +#include "base.h" + +class CoapExchange { + public: + typedef struct ResponseTool { + coap_resource_t *resource; + coap_session_t *session; + const coap_pdu_t *request; + const coap_string_t *query; + coap_pdu_t *response; + sem_t sem; + } ResponseTool; + + static void Destroy(std::string responseId); + static ResponseTool SetResponseTool(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, + const coap_string_t *query, coap_pdu_t *response); + static void Wait(std::string responseId); + static void AddResponse(std::string responseId, CoapExchange::ResponseTool responseTool); + static napi_value Response(napi_env env, napi_callback_info info); + static napi_value GetRequestOptions(napi_env env, napi_callback_info info); + static napi_value GetRequestText(napi_env env, napi_callback_info info); + static napi_value GetSourceAddress(napi_env env, napi_callback_info info); + static napi_value JsConstructor(napi_env env, napi_callback_info info); +}; +static std::unordered_map g_responseMap; + +#endif // LIBCOAP_EXCHANGE_H \ No newline at end of file diff --git a/libcoap/src/main/cpp/server/coap_napi_resource.cpp b/libcoap/src/main/cpp/server/coap_napi_resource.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d4dd98a5863a895871dd4606e9ca5630a03a569d --- /dev/null +++ b/libcoap/src/main/cpp/server/coap_napi_resource.cpp @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "coap_napi_resource.h" + +#include +#include + +#include "coap3/coap_resource.h" +#include "coap_exchange.h" + +static std::unordered_map g_resourceMap; +#define IDSIZE 16 +#define DEFAULT_BUFF_SIZE 1024 +#define ATTRIBUTE_BUFF_SIZE 10240 // 10KB + +coap_resource_t *CoapResource::getResource(std::string resourceId) { + coap_resource_t *resource = nullptr; + auto iter = g_resourceMap.find(resourceId); + if (iter != g_resourceMap.end()) { + resource = g_resourceMap[resourceId].resource; + } + return resource; +} + +napi_threadsafe_function CoapResource::getPostHandler(std::string resourceId) { + napi_threadsafe_function fn = nullptr; + auto iter = g_resourceMap.find(resourceId); + if (iter != g_resourceMap.end()) { + fn = g_resourceMap[resourceId].postHandler; + } + return fn; +} + +napi_threadsafe_function CoapResource::getGetHandler(std::string resourceId) { + napi_threadsafe_function fn = nullptr; + auto iter = g_resourceMap.find(resourceId); + if (iter != g_resourceMap.end()) { + fn = g_resourceMap[resourceId].getHandler; + } + return fn; +} + +static void CallJs(napi_env env, napi_value jsCb, void *context, void *data) { + napi_value undefined, argv, ret; + napi_create_string_utf8(env, (char *)data, NAPI_AUTO_LENGTH, &argv); + napi_call_function(env, undefined, jsCb, 1, &argv, &ret); +} + +static void hnd_get_index(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, + const coap_string_t *query, coap_pdu_t *response) { + char responseId[IDSIZE]; + sprintf(responseId, "%#lx", response); + char resourceId[IDSIZE]; + sprintf(resourceId, "%#lx", resource); + // 通过responseID添加对应的response节点 + CoapExchange::ResponseTool tmp = CoapExchange::SetResponseTool(resource, session, request, query, response); + CoapExchange::AddResponse(responseId, tmp); + // 通过resourceId找到回调函数 + napi_threadsafe_function tsfn = CoapResource::getGetHandler(resourceId); + napi_call_threadsafe_function(tsfn, responseId, napi_tsfn_blocking); + CoapExchange::Wait(responseId); + CoapExchange::Destroy(responseId); +} + +static void hnd_post_index(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, + const coap_string_t *query, coap_pdu_t *response) { + char responseId[IDSIZE]; + sprintf(responseId, "%#lx", response); + char resourceId[IDSIZE]; + sprintf(resourceId, "%#lx", resource); + // 通过responseID添加对应的response节点 + CoapExchange::ResponseTool tmp = CoapExchange::SetResponseTool(resource, session, request, query, response); + CoapExchange::AddResponse(responseId, tmp); + // 通过responseId找到回调函数 + napi_threadsafe_function tsfn = CoapResource::getPostHandler(resourceId); + napi_call_threadsafe_function(tsfn, responseId, napi_tsfn_blocking); + CoapExchange::Wait(responseId); + CoapExchange::Destroy(responseId); +} + +std::string CoapResource::setResourceHandler(coap_resource_t *resource, std::string uri, + napi_threadsafe_function getHandler, + napi_threadsafe_function postHandler) { + char resourceId[IDSIZE]; + sprintf(resourceId, "%#lx", resource); + auto iter = g_resourceMap.find(resourceId); + if (iter == g_resourceMap.end()) { + CoapResource::ResourceHandler tmp = {uri, resource, getHandler, postHandler}; + g_resourceMap.insert(std::pair(resourceId, tmp)); + } + // 允许重新设置回调函数 + if (getHandler != nullptr) { + g_resourceMap[resourceId].getHandler = getHandler; + } + if (postHandler != nullptr) { + g_resourceMap[resourceId].postHandler = postHandler; + } + std::string ret(resourceId); + return ret; +} + +napi_value CoapResource::RegisterPostHandler(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2] = {nullptr}; + napi_status status; + + status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (status != napi_ok || argc != 2) { + napi_throw_type_error(env, nullptr, "Expected 2 arguments for RegisterPostHandler."); + return nullptr; + } + + char resourceId[DEFAULT_BUFF_SIZE] = {0}; + size_t resourceIdLen = sizeof(resourceId) - 1; + status = napi_get_value_string_utf8(env, args[0], resourceId, resourceIdLen, &resourceIdLen); + if (status != napi_ok) { + napi_throw_type_error(env, nullptr, "Failed to convert first argument to UTF-8 string (resource ID)."); + return nullptr; + } + + coap_resource_t *resource = CoapResource::getResource(resourceId); + if (!resource) { + napi_throw_error(env, nullptr, "Resource not found."); + return nullptr; + } + + napi_value workName; + napi_create_string_utf8(env, "threadSafeTest", NAPI_AUTO_LENGTH, &workName); + napi_value jsCb = args[1]; + + // 创建线程安全post回调函数 + napi_threadsafe_function tsfn; + napi_create_threadsafe_function(env, jsCb, nullptr, workName, 0, 1, nullptr, nullptr, nullptr, CallJs, &tsfn); + napi_acquire_threadsafe_function(tsfn); + setResourceHandler(resource, "", nullptr, tsfn); + + // 注册POST请求的处理程序 + coap_register_request_handler(resource, COAP_REQUEST_POST, hnd_post_index); + return nullptr; +} + +napi_value CoapResource::Destroy(napi_env env, napi_callback_info info) { + g_resourceMap.clear(); + return nullptr; +} + +napi_value CoapResource::RegisterGetHandler(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2] = {nullptr}; + napi_status status; + + status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (status != napi_ok || argc != 2) { + napi_throw_type_error(env, nullptr, "Expected 2 arguments for RegisterGetHandler."); + return nullptr; + } + + char resourceId[DEFAULT_BUFF_SIZE] = {0}; + size_t resourceIdLen = sizeof(resourceId) - 1; + status = napi_get_value_string_utf8(env, args[0], resourceId, resourceIdLen, &resourceIdLen); + if (status != napi_ok) { + napi_throw_type_error(env, nullptr, "Failed to convert first argument to UTF-8 string (resource ID)."); + return nullptr; + } + + coap_resource_t *resource = CoapResource::getResource(resourceId); + if (!resource) { + napi_throw_error(env, nullptr, "Resource not found."); + return nullptr; + } + + napi_value workName; + napi_create_string_utf8(env, "threadSafeTest", NAPI_AUTO_LENGTH, &workName); + napi_value jsCb = args[1]; + + // 创建线程安全get回调函数 + napi_threadsafe_function tsfn; + napi_create_threadsafe_function(env, jsCb, nullptr, workName, 0, 1, nullptr, nullptr, nullptr, CallJs, &tsfn); + napi_acquire_threadsafe_function(tsfn); + setResourceHandler(resource, "", tsfn, nullptr); + + // 注册GET请求的处理程序 + coap_register_request_handler(resource, COAP_REQUEST_GET, hnd_get_index); + return nullptr; +} + +napi_value CoapResource::CreateResource(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1] = {nullptr}; + napi_valuetype valuetype; + + napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (status != napi_ok || argc != 1) { + napi_throw_type_error(env, nullptr, "Expected 1 arguments for CreateResource."); + return nullptr; + } + + char resourceUri[DEFAULT_BUFF_SIZE] = {0}; + size_t charLen = sizeof(resourceUri) - 1; + status = napi_get_value_string_utf8(env, args[0], resourceUri, charLen, &charLen); + if (status != napi_ok) { + napi_throw_error(env, nullptr, "Failed to convert argument to UTF-8 string."); + return nullptr; + } + + std::string resourceUriStr(resourceUri); + coap_resource_t *resource = + coap_resource_init(coap_make_str_const(resourceUriStr.c_str()), COAP_RESOURCE_FLAGS_HAS_MCAST_SUPPORT); + if (!resource) { + napi_throw_error(env, nullptr, "Failed to run coap_resource_init."); + return nullptr; + } + + std::string resourceId = setResourceHandler(resource, resourceUriStr, nullptr, nullptr); + napi_value ret; + napi_create_string_utf8(env, resourceId.c_str(), NAPI_AUTO_LENGTH, &ret); + return ret; +} + +napi_value CoapResource::JsConstructor(napi_env env, napi_callback_info info) { + napi_value targetObj = nullptr; + void *data = nullptr; + size_t argc = 0; + napi_value args[1] = {nullptr}; + napi_status status = napi_get_cb_info(env, info, &argc, args, &targetObj, &data); + if (status != napi_ok) { + napi_throw_error(env, nullptr, "Failed to retrieve callback info."); + return nullptr; + } + + CoapResource *classBind = new CoapResource(); + napi_wrap( + env, nullptr, classBind, + [](napi_env env, void *data, void *hint) { + CoapResource *bind = (CoapResource *)data; + delete bind; + bind = nullptr; + }, + nullptr, nullptr); + return targetObj; +} + +napi_value CoapResource::AddAttributes(napi_env env, napi_callback_info info) { + size_t argc = 3; + napi_value args[3] = {nullptr}; + + napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (status != napi_ok || argc != 3) { + napi_throw_type_error(env, nullptr, "Expected 3arguments for AddAttributes."); + return nullptr; + } + + char resourceId[DEFAULT_BUFF_SIZE] = {0}; + size_t resourceIdLen = sizeof(resourceId) - 1; + + status = napi_get_value_string_utf8(env, args[0], resourceId, resourceIdLen, &resourceIdLen); + if (status != napi_ok) { + napi_throw_error(env, nullptr, "Failed to convert argument to UTF-8 string."); + return nullptr; + } + + std::string resourceIdStr(resourceId); + coap_resource_t *resource = CoapResource::getResource(resourceIdStr); + if (!resource) { + napi_throw_error(env, nullptr, "Resource not found."); + return nullptr; + } + + char resourceKey[DEFAULT_BUFF_SIZE] = {0}; + size_t resourceKeyLen = sizeof(resourceKey) - 1; + status = napi_get_value_string_utf8(env, args[1], resourceKey, resourceKeyLen, &resourceKeyLen); + if (status != napi_ok) { + napi_throw_error(env, nullptr, "Failed to convert argument to UTF-8 string."); + return nullptr; + } + + char resourceValue[ATTRIBUTE_BUFF_SIZE] = {0}; + size_t resourceValueLen = sizeof(resourceValue) - 1; + status = napi_get_value_string_utf8(env, args[2], resourceValue, resourceValueLen, &resourceValueLen); + if (status != napi_ok) { + napi_throw_error(env, nullptr, "Failed to convert argument to UTF-8 string."); + return nullptr; + } + + coap_add_attr(resource, coap_make_str_const(resourceKey), coap_make_str_const(resourceValue), 0); + return nullptr; +} diff --git a/libcoap/src/main/cpp/server/coap_napi_resource.h b/libcoap/src/main/cpp/server/coap_napi_resource.h new file mode 100644 index 0000000000000000000000000000000000000000..7ca767dad1f0aa881c8f2a60cf9016a64788fa29 --- /dev/null +++ b/libcoap/src/main/cpp/server/coap_napi_resource.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBCOAP_RESOURCE_H +#define LIBCOAP_RESOURCE_H + +#include + +#include "base.h" + +class CoapResource { + public: + typedef struct ResourceHandler { + std::string uri; + coap_resource_t *resource; + napi_threadsafe_function getHandler; + napi_threadsafe_function postHandler; + } ResourceHandler; + + CoapResource() {} + ~CoapResource() {} + static napi_value Destroy(napi_env env, napi_callback_info info); + static napi_threadsafe_function getPostHandler(std::string resourceId); + static napi_threadsafe_function getGetHandler(std::string resourceId); + static std::string setResourceHandler(coap_resource_t *resource, std::string uri, + napi_threadsafe_function getHandler = nullptr, + napi_threadsafe_function postHandler = nullptr); + + static napi_value RegisterPostHandler(napi_env env, napi_callback_info info); + static napi_value RegisterGetHandler(napi_env env, napi_callback_info info); + static napi_value AddAttributes(napi_env env, napi_callback_info info); + static napi_value JsConstructor(napi_env env, napi_callback_info info); + static napi_value CreateResource(napi_env env, napi_callback_info info); + static coap_resource_t *getResource(std::string resourceId); +}; + +#endif // LIBCOAP_RESOURCE_H \ No newline at end of file diff --git a/libcoap/src/main/cpp/server/server.cpp b/libcoap/src/main/cpp/server/server.cpp new file mode 100644 index 0000000000000000000000000000000000000000..039d666a5788eb60a8fc422b266ba731851219c5 --- /dev/null +++ b/libcoap/src/main/cpp/server/server.cpp @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "server.h" + +#include + +#include "coap3/coap_resource.h" +#include "coap_napi_resource.h" + +#define DEFAULT_BUFF_SIZE 1024 + +// namespace Server { +struct AsyncCallbackInfo { + napi_env env; + napi_async_work asyncWork; + napi_deferred deferred; + Server *server; +}; + +unsigned int Server::Running = 0; +static std::vector resources; +static char g_ip[DEFAULT_BUFF_SIZE] = "127.0.0.1"; //默认使用回环地址 +static char g_port[DEFAULT_BUFF_SIZE] = "0"; // 0表示使用默认端口 + +unsigned int Server::getRunning() { return Running; } + +void Server::setRunning(unsigned int running) { Running = running; } + +static void ServerRun(Server *server, coap_context_t *ctx) { + int coap_fd = coap_context_get_coap_fd(ctx); + int nfds = 0; + fd_set m_readfds; + if (coap_fd != -1) { + /* if coap_fd is -1, then epoll is not supported within libcoap */ + FD_ZERO(&m_readfds); + FD_SET(coap_fd, &m_readfds); + nfds = coap_fd + 1; + } + + unsigned wait_ms = COAP_RESOURCE_CHECK_TIME * 1000; + while (server->getRunning()) { + int result; + if (coap_fd != -1) { + fd_set readfds = m_readfds; + struct timeval tv; + coap_tick_t begin, end; + coap_ticks(&begin); + + tv.tv_sec = wait_ms / 1000; + tv.tv_usec = (wait_ms % 1000) * 1000; + result = select(nfds, &readfds, NULL, NULL, &tv); + if (result == -1) { + if (errno != EAGAIN) { + coap_log_debug("select: %s (%d)\n", coap_socket_strerror(), errno); + break; + } + } + if (result > 0) { + if (FD_ISSET(coap_fd, &readfds)) { + result = coap_io_process(ctx, COAP_IO_NO_WAIT); + } + } + if (result >= 0) { + coap_ticks(&end); + /* Track the overall time spent in select() and coap_io_process() */ + result = (int)(end - begin); + } + } else { + result = coap_io_process(ctx, wait_ms); + } + if (result < 0) { + break; + } else if (result && (unsigned)result < wait_ms) { + wait_ms -= result; + } else { + wait_ms = COAP_RESOURCE_CHECK_TIME * 1000; + } + } +} + +static void ServerNative(Server *server) { + coap_context_t *ctx = nullptr; + int result = EXIT_FAILURE; + + uint32_t scheme_hint_bits; + coap_addr_info_t *info = nullptr; + coap_addr_info_t *info_list = nullptr; + + LOGI("ServerNative ip_address = %s", g_ip); + coap_str_const_t *my_address = coap_make_str_const(g_ip); + bool have_ep = false; + server->setRunning(1); + /*Initialize libcoap library */ + coap_startup(); + coap_set_log_level(COAP_LOG_WARN); + /*Create coAP context */ + ctx = coap_new_context(nullptr); + if (!ctx) { + coap_log_emerg("cannot initialize context\n"); + goto finish; + } + scheme_hint_bits = coap_get_available_scheme_hint_bits(0, 0, COAP_PROTO_NONE); + info_list = coap_resolve_address_info(my_address, 0, 0, 0, 0, 0, scheme_hint_bits, COAP_RESOLVE_TYPE_LOCAL); + /* Create coAP listening endpoint(s) */ + for (info = info_list; info != NULL; info = info->next) { + coap_endpoint_t *ep; + + ep = coap_new_endpoint(ctx, &info->addr, info->proto); + if (!ep) { + coap_log_warn("cannot create endpoint for CoAP proto %u\n", info->proto); + } else { + have_ep = true; + } + } + coap_free_address_info(info_list); + if (have_ep == false) { + coap_log_err("No context available for interface '%s'\n", (const char *)my_address->s); + goto finish; + } + /* Add in Multicast listening as appropriate */ + coap_join_mcast_group_intf(ctx, g_ip, NULL); + for (auto i : resources) { + coap_add_resource(ctx, i); + } + /* Handle any libcoap I/O requirements */ + ServerRun(server, ctx); + result = EXIT_SUCCESS; + +finish : { + coap_free_context(ctx); + coap_cleanup(); + resources.clear(); +} +} +void DealCallBack(napi_env env, AsyncCallbackInfo *asyncCallbackInfo) { + napi_value arr; + napi_create_array(env, &arr); + + napi_value code; + napi_create_int32(env, 1, &code); + napi_value obj; + napi_create_object(env, &obj); + napi_set_named_property(env, obj, "code", code); + napi_set_named_property(env, obj, "message", arr); + napi_resolve_deferred(asyncCallbackInfo->env, asyncCallbackInfo->deferred, obj); + napi_delete_async_work(env, asyncCallbackInfo->asyncWork); + delete asyncCallbackInfo; + asyncCallbackInfo = nullptr; +} + +static std::unordered_map g_serverMap; + +Server *getServer(std::string classIdStr) { + Server *server = nullptr; + auto iter = g_serverMap.find(classIdStr); + if (iter != g_serverMap.end()) { + server = g_serverMap[classIdStr]; + } + return server; +} + +//定义线程数据结构体 +struct ThreadSafeInfo { + char *sum; +}; + +napi_value Server::Start(napi_env env, napi_callback_info info) { + LOGI("ServerNative Server::Start"); + size_t argc = 1; + napi_value args[1] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + //获取当前唯一标识classId + size_t len = DEFAULT_BUFF_SIZE; + char classId[DEFAULT_BUFF_SIZE] = {0}; + napi_get_value_string_utf8(env, args[0], classId, len, &len); + std::string classIdStr = classId; + Server *server = getServer(classIdStr); + if (!server) { + return nullptr; + } + + napi_deferred deferred; + napi_value promise; + napi_create_promise(env, &deferred, &promise); + AsyncCallbackInfo *asyncCallbackInfo = + new AsyncCallbackInfo{.env = env, .asyncWork = nullptr, .deferred = deferred, .server = server}; + napi_value resourceName; + napi_create_string_latin1(env, "ServerCreate", NAPI_AUTO_LENGTH, &resourceName); + napi_create_async_work( + env, nullptr, resourceName, + [](napi_env env, void *data) { + AsyncCallbackInfo *asyncCallbackInfo = (AsyncCallbackInfo *)data; + ServerNative(asyncCallbackInfo->server); + }, + [](napi_env env, napi_status status, void *data) { DealCallBack(env, (AsyncCallbackInfo *)data); }, + (void *)asyncCallbackInfo, &asyncCallbackInfo->asyncWork); + napi_queue_async_work(env, asyncCallbackInfo->asyncWork); + return promise; +} + +napi_value Server::Stop(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + //获取当前唯一标识classId + size_t len = DEFAULT_BUFF_SIZE; + char classId[DEFAULT_BUFF_SIZE] = {0}; + napi_get_value_string_utf8(env, args[0], classId, len, &len); + std::string classIdStr = classId; + Server *server = getServer(classIdStr); + if (!server) { + return nullptr; + } + + server->setRunning(0); + return nullptr; +} +napi_value Server::SetAddr(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + napi_status status; + + //获取回调函数的参数 + status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (status != napi_ok || argc < 2) { + napi_throw_type_error(env, nullptr, "Expected at least 2 argument for SetAddr."); + return nullptr; + } + + size_t charLen = sizeof(g_ip) - 1; + status = napi_get_value_string_utf8(env, args[0], g_ip, charLen, &charLen); + if (status != napi_ok) { + napi_throw_type_error(env, nullptr, "Failed to convert argument to UTF-8 string."); + return nullptr; + } + + charLen = sizeof(g_port) - 1; + status = napi_get_value_string_utf8(env, args[1], g_port, charLen, &charLen); + if (status != napi_ok) { + napi_throw_type_error(env, nullptr, "Failed to convert argument to UTF-8 string."); + return nullptr; + } + return nullptr; +} + +napi_value Server::AddResource(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + napi_status status; + + // 获取回调函数的参数 + status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (status != napi_ok || argc < 1) { + napi_throw_type_error(env, nullptr, "Expected at least one argument for AddResource."); + return nullptr; + } + + char resourceId[DEFAULT_BUFF_SIZE] = {0}; + size_t charLen = sizeof(resourceId) - 1; // 确保有足够的空间存放字符串和终止符 + status = napi_get_value_string_utf8(env, args[0], resourceId, charLen, &charLen); + if (status != napi_ok) { + napi_throw_type_error(env, nullptr, "Failed to convert argument to UTF-8 string."); + return nullptr; + } + + std::string resourceIdStr(resourceId, charLen); // 使用正确的长度构造字符串 + coap_resource_t *resource = CoapResource::getResource(resourceIdStr); + if (!resource) { + napi_throw_error(env, nullptr, "Resource not found."); + return nullptr; + } + resources.push_back(resource); + return nullptr; +} + +napi_value Server::JsConstructor(napi_env env, napi_callback_info info) { + napi_value targetObj = nullptr; + void *data = nullptr; + size_t argsNum = 0; + napi_value args[1] = {nullptr}; + napi_get_cb_info(env, info, &argsNum, args, &targetObj, &data); + Server *classBind = new Server(); + uintptr_t classId = reinterpret_cast(classBind); + std::string classIdStrTemp = std::to_string(classId); + classBind->classIdStr = classIdStrTemp; + napi_value napiClassId; + srand(atoi(classIdStrTemp.c_str())); + napi_create_string_utf8(env, classIdStrTemp.c_str(), classIdStrTemp.length(), &napiClassId); + napi_set_named_property(env, targetObj, "classId", napiClassId); + g_serverMap.insert(std::pair(classIdStrTemp, classBind)); + + napi_wrap( + env, nullptr, classBind, + [](napi_env env, void *data, void *hint) { + Server *bind = (Server *)data; + delete bind; + bind = nullptr; + }, + nullptr, nullptr); + return targetObj; +} \ No newline at end of file diff --git a/libcoap/src/main/cpp/server/server.h b/libcoap/src/main/cpp/server/server.h new file mode 100644 index 0000000000000000000000000000000000000000..1a88c0b94d699af409cc7c28651bd0160cd4ea77 --- /dev/null +++ b/libcoap/src/main/cpp/server/server.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBCOAP_SERVER_H +#define LIBCOAP_SERVER_H +#include + +#include "base.h" +#include "coap_napi_resource.h" + +class Server { + public: + static napi_value Start(napi_env env, napi_callback_info info); + static napi_value Stop(napi_env env, napi_callback_info info); + static napi_value JsConstructor(napi_env env, napi_callback_info info); + static napi_value AddResource(napi_env env, napi_callback_info info); + static napi_value SetAddr(napi_env env, napi_callback_info info); + unsigned int getRunning(); + void setRunning(unsigned int running); + + std::string classIdStr; + + private: + static unsigned int Running; +}; // namespace OhosLogServer + +#endif // LIBCOAP_SERVER_H \ No newline at end of file diff --git a/libcoap/src/main/cpp/types/coap/index.d.ts b/libcoap/src/main/cpp/types/coap/index.d.ts index 9088dd9c61e57e81e4325bda6eb7debb3317f55e..759c072d8c95fd20f6fb0853a8e0cdee6072aba0 100644 --- a/libcoap/src/main/cpp/types/coap/index.d.ts +++ b/libcoap/src/main/cpp/types/coap/index.d.ts @@ -12,6 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + declare class newCoapClient { classId: number; /** @@ -53,7 +54,7 @@ declare class newCoapClient { /** * 客户端设置option */ - addOption: (optionNum: number, value: string|number, classId: number) => void; + addOption: (optionNum: number, value: string | number, classId: number) => void; /** * 客户端设置message id @@ -71,6 +72,87 @@ declare class newCoapClient { registerOption: (option: number, classId: number) => void; } +declare class newCoapServer { + classId: number; + + /** + * 启动服务器 + */ + start: (classId: number) => Promise; + + /** + * 关闭服务器 + */ + stop: (classId: number) => void; + + /** + * 添加资源 + */ + addResource: (resourceID: string) => void; + + /** + *设置服务IP和端口 + */ + setAddr: (ip: string, port: string) => void; + + /** + * 判断是否启动状态码 + */ + getStatus: (classId: number) => number +} + +declare class newCoapResource { + resourceID: string | number; + + /** + * 添加get请求处理器 + */ + registerGetHandler: (resourceID: string, cb: Function) => void; + + /** + * 添加post请求处理器 + */ + registerPostHandler: (resourceID: string, cb: Function) => void; + + /** + * 添加资源属性 + */ + addAttributes: (resourceId: string, attributesOptionOne: string, attributesOptionTwo: string) => void; + + /** + * 添加Id + */ + createResource: (resourceId: string) => string; + + /** + * 注销资源 + */ + destroy: () => void; +} + +declare class newCoapExchange { + + /** + *发送响应到客户端 + */ + response: (responseId: string, code: number, data: string) => void; + + /** + *获取请求选项 + */ + getRequestOptions: (responseId: string) => string; + + /** + *获取请求负载 + */ + getRequestText: (responseId: string) => string; + + /** + *发起请求的对端IP和接口 + */ + getSourceAddress: (responseId: string) => string; +} + /** * 打开native层的log,默认是关闭的 */ diff --git a/libcoap/src/main/ets/components/mainpage/CoapResponse.ets b/libcoap/src/main/ets/components/mainpage/CoapResponse.ets index d2013a9422f4ce280c861e6ab6534b62efb90df1..da7bb0219327867c281ea9dc0f7de65ab1058b12 100644 --- a/libcoap/src/main/ets/components/mainpage/CoapResponse.ets +++ b/libcoap/src/main/ets/components/mainpage/CoapResponse.ets @@ -18,6 +18,39 @@ export class CoapResponse { message: string[] = [""]; } +/* +coap响应状态码 + */ +export enum CoapResourceCode { + COAP_RESPONSE_CODE_CREATED = 201, + COAP_RESPONSE_CODE_DELETED = 202, + COAP_RESPONSE_CODE_VALID = 203, + COAP_RESPONSE_CODE_CHANGED = 204, + COAP_RESPONSE_CODE_CONTENT = 205, + COAP_RESPONSE_CODE_CONTINUE = 231, + COAP_RESPONSE_CODE_BAD_REQUEST = 400, + COAP_RESPONSE_CODE_UNAUTHORIZED = 401, + COAP_RESPONSE_CODE_BAD_OPTION = 402, + COAP_RESPONSE_CODE_FORBIDDEN = 403, + COAP_RESPONSE_CODE_NOT_FOUND = 404, + COAP_RESPONSE_CODE_NOT_ALLOWED = 405, + COAP_RESPONSE_CODE_NOT_ACCEPTABLE = 406, + COAP_RESPONSE_CODE_INCOMPLETE = 408, + COAP_RESPONSE_CODE_CONFLICT = 409, + COAP_RESPONSE_CODE_PRECONDITION_FAILED = 412, + COAP_RESPONSE_CODE_REQUEST_TOO_LARGE = 413, + COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT = 415, + COAP_RESPONSE_CODE_UNPROCESSABLE = 422, + COAP_RESPONSE_CODE_TOO_MANY_REQUESTS = 429, + COAP_RESPONSE_CODE_INTERNAL_ERROR = 500, + COAP_RESPONSE_CODE_NOT_IMPLEMENTED = 501, + COAP_RESPONSE_CODE_BAD_GATEWAY = 502, + COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE = 503, + COAP_RESPONSE_CODE_GATEWAY_TIMEOUT = 504, + COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED = 505, + COAP_RESPONSE_CODE_HOP_LIMIT_REACHED = 508 +} + /** * coap native回调状态码 */ diff --git a/libcoap/src/main/ets/components/mainpage/coapExchange.ets b/libcoap/src/main/ets/components/mainpage/coapExchange.ets new file mode 100644 index 0000000000000000000000000000000000000000..05b0f6f4fc27bda52946c7d213da0010871751c3 --- /dev/null +++ b/libcoap/src/main/ets/components/mainpage/coapExchange.ets @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { newCoapExchange } from 'libcoap.so'; + +export class CoapExchange { + private coapExchange: newCoapExchange; + + constructor() { + this.coapExchange = new newCoapExchange(); + } + + /** + * 添加get请求处理器 + */ + response(responseId: string, code:number, data: string): void { + return this.coapExchange.response(responseId, code, data); + } + + getRequestOptions(responseId: string): string { + return this.coapExchange.getRequestOptions(responseId); + } + + getRequestText(responseId: string): string { + return this.coapExchange.getRequestText(responseId); + } + + getSourceAddress(responseId: string): string { + return this.coapExchange.getSourceAddress(responseId); + } +} \ No newline at end of file diff --git a/libcoap/src/main/ets/components/mainpage/coapResource.ets b/libcoap/src/main/ets/components/mainpage/coapResource.ets new file mode 100644 index 0000000000000000000000000000000000000000..c1b6dc82b5178abaa0e0bee02e9b6b751adcf294 --- /dev/null +++ b/libcoap/src/main/ets/components/mainpage/coapResource.ets @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { newCoapResource } from 'libcoap.so'; + +export class CoapResource { + private coapResource: newCoapResource; + private resourceId: string | number = 0; + + constructor() { + this.coapResource = new newCoapResource(); + } + + /** + *get请求 + */ + registerGetHandler(resourceId: string, cb: Function): void { + return this.coapResource.registerGetHandler(resourceId, cb); + } + + /** + *post请求 + */ + registerPostHandler(resourceId: string, cb: Function): void { + return this.coapResource.registerPostHandler(resourceId, cb); + } + + /** + *添加资源属性 + */ + addAttributes(resourceId: string, attributesOptionOne: string, attributesOptionTwo: string): void { + return this.coapResource.addAttributes(resourceId, attributesOptionOne, attributesOptionTwo); + } + + /** + *添加id + */ + createResource(resourceId: string): string { + return this.coapResource.createResource(resourceId); + } + + /** + * 注销资源 + */ + destroy(): void { + return this.coapResource.destroy(); + } +} \ No newline at end of file diff --git a/libcoap/src/main/ets/components/mainpage/coapServer.ets b/libcoap/src/main/ets/components/mainpage/coapServer.ets new file mode 100644 index 0000000000000000000000000000000000000000..7e5d34f0c4cda1b840c49374807d93ae87465be5 --- /dev/null +++ b/libcoap/src/main/ets/components/mainpage/coapServer.ets @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { newCoapServer } from 'libcoap.so'; +import { CoapResponse } from './coapResponse'; + +export class CoapServer { + private coapServer: newCoapServer; + private classId = 0; + + constructor() { + console.log("libcoap constructor"); + this.coapServer = new newCoapServer(); + this.classId = this.coapServer.classId; + console.log("libcoap constructor classId:" + this.classId); + } + + setAddr(ip: string, port: string): void { + return this.coapServer.setAddr(ip, port); + } + + start(): Promise { + return this.coapServer.start(this.classId); + } + + stop(): void { + return this.coapServer.stop(this.classId); + } + + addResource(resourceId: string): void { + return this.coapServer.addResource(resourceId); + } + + getStatus(): number { + return this.coapServer.getStatus(this.classId); + } +} \ No newline at end of file