varnish 4.0 官方文檔翻譯20-Device detection

Device detection

設備檢測基於請求中的User-agent找出哪一種內容返回給客戶端。css

使用Device detection狀況的一個例子,發送給小屏幕的手機客戶端或者複雜的網絡環境,減小發送文件的數量,或者提供給不能解碼的客戶端一個video解碼器。html

有些使用這種方案的典型場景:android

  • 重寫urlgit

  • 使用不一樣的後端給客戶端github

  • 改變後端請求爲了讓後端發送裁剪的內容正則表達式

可能爲了便於理解,下文中假定req.http.X-UA-Device頭表示當前前客戶端,同時每種客戶端是惟一的。後端

簡單的像這樣設置頭:api

sub vcl_recv {
    if (req.http.User-Agent ~ "(?i)iphone" {
        set req.http.X-UA-Device = "mobile-iphone";
    }
}

不一樣的處理請求,同時免費提供組織和驗證更詳細的的客戶端。基本且通用的是使用正則表達式來設置。查看https://github.com/varnish/varnish-devicedetect/緩存

Serve the different content on the same URL

這個戲法代表:網絡

  • 驗證客戶端(至關簡單,只須要包含devicedetect.vcl,而後調用它)

  • 根據客戶端的類型找出怎樣發送請求到後端。這包含了些例子,設置header,改變header,甚至改變發送到後端的url。

  • 修改後端響應,增長miss標誌'Vary'頭。

  • 修改發送到客戶端的輸出,這樣若是任何cache不受控,能夠不提供錯誤的內容給客戶端。

全部這些都須要肯定咱們每種設備只會得到一個緩存的對象。

Example 1: Send HTTP header to backend

基本狀況是Varnish增長X-UA-Device頭在發送到後端的請求上,而後後端在響應header中添加Vary頭,響應的內容依賴發送到後端的header。

從Varnish角度看一切開箱即用。

VCL:

sub vcl_recv {
    # call some detection engine that set req.http.X-UA-Device
    # 調用一些檢測機來設置req.http.X-UA-Device。
}
    # req.http.X-UA-Device is copied by Varnish into bereq.http.X-UA-Device
    # req.http.X-UA-Device 被複制到bereq.http.X-UA-Device,當請求傳遞到後端時。

    # so, this is a bit counterintuitive. The backend creates content based on
    # the normalized User-Agent, but we use Vary on X-UA-Device so Varnish will
    # use the same cached object for all U-As that map to the same X-UA-Device.
    #
    # 所以這是有點反直覺的。後端建立響應內容基於正常的User-Agent,可是在X-UA-Device使用
    # Vary,varnish將使用相同的已緩存的對象,對全部的U-As映射到相同的X-UA-Device。

    # If the backend does not mention in Vary that it has crafted special
    # content based on the User-Agent (==X-UA-Device), add it.
    # 若是基於User-Agent的後端響應不帶有Vary,在header種增長它。

    # If your backend does set Vary: User-Agent, you may have to remove that here.
    # 若是後端設置了Vary:User-Agent,你能夠在此刪除它,或者設置成其餘的。

sub vcl_backend_response {
    if (bereq.http.X-UA-Device) {
        if (!beresp.http.Vary) { # no Vary at all
            set beresp.http.Vary = "X-UA-Device";
        } elseif (beresp.http.Vary !~ "X-UA-Device") { # add to existing Vary
            set beresp.http.Vary = beresp.http.Vary + ", X-UA-Device";
        }
    }
    # comment this out if you don't want the client to know your
    # classification
    # 若是不想客戶端知道你的分類,註釋掉便可。
    set beresp.http.X-UA-Device = bereq.http.X-UA-Device;
}

# to keep any caches in the wild from serving wrong content to client #2
# behind them, we need to transform the Vary on the way out.
sub vcl_deliver {
    if ((req.http.X-UA-Device) && (resp.http.Vary)) {
        set resp.http.Vary = regsub(resp.http.Vary, "X-UA-Device", "User-Agent");
        # 在resp.http.Vary的值中將X-UA-Device替換爲User-Agent。
    }
}

Example 2: Normalize the User-Agent string

標準化User-Agent字符。

另外一種發送設備類型的目的是爲了重寫或者標準化發送到後端的User-Agent。

例如:

User-Agent: Mozilla/5.0 (Linux; U; Android 2.2; nb-no; HTC Desire Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1

變成:

User-Agent: mobile-android

若是你在後端上不須要源http header的任何信息能夠這樣設置。一種可能性就是使用CGI腳本,只需小小設置下預約義的頭信息在能夠用的腳本中。

VCL:

sub vcl_recv {
    # call some detection engine that set req.http.X-UA-Device
}

# override the header before it is sent to the backend
# 發送到後端以前重寫header。
sub vcl_miss { if (req.http.X-UA-Device) { set bereq.http.User-Agent = req.http.X-UA-Device; } }
sub vcl_pass { if (req.http.X-UA-Device) { set bereq.http.User-Agent = req.http.X-UA-Device; } }

# standard Vary handling code from previous(以前的) examples.
sub vcl_backend_response {
    if (bereq.http.X-UA-Device) {
        if (!beresp.http.Vary) { # no Vary at all
            set beresp.http.Vary = "X-UA-Device";
        } elseif (beresp.http.Vary !~ "X-UA-Device") { # add to existing Vary
            set beresp.http.Vary = beresp.http.Vary + ", X-UA-Device";
        }
    }
    set beresp.http.X-UA-Device = bereq.http.X-UA-Device;
}
sub vcl_deliver {
    if ((req.http.X-UA-Device) && (resp.http.Vary)) {
        set resp.http.Vary = regsub(resp.http.Vary, "X-UA-Device", "User-Agent");
    }
}

Example 3: Add the device class as a GET query parameter

以GET參數做爲設備類型

上面兩種方式都不行的話你可以使用GET參數來增長設備類型。

http://example.com/article/1234.html --> http://example.com/article/1234.html?devicetype=mobile-iphone

客戶端不會查看分類,僅僅是後端請求被修改了。

VCL:

sub vcl_recv {
    # call some detection engine that set req.http.X-UA-Device
}

sub append_ua {
    if ((req.http.X-UA-Device) && (req.method == "GET")) {
        # if there are existing GET arguments;
        if (req.url ~ "\?") {
            set req.http.X-get-devicetype = "&devicetype=" + req.http.X-UA-Device;
        } else {
            set req.http.X-get-devicetype = "?devicetype=" + req.http.X-UA-Device;
        }
        set req.url = req.url + req.http.X-get-devicetype;
        # 重寫url
        unset req.http.X-get-devicetype;
        # 移除req.http.X-get-devicetype
    }
}

# do this after vcl_hash, so all Vary-ants can be purged in one go. (avoid ban()ing)
sub vcl_miss { call append_ua; }
sub vcl_pass { call append_ua; }

# Handle redirects, otherwise standard Vary handling code from previous
# examples.
sub vcl_backend_response {
    if (bereq.http.X-UA-Device) {
        if (!beresp.http.Vary) { # no Vary at all
            set beresp.http.Vary = "X-UA-Device";
        } elseif (beresp.http.Vary !~ "X-UA-Device") { # add to existing Vary
            set beresp.http.Vary = beresp.http.Vary + ", X-UA-Device";
        }

        # if the backend returns a redirect (think missing trailing slash),
        # we will potentially show the extra address to the client. we
        # don't want that.  if the backend reorders the get parameters, you
        # may need to be smarter here. (? and & ordering)

        if (beresp.status == 301 || beresp.status == 302 || beresp.status == 303) {
            set beresp.http.location = regsub(beresp.http.location, "[?&]devicetype=.*$", "");
        }
    }
    set beresp.http.X-UA-Device = bereq.http.X-UA-Device;
}
sub vcl_deliver {
    if ((req.http.X-UA-Device) && (resp.http.Vary)) {
        set resp.http.Vary = regsub(resp.http.Vary, "X-UA-Device", "User-Agent");
    }
}

Different backend for mobile clients

對手機客戶端使用不一樣的後端。

若是對於手機客戶端你有不一樣的後端來提供頁面,或者在VCL種有任何特殊需求,你可使'X-UA-Device'頭像這樣:

backend mobile {
    .host = "10.0.0.1";
    .port = "80";
}

sub vcl_recv {
    # call some detection engine

    if (req.http.X-UA-Device ~ "^mobile" || req.http.X-UA-device ~ "^tablet") {
        set req.backend_hint = mobile;
    }
}
sub vcl_hash {
    if (req.http.X-UA-Device) {
        hash_data(req.http.X-UA-Device);
    }
}

Redirecting mobile clients

若是想重定向手機客戶端可使用下面的代碼片斷:

sub vcl_recv {
    # call some detection engine

    if (req.http.X-UA-Device ~ "^mobile" || req.http.X-UA-device ~ "^tablet") {
        return(synth(750, "Moved Temporarily"));
    }
}

sub vcl_synth {
    if (obj.status == 750) {
        set obj.http.Location = "http://m.example.com" + req.url;
        set obj.status = 302;
        return(deliver);
    }
}

本身的例子

sub vcl_recv {
    if (req.http.host == "www.example.com" && req.http.User-Agent ~ "(?i)MIDP|WAP|UP.Browser|Smartphone|Obigo|Mobile|AU.Browser|wxd.Mms|WxdB.Browser|CLDC|UP.Link|KM.Browser|UCWEB|SEMC\-Browser|Mini|Symbian|Palm|Nokia|Panasonic|MOT|SonyEricsson|NEC|Alcatel|Ericsson|BENQ|BenQ|Amoisonic|Amoi|Capitel|PHILIPS|SAMSUNG|Lenovo|Mitsu|Motorola|SHARP|WAPPER|LG|EG900|CECT|Compal|kejian|Bird|BIRD|G900/V1.0|Arima|CTL|TDG|Daxian|DAXIAN|DBTEL|Eastcom|EASTCOM|PANTECH|Dopod|Haier|HAIER|KONKA|KEJIAN|LENOVO|Soutec|SOUTEC|SAGEM|SEC|SED|EMOL|INNO55|ZTE|iPhone|Android|Windows CE|Wget|Java|Opera") {
        set req.backend_hint = mobile;
        return(pass);
    }
}
相關文章
相關標籤/搜索