日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
一個有意思的Tomcat異常

在公眾號后臺,經(jīng)常能看到讀者的消息,其中一部分消息是關(guān)于Tomcat使用過程中遇到的問題。但是,由于微信的「克制」,如果消息回復(fù)的比較晚,就會遇到「過期」的尷尬,我并不能主動聯(lián)系到提問的人。

創(chuàng)新互聯(lián)公司于2013年開始,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都網(wǎng)站設(shè)計、網(wǎng)站制作、外貿(mào)營銷網(wǎng)站建設(shè)網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元屯留做網(wǎng)站,已為上家服務(wù),為屯留各地企業(yè)和個人服務(wù),聯(lián)系電話:13518219792

后面有需要討論問題的朋友,如果公眾號發(fā)消息未收到回復(fù),可以加我微信。

說回正題,之前有位讀者留言,說了一個 Tomcat 異常的問題。

即 Tomcat 各功能正常,不影響使用,但是偶爾的在日志中會看到類似于這樣的異常信息:

 
 
 
 
  1. INFO [https-apr-8443-exec-5] org.apache.coyote.http11.Http11Processor.service Error parsing HTTP request header
  2.  Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
  3.  java.lang.IllegalArgumentException: Invalid character (CR or LF) found in method name
  4.     at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:443)
  5.     at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:982)

為啥報這個呢?明明自己沒做什么操作。

順著異常信息我們往上看,首先這個提示是解析請求頭出現(xiàn)的錯誤。更細(xì)節(jié)一些是解析請求頭中第一行,所謂的「Request Line」的時候出了問題。

什么是「Request Line」呢? 就是HTTP 規(guī)范中指定的,以請求方法開頭 再加上請求URI 等。具體看這個規(guī)范說明

這里我們的異常信息提示我們是在解析 Method name的時候出了問題。看規(guī)范里說了「The Request-Line begins with a method token」也就是有固定的東西的,不是啥都能叫一個method name。我們熟悉的GET/POST/PUT/DELETE都是這里允許的。

我們再來看 Tomcat 的源碼,是如何判斷這里的 Requet Line 是不是一個包含一個合法的 method name。

順著異常的類和方法,輕車熟路,直接就能看到了。

 
 
 
 
  1. if (parsingRequestLinePhase == 2) {
  2.     //
  3.     // Reading the method name
  4.     // Method name is a token
  5.     //
  6.     boolean space = false;
  7.     while (!space) {
  8.         // Read new bytes if needed
  9.         if (byteBuffer.position() >= byteBuffer.limit()) {
  10.             if (!fill(false)) // request line parsing
  11.                 return false;
  12.         }
  13.         // Spec says method name is a token followed by a single SP but
  14.         // also be tolerant of multiple SP and/or HT.
  15.         int pos = byteBuffer.position();
  16.         byte chr = byteBuffer.get();
  17.         if (chr == Constants.SP || chr == Constants.HT) {
  18.             space = true;
  19.             request.method().setBytes(byteBuffer.array(), parsingRequestLineStart,
  20.                     pos - parsingRequestLineStart);
  21.         } else if (!HttpParser.isToken(chr)) {
  22.             byteBuffer.position(byteBuffer.position() - 1);
  23.             throw new IllegalArgumentException(sm.getString("iib.invalidmethod"));
  24.         }
  25.     }
  26.     parsingRequestLinePhase = 3;
  27. }

我們注意紅色的異常就是上面產(chǎn)生的內(nèi)容。產(chǎn)生這個是由于讀取的byte 不是個 SP 同時下面的 isToken 也不是true導(dǎo)致。

那Token都有誰是怎么定義的?

這里挺有意思的,直接用一個boolean數(shù)組來存,前面我們傳進(jìn)來的byte,對應(yīng)的是這個數(shù)組的下標(biāo)。

 
 
 
 
  1. public static boolean isToken(int c) {
  2.     // Fast for correct values, slower for incorrect ones
  3.     try {
  4.         return IS_TOKEN[c];
  5.     } catch (ArrayIndexOutOfBoundsException ex) {
  6.         return false;
  7.     }
  8. }

這里的boolean數(shù)組,初始化時有幾個關(guān)聯(lián)的數(shù)組一起,長度為128。

 
 
 
 
  1. private static final boolean[] IS_CONTROL = new boolean[ARRAY_SIZE];
  2. private static final boolean[] IS_SEPARATOR = new boolean[ARRAY_SIZE];
  3. private static final boolean[] IS_TOKEN = new boolean[ARRAY_SIZE];
  4. // Control> 0-31, 127
  5. if (i < 32 || i == 127) {
  6.     IS_CONTROL[i] = true;
  7. }
  8. // Separator
  9. if (    i == '(' || i == ')' || i == '<' || i == '>'  || i == '@'  ||
  10.         i == ',' || i == ';' || i == ':' || i == '\\' || i == '\"' ||
  11.         i == '/' || i == '[' || i == ']' || i == '?'  || i == '='  ||
  12.         i == '{' || i == '}' || i == ' ' || i == '\t') {
  13.     IS_SEPARATOR[i] = true;
  14. }
  15. // Token: Anything 0-127 that is not a control and not a separator
  16. if (!IS_CONTROL[i] && !IS_SEPARATOR[i] && i < 128) {
  17.     IS_TOKEN[i] = true;
  18. }

所以這里token的定義明確了,非控制字符,非分隔符,ascii 碼小于128 的都是 token。

所以問題產(chǎn)生原因定位了,是由于我們的請求頭中傳遞了「非法」方法名稱,導(dǎo)致請求不能正確處理。

我們來看一個正常的請求信息

Request Line 就是上面看到的第一行內(nèi)容。 GET /a/ HTTP/1.1

那有問題的內(nèi)容大概是這個樣子

誰能從上面解析出來請求方法?

這時你可能會問,正常請求都好好的,你這個怎么搞的?

對。正常沒問題,如果我們的Connector 是普通的此時可以響應(yīng)請求,如果你一直http://localhost:port/a ,可以正常響應(yīng),此時后臺收到一個https://localhost:port1/a,你要怎么響應(yīng)?

要知道這兩個編碼大不一樣。所以就出現(xiàn)了本文開頭的問題。

如果不想走尋常路,可以自己寫個Socket ,連到 Tomcat Server上,發(fā)個不合法的請求,大概也是一個樣子。

那出現(xiàn)了這類問題怎么排查呢? 別忘了 Tomcat 提供了一系列有用的 Valve ,其中一個查看請求的叫AccessLogValve(閥門(Valve)常打開,快發(fā)請求過來 | Tomcat的AccessLogValve介紹)

在 Log里可以查看每個到達(dá)的請求來源IP,請求協(xié)議,響應(yīng)狀態(tài),請求方法等。但是如果上面的異常產(chǎn)生時,請求方法這類有問題的內(nèi)容也是拿不到的,此時的response status 是400 。但通過IP我們能看到是誰在一直請求。如果判斷是非法請求后,可以再增加我們的過濾Valve,直接將其設(shè)置為Deny就OK了。

【本文為專欄作者“侯樹成”的原創(chuàng)稿件,轉(zhuǎn)載請通過作者微信公眾號『Tomcat那些事兒』獲取授權(quán)】


本文標(biāo)題:一個有意思的Tomcat異常
網(wǎng)站網(wǎng)址:http://www.dlmjj.cn/article/coshcio.html