先日作成したものと見かけは同じですが、中身が少し違います。
sendで渡したオブジェクトが持つプロパティによって異なる機能を呼び出せる仕組み(ただし今回はreadTextFileのみ)になっています。
sendで渡したオブジェクトが持つプロパティによって異なる機能を呼び出せる仕組み(ただし今回はreadTextFileのみ)になっています。
できれば、もう少し違う仕組みにしたかった(後述)ですが、とりあえず動作する物を作る方が先決と考えて、無難な方法で実装しました。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { listenAndServe } from "https://deno.land/std/http/server.ts"; | |
export async function httpサーバを起動(httpリクエスト処理: any){ | |
const port = await 空いてるport番号を返す() | |
const server = listenAndServe({ port: port }, async (req)=> { httpリクエスト処理(req) }); | |
console.log(`HTTP webserver running. Access it at: http://localhost:` + port + `/`) | |
ブラウザ起動(port) | |
} | |
function ブラウザ起動(port: number){ | |
Deno.run({cmd:['cmd', '/C start http://localhost:'+port]}) | |
} | |
async function 空いてるport番号を返す(): Promise<number>{ | |
let pathBat = await 一時ファイルを作成('netstat -na | find "%~1"') | |
if(pathBat.match(/[ ]/)){ | |
throw new Error('一時ファイルのPathにスペースが含まれています。\n'+pathBat+'\nDeno.runでは「"」の記号の前に「\\」が挿入されてしまうため、使用できません。') | |
} | |
for(let port=49152; port<=65535 ;port++){ | |
const p = Deno.run({cmd:['cmd', '/C', 'chcp 65001 & '+pathBat+' ' + port], stdout:'piped'}) | |
const o = await p.output() | |
const text = new TextDecoder().decode(o) | |
// console.log('text:\n'+text) | |
if(text.indexOf(':'+port)==-1){ | |
Deno.remove(pathBat) | |
return port | |
} | |
} | |
throw new Error('throw from port num search') | |
} | |
async function 一時ファイルを作成(value:string){ | |
const path = await Deno.makeTempFile({prefix: 'deno_tmp', suffix:'.bat'}) | |
await Deno.writeTextFile(path, value) | |
return path | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as path from "https://deno.land/std@0.97.0/path/mod.ts"; // pathResolver | |
import { webSocket接続 } from './webSocket接続.ts' | |
export async function httpリクエスト処理(request: any){ | |
if(request.method != 'GET'){return} | |
console.log('url:[' + request.url + ']') | |
let url = request.url | |
if(url=='/ws'){return webSocket接続(request)} | |
if(url=='/'){ url = './index.html' } | |
if(url.indexOf('/')==0){ url = '.' + url } | |
url = 絶対Pathに変換(url) | |
const ファイルの有無 = await fileExists(url) | |
if(!ファイルの有無){ | |
return request.respond({ status: 404 }) | |
} | |
const file = await Deno.open(url); | |
request.respond({ | |
status: 200, | |
headers: new Headers({"content-type": 拡張子2contentType(url)}), | |
body: file | |
}); | |
} | |
function pathResolver(meta: ImportMeta): (p: string) => string { | |
return (p) => path.fromFileUrl(new URL(p, meta.url)) | |
} | |
const 絶対Pathに変換 = pathResolver(import.meta); | |
function 拡張子2contentType(url: string):string{ | |
if(url.match(/\.htm(l?)$/)){return 'text/html'} | |
if(url.match(/\.css$/)){return 'text/css'} | |
if(url.match(/\.js$/)){return 'text/javascript'} | |
return '' | |
} | |
async function fileExists(filePath: string): Promise<boolean> { | |
try { | |
await Deno.lstat(filePath); | |
return true; | |
} catch (err) { | |
if (err instanceof Deno.errors.NotFound) { | |
return false; | |
} | |
throw err; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="ja"> | |
<head> | |
<meta charset="UTF-8" /> | |
<title>webSocket接続の維持</title> | |
</head> | |
<body> | |
<input id=inp style="width:100%"> | |
<textarea id=ta style="width:100%;height:200px"></textarea> | |
</body> | |
<script src="JSONレシーバー_ブラウザ側.js"></script> | |
<script> | |
function ws接続(){ | |
const ws = new WebSocket(`ws://${location.host}/ws`) | |
ws.addEventListener("open", () => {}); | |
ws.addEventListener("close", () => {}); | |
ws.addEventListener("message", ({data}) => { | |
JSONレシーバー.onmessage(data) | |
}); | |
JSONレシーバー.初期化(ws) | |
} | |
inp.onchange = function(){ | |
const path = this.value | |
if(!path){return} | |
const obj = { | |
readTextFile : true, | |
path | |
} | |
JSONレシーバー.send(obj, (txt)=>{ ta.value = txt }) | |
} | |
ws接続() | |
</script> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export async function JSONレシーバー(ws:any, msg:string, clients:Map<number, any>){ | |
const obj = JSON.parse(msg) | |
/* objの中身は以下のようになっている。 | |
obj = { | |
識別ID : 0以上の整数。複数のmsgの返信先関数を特定するためのもの。ブラウザ側JSONレシーバーが付与する。 | |
obj : オブジェクト。ブラウザ側JSONレシーバーは送信元関数から受け取った値をこのプロパティに入れるだけ。 | |
} | |
*/ | |
console.log('msg['+msg+']') | |
if(obj.obj.readTextFile){ | |
const text = await Deno.readTextFile(obj.obj.path) | |
return 返信(ws, obj.識別ID, text) | |
} | |
} | |
function 返信(ws:any, 識別ID:number, msg:any){ | |
const obj返信用 = { | |
識別ID, | |
returnValue : msg | |
} | |
const str返信用 = JSON.stringify(obj返信用) | |
ws.send(str返信用) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
JSONレシーバー = function(){ | |
const arr識別ID = [] | |
let webSocket | |
return { | |
onmessage : function(msg){ | |
const obj = JSON.parse(msg) | |
const 識別ID = obj.識別ID | |
const 返送先 = arr識別ID[識別ID] | |
arr識別ID[識別ID] = 0 | |
返送先(obj.returnValue) | |
}, | |
send : function(msg, 返送用関数){ | |
let 識別ID = 0 | |
while(arr識別ID[識別ID]){ 識別++ } | |
arr識別ID[識別ID] = 返送用関数 | |
const obj = {識別ID, obj:msg} | |
const txt = JSON.stringify(obj) | |
webSocket.send(txt) | |
}, | |
初期化 : function(ws){ | |
webSocket = ws | |
} | |
} | |
}() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// --allow-run --allow-net --allow-read --allow-write | |
import { httpサーバを起動 } from './httpサーバを起動.ts' | |
import { httpリクエスト処理 } from './httpリクエスト処理.ts' | |
httpサーバを起動(httpリクエスト処理) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
WebSocket, | |
isWebSocketCloseEvent, | |
} from "https://deno.land/std/ws/mod.ts"; | |
import { JSONレシーバー } from "./JSONレシーバー_deno側.ts" | |
export async function webSocketリクエスト処理(ws: WebSocket): Promise<void> { | |
const id = ++clientId; | |
clients.set(id, ws); | |
console.log(`[${id}]番目の接続 open`); | |
for await (const msg of ws) { | |
if (typeof msg === "string") { | |
JSONレシーバー(ws, msg, clients) | |
continue | |
} else if (isWebSocketCloseEvent(msg)) { | |
console.log(`[${id}]番目の接続 close`); | |
clients.delete(id); | |
break; | |
} | |
} | |
} | |
// 以下はexportしない(import先からコールできない)関数など。 | |
const clients = new Map<number, WebSocket>(); | |
let clientId = 0; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
acceptWebSocket, | |
acceptable, | |
} from "https://deno.land/std/ws/mod.ts"; | |
import { webSocketリクエスト処理 } from './webSocketリクエスト処理.ts' | |
export function webSocket接続(request: any):void{ | |
console.log('webSocket接続') | |
if(!acceptable(request)){return console.log('webSocket接続 not acceptable')} | |
acceptWebSocket({ | |
conn: request.conn, | |
bufReader: request.r, | |
bufWriter: request.w, | |
headers: request.headers, | |
}).then(webSocketリクエスト処理); | |
} |
動作確認ok
気になっているのは「Promise/async/await」で折角コールバック地獄から抜け出せたのに、onmessageイベントの戻り値の受け取り方がコールバックじゃ駄目じゃない?という所。
できればブラウザ側で「const txt = await Deno.readTextFile(path)」みたいな書き方をしたいです。
そのためにはonmessageのリスナーをどうにかしなきゃいけないと考えましたが、webSocketの接続数が1だと各機能からの呼び出しに対する応答の順番が保証されないので無理な気がします。
接続数を各機能の数に合わせて増やせばOKなのかもしれませんが…。
0 件のコメント:
コメントを投稿