Subversion 聽上去像是十年前的東西了,尤其是 mod_dav_svn。不過祖傳代碼放在哪裡可就不好說了。總之,你已經有了一個現成的 Nginx,希望為已經一個一直裸奔著的代碼倉庫添加 HTTPS 的支持,但並非一句 proxy_pass 就能搞得定。

例如:

upstream subversion{
    127.0.0.1:1080;
}
server{
    listen [::]:443;
    server_name svn.example.com;

    # SSL ...

    location / {
        proxy_pass http://subversion;
    }
}

首先,恭喜一下,沒有使用 proxy_pass http://subversion/,避免了第一個坑。因為結尾的 / 字符會使得 Nginx 自動對 URL 進行編碼,從而影響正常使用。但馬上就會在提交時會遇到 502 錯誤。

COPY 和 DELETE 的支援

Subversion 認爲 https://svn.example.com 是一个 HTTPS 連結,而 Apache 只提供了 HTTP 服務,所以請求 Header 中的 Destination 應以 http:// 開頭才能正常工作。

很快,你從 Stackoverfollow 找到了修改的辦法:

location / {
    proxy_pass http://subversion;
    set $fixed_destination $http_destination;
    if ( $http_destination ~* ^https(.*)$ ) {
        set $fixed_destination http$1;
    }
    proxy_set_header Destination $fixed_destination;
}

然後發現一切 OK。很好,果然掉進了第二個坑。

Nginx 的迷惑行爲

$fixed_destination 看起來只是把 https:// 替換成了 http://,非常簡單明瞭,沒有問題。

當你愉快地從主幹上複製了一個新分支,修改了一個檔案名包含中文的檔案,順利地編譯並通過測試;然後再合併回主乾時,如果足夠細心的話,就會發現這個檔案名被 urlencode 了。當然,並不是僅僅包括中文名檔案,想像一下,可是分支中的提交的所有檔案的名字都被 urlencode 了哦!而且已經被 urlencode 過的檔案名,下次合併分支時還會被再 urlencode 一次哦!

問題就在於 $fixed_destinationhttp$1 其實就被 Nginx 進行了一次 urlencode。也許你決定把修改 Destination 的操作交給 Apache,或者決定寫一段 lua 腳本進行解碼,來規避這個神奇的問題。且慢!這裡還有一個神奇的解決辦法:

location / {
    proxy_pass http://subversion;
    set $fixed_destination $http_destination;
    if ( $http_destination ~* ^https(?<unencoded_destinaton>.*)$ ) {
        set $fixed_destination http$unencoded_destinaton;
    }
    proxy_set_header Destination $fixed_destination;
}

給匹配用的正則組加一個命名就 OK 了……

其他

這個解決方案出自 Maxim Dounin 九年多以前的回复。所以,在 Nginx 中使用正則修改變量最好使用命名捕獲組

十年前的架構,十年前的問題,十年後仍然存在。神奇的是,十年前的方案仍然管用。