LoginSignup
9
11

More than 5 years have passed since last update.

ServiceWorker上で動く軽量WAFみたいなミドルレイヤー作ってみた

Last updated at Posted at 2015-02-26

これ mizchi/sabizan
ServiceWorkerっていう名前がサビ残っぽいよなってみんな思ってたと思う。

ServiceWorkerの勉強兼仕事で使うためのモックサーバーとして作った

用途

  1. ServiceWorker層でビジネスロジックを持つAPIサーバー
  2. またはテスト用のAPIモックサーバー

できるだけexpress風にしようとしたけどRequestのネイティブオブジェクトに値を挿入するの気持ち悪くて互換は諦めた。適当なラッパー書けば同じコードから生成できる気がしている。

どう動くか

dist/sabizan.js は service worker上で動くことが必須で、他のクライアントJSと同じくbrowserifyを使うか、importScriptで読み込むことを意図している。

Sabizan = require 'sabizan' # with browserify or importScript('dist/sabizan.js')
proxy = new Sabizan location.origin+'/api'

# it will respond to https://localhost:3000/api/user/fuga?foo=bar
proxy.get '/user/:id', ({id}, {foo}, req) ->
  {id, foo}

# Return with promise
proxy.post '/post', ({}, body) ->
  new Promise (done) ->
    setTimeout ->
      done {type: 'this is post:'+params.prop}
    , 300

self.onfetch = (event) ->
  if proxy.isHandleScope event.request.url
    event.respondWith(proxy.createResponse(event.request))

sinatraとかexpressっぽいと思う。ルーティングをparseする処理は express の中で使われてる path-to-regexp を使った。

クエリストリングのパースにrequire('url')したせいでbrowserify がnode本体のそこそこ大きいurlモジュールを呼んでしまっていて、そのせいでサイズがやや大きい。

何が嬉しいか

  • UIスレッドから独立しているのでビジネスロジックで重い処理をしても固まらない(WebWorkerと一緒)
  • ビジネスロジックが完全にDOMから触れられなくなるので、設計に対する矯正ギプス的な役割ができる

FluxだとViewとStoreが元々疎結合で、たとえばmizchi/ardaはただのevent-emitterなのでStore層をServiceWorkerの中へ持っていけるのではないかと考えている。というか実際それを意識して設計していた面はある。

とはいえ、まだまだ仕様策定中の機能なので、自分の目的が達せられない可能性はある。コード量は少ないんで適当に読んでください。

注意点

これを作るにあたって、次のようなコードを書いてブラウザからワーカーに対して1秒ごとにポーリングしてみたんだけど、どうやらServiceWorker は30秒ぐらいでインスタンスが破棄される模様。KeepAlive的なオプションはいまのとこなさそう。

sw.js

console.log 'worker started'

counter = {cnt: 0}
self.onfetch = (event) ->
  console.log 'onfetch', event.request.url
  if event.request.url.indexOf(location.origin+'/api') > -1
    counter.cnt = counter.cnt + 1
    event.respondWith new Response JSON.stringify(counter),
      status: 200
      headers:
        'Content-Type': 'application/json'

polling

require 'whatwg-fetch'
window.addEventListener 'load', =>
  navigator.serviceWorker.register 'service-worker.js', scope: '.'
  .then (r) ->
    console.log('service-worker registered')
    setInterval ->
      fetch('api/foo', {headers: {'content-type':'application/json'}})
      .then (res) -> res.json()
      .then (json) -> console.log(json)
      .catch ->
        'serviceWorker not ready'
    , 1000

  .catch (whut) ->
    console.error('fail to register', whut)

何もしなければcnt が 30 秒ごとにリセットされる。registerが走ればさらに30秒、っぽい挙動にみえる。

分厚いStore層をここに構築すると、30秒ごとに捨てられるのが厳しいので、KeepAlive的な指定がほしいと思った。

9
11
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
11