貓聽得懂人話嗎

菲貓

不知道別人家的貓是怎樣,不過我是蠻相信我家的貓是聽得懂的,我們家的貓咪年紀大了,開始有腎臟問題,行動力也差很多,有一陣子看他身體不舒服,然後還要移動去尿尿埋沙好像很辛苦,於是有一次我就跟他說不要埋了我幫他處理,結果,從此之後他就再也不埋尿尿便便了,即使後來身體比較好也一樣...

我們家貓咪的名字叫菲菲,其實他是一隻蠻特別的貓咪,一來他是很少見的母的橘貓,根據我隨便搜尋了一下,好像要有兩組 DNA 的基因都要是橘色基因,然後機率都是三分之一的樣子;二來他的叫聲很特別,別的貓咪是喵喵叫,他都是凹凹叫,而且很多話;另外就是耳朵的尖端還有些毛也很少在橘貓上見到。這幾個特點之外,其他的地方就和一般常見的橘貓很像了,愛吃、親人、喜歡摸摸,根據我的觀察,家裡的成員當中,他最喜歡我摸他了,常常摸頭頭下巴摸一摸他就乾脆整顆頭都不出力支撐,直接倒在我手上了,啊,他還有一個特色是很會騙人,就我所知有好幾個藍星人跟他接觸之後覺得貓咪很可愛也開始養貓了。

回到聽得懂人話嗎這個問題上,另外一個例子是他前幾兩週身體狀況惡化很多,後腳無力,幾乎走不太動了,尿尿便便常常會來不及跑去貓沙盆,雖然有鋪尿布但是他不一定躺在上面,我就跟他說有事情要叫我們,要便便尿尿都跟我們說,結果就真的開始偶爾會突然開始叫幾聲,然後我們問他什麼事情,他就開始尿尿了(不過還是蠻堅持想要到貓沙上面)。

其實菲菲這次身體狀況惡化,醫生是已經無法處理了,我們就是在家裡讓他盡量過的舒適,晚上事情忙完就會去客廳陪他摸摸他,順便玩對馬戰鬼導演版,所幸他還算可以安靜躺著,因為不知道他到底這樣會不會需要其他協助,還跑去問醫生,不過根據醫生說法,痛苦的話貓咪還是會有反應,像是焦慮、一直換姿勢或是嘔吐等等,總之就是會更慘烈就是,還好菲菲這樣還蠻平靜。菲菲最後是在上週六晚上離開的,他挑了一個剛好沒人在旁邊的時間離開,就是我晚上去倒垃圾的時候,我離開家裡之前還跟他說我要去倒垃圾了,一下子就會回來陪他了,等到我回來時他狀況已經驟變了,呼吸變成很慢,大概好幾秒鐘才一次,然後很吃力了,眼睛也都完全沒反應了,之後只有再呼吸幾次就停了,告別就這樣突然的來到,本來最近看房子還在想不知道到新房子的話要讓他佔據那邊呢,悲傷之餘跟老婆先查過的廠商聯絡,心裡其實也有點逃避現實的想著會不會菲菲其實還活著呢,但是也不敢再多看他幾眼,維持在一個自以為的薛丁格的貓的狀態。

隔天週日天氣很好,週六其實下了很大的雨,所以週日天空特別的蔚藍,空氣很乾淨能見度很高,比這兩天颱風前的天空還要漂亮,在開車往火化園區的路上,看著這一大片蔚藍的天空,想到岳母前幾天有說貓咪在挑日子,就覺得他真的是很會挑日子和時間,挑到天氣這麼這麼好的一天,不過一路上還是會逃避現實的想著他會不會其實還活著,要是還活著就把他送進去火化太可怕了吧,不過這種不切實際的幻想終於還是在禮儀師幫菲菲清潔身體時被打破了,看著完全沒反應不會動的貓咪身體,我心理想著:「啊,真的死了」,等火化出來時,看著骨頭想著:「啊,就這樣變成這樣了」。

跟小孩說貓咪去當小天使了,他雖然不常摸菲菲,但是還是會說不想要看不到貓咪,會想他,看到放在桌上的貓咪罐頭還會怕他去當小天使肚子餓怎麼辦(只好趕快裝到箱子),雖然他沒有反應很大不過似乎這陣子也是有比較敏感。至於我,一開始面對空曠的客廳時感到很不習慣,甚至想逃避那個空間,過了快一週是已經沒那麼害怕客廳了,不過大概是很難繼續玩對馬戰鬼了吧,本來甚至很怕會無法看其他貓咪影片照片的,結果似乎也還好,不過以前要出門時,我都會跟佔據在客廳的菲菲報備一下,這個習慣的慣性即使到現在都還在,或許遲早我還是會習慣菲菲不在了,出門再也不會有要跟他報備的衝動吧,也或許更讓人難受的是知道自己遲早會漸漸忘懷他吧。

PS. 本來想放小孩跟貓咪的合照,不過實在沒很多張,最後挑的是我幫他拍的第一張照片。


CSP for Lambda@Edge

CSP

之前工作上主要是用 AWS,AWS 放靜態網站有過 CloudFront CDN 時,如果需要調整 header 的話,官方的解決方案是用 Lambda@Edge,寫 AWS Lambda function 的時候,其實我個人有一個偏好,就是能不用第三方 module 就不用,主要原因有兩個,第一個原因是,如果程式碼太大包,會無法在 AWS console 上直接看(或修改)程式碼;第二個原因是發佈流程會比較麻煩,因為還要去安裝 module,然後再全部打包起來上傳。

要調整 header 的一個主要原因就是為了 security headers,大部分的 security header 都還算單純,但是 CSP(Content Security Policy)就複雜很多了,如果沒有用結構化的資料,其實很難維護,但是針對 Lambda function 我又不想要用第三方 module,最後我想到的解決方案,就是設計一個很簡短的工具函式來把結構化的資料轉成 CSP header 的值,這就是我最近趁 COSCUP 2021 會議期間整理好的新的 open source 專案:CSP

這個專案內容就只是一個簡單的 function:

const CSP = (directives) => {
  return directives
    .map((directive) => {
    	return `${directive.name} ${directive.value.join(' ')};`;
    })
    .join(' ');
};

不過為了好好設計這個 function 其實我也是花不少功夫,首先就是輸入參數的結構要長怎樣,其實一般比較常見的是用物件 property 直接就作為 directive name 的形式,像是 Google 的CSP Evaluator

{
  "default-src": ["'none'"],
  "script-src": ["'self'"],
  "connect-src": ["blah", "blah"]
}

這種結構比較精簡,但是問題就是無法保證順序,考慮再三之後,決定還是用陣列的形式:

[
  {
    "name": "default-src",
    "value": ["'none'"]
  },
  {
    "name": "script-src"",
    "value": ["'self'"]
  }
]

這樣就可以讓開發人員確保輸出的順序,其實大部分時候我也不會那麼在意順序,不過要是default-src如果不是第一個感覺就很不舒服。確定主要的資料結構後,再來就是屬性名稱要用什麼好的問題了,為了找到正確的名稱,我去翻了CSP spec找到關於 parsing 相關的說明,確定了 spec 定義的結構是這樣的(使用 TypeScript 語法):

type Source = string;

type Directive = {
  name: string;
  value: Source[];
};

type Policy = {
  source: "header" | "meta";
  disposition: "enforce" | "report";
  directiveSet: OrderedSet<Directive>;
};

type Policies = Policy[];

在輸入資料的陣列中,每個元素都是DirectiveDirective的兩個屬性分別是namevaluevalue則是Source的陣列集合,當然Source還有更嚴謹的定義,不過這邊就簡化成字串就好。確定完輸入資料的結構後,就是要想盡辦法簡化 function 的內容了,但是也不希望太難讀懂,調整了幾次變成現在的樣子,我還提供了精簡的版本:

const CSP = p => p.map(d => `${d.name} ${d.value.join(' ')};`).join(' ');

其實我對於那個mapjoin一直耿耿於懷,很想要用reduce解決,但是要避免頭尾多空白,會需要多判斷式,就算不予理會,程式碼長度其實還是比現在這個版本長,結果還是mapjoin看起來比較漂亮,所以最後的版本就維持這樣了。

然後我還寫了測試和提供了兩個example,分別是 Lambda@Edge 和 Cloudflare Workers 的,不確定還有沒有類似的服務,如果有發現會再加上。最後就是,因為這個 function 設計就是要給人複製貼上的,所以並沒有發布到 npm 上,然後使用 MIT-0 license 所以也不用 attribution,覺得有興趣使用的就請直接複製貼上吧~

PS. 如果有其他需求,可以看看csp-header,例如 Express 使用,我覺得介面設計得很不錯。


Static Site Hosting 服務需求

前陣子研究了一下用 GCP 來放靜態網站,那時候有整理了一下需求,這篇文章把需求的緣由也整理出來,先說在前面,這些需求並不是要求單一家服務就可以達成所有目標,內文也盡量不提到特定服務,所以不同服務要怎樣達到這些需求就有賴各位自行研究了。

支援 CDN

這個需求沒什麼好說了吧。

支援 HTTP/2

主要的服務應該都支援了,不過還是列一下。

支援加上 Security Headers

現在大家對於安全性的要求很高,所以可以加上 security headers 對我來說已經是一個必備的功能了,像是 CSP、HSTS 等,這項需求看廣一點,其實就是要自定義回傳 header 的功能,如果可以根據路徑調整就更好了,基本上應該只有 HTML 文件本身需要這些 header。

支援 HTTP 轉成 HTTPS

在各家瀏覽器的推波助瀾之下,不支援 HTTPS 的網站感覺就是次一等了,所以把 HTTP protocol 的 traffic 全部轉到 HTTPS 這件事情我也列為必備,有兩種支援的方式,一種是服務內建支援 protocol 轉址,這種最好,因為它會保留請求的路徑(path),而不是把訪客導到首頁,另外一種就是用下面要提到的全站轉址的方式來達成。

雖然我自己是都會把 HTTP 轉到 HTTPS,不過看網站目標,也還是有可能需要繼續支援 HTTP 的。

支援全站轉址

主要的需求是把www.example.com轉址到example.com,或是反過來,像www.apple.com那樣,當然最好還能保留請求的路徑,這個看似很基本的設定,其實現在還蠻常會發現有網站沒做到這件事,尤其是台灣的,我真的是黑人問號?_?

除了 host name 的轉換之外,還有一種情形是需要把整個網站的 request 都轉到某個 URL,例如docs.example.com要關站,然後要把流量都轉到https://example.com/docs

以下算是非必備的需求

支援把 404 改寫成 200

非 SSR 的 SPA 然後配上 route 的話,會有個問題就是除了首頁的 route 都會 404,雖然一般可以用 error_document/not_found_page 之類的設定來讓內容可以正確呈現,但是 404 的 status code 還是會有不少問題,一來是影響搜尋引擎的結果,二來是不知道是不是所有瀏覽器都還會正確的處理 404 時的網頁內容,所以最好還是能回正確的 status code,可以辦到這件事的方法就我所知道的也是有兩種,一種是 rewrite 機制,另外一種就是可以寫程式處理 request/respone 的,像是 Lambda@Edge 那樣。不過在處理這個功能時要是直接全部的路徑回應都變 200 其實也不太好,要完美有點麻煩啊。

支援 CORS

如果會有需要靜態的 JSON 檔案,然後跨網域直接抓下來當資料使用,那就會需要支援 CORS header,和上面自定義回傳 header 那一項不太一樣的是,CORS header 其實是有互動而不是寫死的,應該是要根據 request 的 header 內容來改變回傳的 CORS header,如果需要 preflight request 那還要支援 OPTIONS method 和相對應的回應,不過如果單純只是靜態 JSON 檔案,靠自訂回傳 header 的功能直接寫死應該也是夠用了。

支援 Basic Auth

如果有尚未公開的網站,還是希望至少有個基本的保護,Basic Auth 只是其中一種方法。

支援根據 Header 切換 origin

這個需求的來源就是用手機的訪客可以看到手機版網頁,用桌上型電腦的訪客看到桌面版網頁,然後網址想要維持一致而且兩種版本的網站想要分開開發,不一定會有這個需求。然後不得不說,AWS 的CloudFront-Is-*-Viewerheader真是蠻方便的,不過他們沒洩漏過判斷方式,Cloudflare 則是只有企業方案有支援,但是有提供他們如何判斷 device type。

支援根據路徑切換 origin

如果有特定路徑下的網頁是另外開發的,有支援這個功能的話就會比較好處理,一個比較常見的情境是開發文件的 API spec 是用其他工具或服務產生的,例如用 OpenAPI 文件產生的那種就很常見,或是有些語言也都有常用的文件產生工具,例如 Python 的 Sphinx。

支援 brotli

Google 開發的壓縮格式,對文字資料的壓縮表現比以前主流的 gzip 還好,主要的服務應該都支援了,不過還是列一下。


MacOS 我的設定

最近因應新工作買了台新的 M1 Mac mini,所以重新把常用設定弄了一下,這篇文章來紀錄一下。

Mac OS Preference by othree

首先是 一般 裡面的顯示捲軸(scrollbar),預設的設定是 自動依據滑鼠或觸控軌跡板 ,這個選項的意思是,如果你用的是蘋果的滑鼠或觸控板,那他就會用自動隱藏的那種捲軸,如果有其他品牌的外接游標控制設備,那捲軸就是傳統的,有得捲動時就是會一直出現在那,佔據一塊空間,其實我對於這個預設行為有疑惑很久了,為什麼其他品牌的滑鼠就一定要用傳統的捲軸模式?直到某一天在系統篇好設定亂逛的時候才發現原來就是這個選項。

閱讀「MacOS 我的設定」全文

三浦建太郎逝世

本部落格的 ACG 類別真的都用來發這種文了,5 月 20 日的時候,白泉社官方發布訃文,三浦建太郎老師在 5 月 6 日因為主動脈剝離逝世了,享年 54 歲。

白泉社官網還有多國語言版本的訃文。

最令人惋惜的就是他的代表作品烙印勇士(ベルセルク)還未完成了吧,從我還在國高中時就開始看了,不過後來連載速度慢了很多,當時我想說他是不是被夕映治癒了,靈魂內黑黑的東西被洗滌一空,後來才知道他健康出問題了,其實也是不意外,烙印勇士雖然是月刊連載,但是很多畫面其實都是畫的非常精細,很難想像早期要維持那樣的作畫精細度和連載速度要投入多少精力,這幾年速度就慢很多了,差不多都是一兩年才出一本單行本。目前白泉社還沒有說烙印勇士之後要怎樣,不過有特別提到之後確定後會在跟大家說,我自己是認為三浦應該是對於整個故事的發展早就都規劃好了,不過有留下多少資料又是另外一回事,還有就是如果要找人接手,那個作畫也是一個大問題吧。

最後放上兩張圖,是烙印勇士當中我印象最深刻的地方。

ベルセルク

ベルセルク

PS. 東立有出數位版,而且說現在是沒有任何修剪的版本了,至於實體書去年有再版過,現在應該是缺貨中。


➡ 看看其它文章