Chrome在302重定向的時候對原請求產生2次請求的問題說明

這個問題應該確確實實是一個Chrome的BUG,我在本身的編程環境中發現,並在多個服務器,多個編程語言的運行環境,以及多個瀏覽器下都測試過,都看到有2次請求出現。爲了證實不是本身環境的問題,我也特地去找了一些其餘站點,用它裏面的一些會產生重定向的請求來測試。好比這個請求地址http://wenda.golaravel.com/account/openid/qq_login/,這是golaravel問答模塊進行qq登陸的連接地址,它會重定向到qq受權登陸的頁面。php

使用fiddler監控wenda.golaravel.com這個站點,而後在Chrome裏面訪問以上地址,回到fiddler查看監控結果:node

image

從這個結果能夠明確地看到,Chrome對這個地址發出了2次http請求,而且這兩次http請求都從服務器拿到了Response。從fiddler的Statistics欄裏面,能夠對比看出這兩個請求過程的一些信息:
第一個是:nginx

image
第二個是:
image
從這個對比結果能夠看到,這兩個請求並非一種串行的關係,而是並行的關係。可是這個問題的特性還不止這麼簡單,目前我發現的一些現象以下:laravel

1)不是每次都會出現這個狀況,我只能說大部分測試操做都出現這種狀況,可是少部分狀況是正常的:訪問302的地址,對原地址只發起了一次請求,第二次是對302 Location指定的地址的請求,這是正常的;
2)這兩個請求並非每次都能成功從服務器拿到響應,有的時候其中的一個會報500的錯誤,或者是顯示failed,此時若是觀察fiddler的log記錄,會看到有下面的一些關鍵信息:chrome

Session #143 raised exception System.Net.Sockets.SocketException
//or
This request was retried after a Receive operation failed.

3)大部分狀況下,都是這兩個請求先發起,而後再對302 Location重定向請求;可是極少的狀況會遇到302重定向請求夾在這兩個請求中間。express

目前我並無找到這個問題產生的緣由,網上相關的資料太少了,因此我只能從多個方面來驗證是不是Chrome自己的問題,最後得出的結論也跟個人猜測一致,這就是Chrome較新版本的一個BUG,我在當前時間點的最新版本的Chrome瀏覽器裏面看到這個現象的:apache

image

下面我會詳細地證實它是否爲Chrome的一個BUG。編程

1. 問題的發現windows

我使用laravel框架寫了一個很簡單的程序,就幾個頁面,作微信登陸的demo用的,因此頁面裏的微信登陸按鈕,實際上就是一個須要重定向到微信受權頁面的地址。我在瀏覽器裏面訪問這個地址的時候,在本地windows下的apache服務器的access.log裏面,發現這一個訪問操做產生了三次http請求:
image
從這個日誌裏很明顯地看到有2次對/login/weixin的請求,這個就是重定向請求的原請求記錄。發現這個問題以後,我並不認爲是服務器或者瀏覽器的問題,而是首先懷疑是不是本身代碼裏面有重定向loop的狀況。因此第一步就去仔細地檢查本身的代碼:瀏覽器

/**
 * 微信登陸
 * @param Request $request
 * @return mixed
 */
public function weixin(Request $request)
{
    $q_str = $request->getQueryString();

    return Socialite::with('weixin')
        //設置受權的回調地址,經過命名路由的形式
        ->setRedirectUrl(route('notify_weixin') . ($q_str ? '?' . $q_str : ''))
        ->redirect();
}

最後的redirect方法是:

/**
 * 重定向並將state參數寫到cookie裏面去,而不是採用session
 * @return string
 */
public function redirect()
{
    $state = $this->getState();

    $response = new RedirectResponse($this->getAuthUrl($state), 302, [
        'Set-Cookie' => implode('', [
            $this->state_cookie_name,
            '=',
            $this->getEncryptState($state),
            "; path=/; domain=",
            $this->getDomain(),
            "; expires=" . gmstrftime("%A, %d-%b-%Y %H:%M:%S GMT", time() + $this->state_cookie_time),
            "; Max-Age=" . $this->state_cookie_time,
            "; httponly"
        ])
    ]);

    return $response;
}

這個代碼裏的RedirectResponse是laravel框架提供的類。從這個代碼,我並無看出什麼問題,由於沒有屢次建立Response的處理。因此開始懷疑是不是新版的laravel框架的問題,由於我用的是最新的laravel框架。因此我又用公司的php環境測試了一下,公司的php環境用的yii這個php框架,結果發現,公司的環境在本地依然存在這個問題。

爲了排除是框架的問題,我又本身寫了兩個最簡單的php頁面:
image
image
我把demo1部署到demo1.com,demo2部署到demo2.com,而後訪問demo1.com來測試。最後也仍是遇到了這個問題。在這一次測試裏面,我還發現了有兩個關鍵點:

1)若是把302改爲301重定向,那麼chrome就不會產生兩次請求。可是實際上301用在這裏是不對的,由於它的含義是原地址的資源已經永久轉移到別的位置了。
2)這個問題致使的兩次對原地址的請求,大部分狀況下,服務器都能收到並進行處理。(demo1裏面的打印信息,在這個問題出現的時候,每次都打印兩個時間信息,說明服務器響應了2次)。這意味着用戶的一次訪問操做,瀏覽器發出了2個請求,服務器對同一個用戶操做進行了2次處理。這並非個沒有影響的事,好比當這個請求對某個資源的狀態作了一些持續性的改變時,如數據累計,那就意味着用戶一次操做,就會累計2次,這樣這些數據結果可能就有問題了。這也是我把這個問題詳細記錄說明的主要緣由,我以爲開發中應該注意到這個問題的存在,以便在排除一些疑難的數據問題的時候,能夠思考到這個層面上來。

到此爲止,基本上已經排除是php框架的問題了。接下來考慮的問題產生對象主要是編程語言,服務器以及瀏覽器。

2. 排查是否爲編程語言的問題

爲了驗證是否爲php語言自己的問題,我又用express框架寫了如下簡單代碼,並運行在node的環境裏面來測試:

app.get('/', function (req, res) {
    res.redirect('/redirect');
});

app.get('/redirect', function (req, res) {
    res.send('success');
});

最後測試發現這個問題,在Nodejs裏面一樣存在。因此也能夠排除是php語言的問題了。

3. 排查是否爲服務器的問題

由於這個問題自己是在本地windows服務器裏面發現的,因此我懷疑是否爲本機apache的問題,因此我又把相關的代碼部署到遠程的nginx服務器上,最後一樣測試到這個問題的存在。

加上上一步在nodejs裏面測試的時候,其實是用本機的node服務器運行的,綜合這兩個服務器測試結果,也能證實並非apache服務器的問題。

4. 排查是否爲瀏覽器的問題

個人機子上有360瀏覽器,firefox,IE11,Edge瀏覽器和最新的Chrome瀏覽器的。最後測試發現只有Chrome瀏覽器裏面有這個問題,其它瀏覽器測試,在fiddler裏面都只能看到對原地址僅發起了一次http請求,在每一個瀏覽器我都重複作了大概十屢次訪問操做才得出這個結論。爲了進一步驗證Chrome的版本問題,我又特地刪了當前版本,下載了一個Chrome46的瀏覽器測試,結果沒有發現這個問題。到此爲止,也就基本上能夠認爲是Chrome瀏覽器的問題。

5. 進一步排查是否爲本機運行環境的問題

爲了進一步排查是本身開發環境的問題,我專門到網上找了一些其餘站點的302請求地址作測試。除了本文開頭提供的地址,下面這個地址也能夠測試:

http://www.toutiao.com/auth/connect/?type=sso&platform=weixin&next=https://sso.toutiao.com/auth/login_success/?service=http://www.toutiao.com/

image

最後,根據以上的排查內容,能夠認定這個問題是Chrome的一個BUG,我已經report給他們了,回不回覆不重要,最重要的是下一個版本這個問題是否可以解決。因此接下來這個問題,個人處理方式是:跟進chrome的版本更新狀況,並在新版本中進行測試。但願它能在後續的版本中獲得解決。

其實在以上排查過程當中,還有一些狀況值的考慮,好比操做系統環境,網絡環境的影響,畢竟http請求自己處於這兩個大的因素之下,因此也不能徹底排除它們的緣由。因此,要是感興趣的朋友,以爲這個問題值得研究的話,很是但願你能把本身的測試結果反饋過來,若是這個問題在你的機器上也存在,那麼就能增長本文的客觀性和正確性,就能幫助更多的人;若是不少人都沒有測試出這個問題,就說明本文的結論有誤,這篇文章應該做廢才行。

相關文章
相關標籤/搜索