<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Jakarta Commons HttpClient 學習筆記(轉)

    1、HttpClient的功能



    1. 基于標準,純正java,實現了http1.0和1.1。
    2. 在一個可擴展的OO框架內,實現了HTTP的全部方法(GET, POST,
      PUT, DELETE, HEAD, OPTIONS, and TRACE)
    3. 支持HTTPS(ssl上的HTTP)的加密操作
    4. 透明地穿過HTTP代理建立連接
    5. 通過CONNECT方法,利用通過建立穿過HTTP代理的HTTPS連接
    6. 利用本地Java socket,透明地穿過SOCKS(版本5和4)代理建立連接
    7. 支持利用Basic、Digest和NTLM加密的認證
    8. 支持用于上傳大文件的Multi-Part表單POST方法
    9. 插件式安全socket實現,易于使用第三方的解決方案
    10. 連接管理,支持多線程應用,支持設定單個主機總連接和最高連接數量,自動檢測和關閉失效連接
    11. 直接將請求信息流送到服務器的端口
    12. 直接讀取從服務器的端口送出的應答信息
    13. 支持HTTP/1.0中用KeepAlive和HTTP/1.1中用persistance設置的持久連接
    14. 直接訪問由服務器送出的應答代碼和頭部信息
    15. 可設置連接超時時間

    16. HttpMethods 實現Command Pattern,以允許并行請求或高效連接復用
    17. 遵循the Apache Software License協議,源碼免費可得

    2、預備工作


      對jre1.3.*,如果要HttpClient支持https,則需要下載并安裝java.sun.com/products/jsse/">jsse和java.sun.com/products/jce/">jce.安裝的步驟如下:
    1)下載jsse和jce.
    2)檢查CLASSPATH中沒有與jsse和jce相關的jar包
    3)將 US_export_policy.jar、local_policy.jar、jsse.jar、jnet.jar、jce1_2_x.jar、sunjce_provider.jar、jcert.jar復制到目錄:
    UNIX:$JDK_HOME/jre/lib/ext
    Windows:%JDK_HOME%\jre\lib\ext
    4)修改下述目錄下的java.security文件。
    UNIX:$JDK_HOME/jre/lib/security/
    Windows:%JDK_HOME%\jre\lib\security\
    5)

    #
    # List of providers and their preference orders:
    #
    security.provider.1=sun.security.provider.Sun
    security.provider.2=com.sun.rsajca.Provider
    改為:
    #
    # List of providers and their preference orders:
    #
    security.provider.1=com.sun.crypto.provider.SunJCE
    security.provider.2=sun.security.provider.Sun
    security.provider.3=com.sun.rsajca.Provider
    security.provider.4=com.sun.net.ssl.internal.ssl.Provider

      HttpClient還要求安裝commons-logging,下面跟httpclient一塊安裝。

    3、取得源碼


    cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic login
    password: anoncvs
    cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic checkout jakarta-commons/logging
    cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic checkout jakarta-commons/httpclient

      編譯:
    cd jakarta-commons/logging
    ant dist
    cp dis/*.jar ../httpclient/lib/
    cd ../httpclient
    ant dist

    4、使用HttpClient編程的基本步聚


    1. 創建 HttpClient 的一個實例.
    2. 創建某個方法(DeleteMethod,EntityEnclosingMethod,ExpectContinueMethod,GetMethod,HeadMethod,MultipartPostMethod,OptionsMethod,PostMethod,PutMethod,TraceMethod)的一個實例,一般可用要目標URL為參數。
    3. 讓 HttpClient 執行這個方法.
    4. 讀取應答信息.
    5. 釋放連接.
    6. 處理應答.

      在執行方法的過程中,有兩種異常,一種是HttpRecoverableException,表示偶然性錯誤發生,一般再試可能成功,另一種是IOException,嚴重錯誤。
      這兒有這個教程中的一個例程,可以java">下載。

    5、認證


      HttpClient三種不同的認證方案: Basic, Digest and NTLM. 這些方案可用于服務器或代理對客戶端的認證,簡稱服務器認證或代理認證。
    1)服務器認證(Server Authentication)
      HttpClient處理服務器認證幾乎是透明的,僅需要開發人員提供登錄信息(login credentials)。登錄信息保存在HttpState類的實例中,可以通過 setCredentials(String realm, Credentials cred)和getCredentials(String realm)來獲取或設置。注意,設定對非特定站點訪問所需要的登錄信息,將realm參數置為null. HttpClient內建的自動認證,可以通過HttpMethod類的setDoAuthentication(boolean doAuthentication)方法關閉,而且這次關閉只影響HttpMethod當前的實例。
      搶先認證(Preemptive Authentication)可以通過下述方法打開.
    client.getState().setAuthenticationPreemptive(true);
      在這種模式時,HttpClient會主動將basic認證應答信息傳給服務器,即使在某種情況下服務器可能返回認證失敗的應答,這樣做主要是為了減少連接的建立。為使每個新建的 HttpState實例都實行搶先認證,可以如下設置系統屬性。
    setSystemProperty(Authenticator.PREEMPTIVE_PROPERTY, "true");

    Httpclient實現的搶先認證遵循rfc2617.
    2)代理認證(proxy authentication)
      除了登錄信息需單獨存放以外,代理認證服務器認證幾乎一致。用 setProxyCredentials(String realm, Credentials cred)和 getProxyCredentials(String realm)設、取登錄信息。
    3)認證方案(authentication schemes)
      Basic
      是HTTP中規定最早的也是最兼容(?)的方案,遺憾的是也是最不安全的一個方案,因為它以明碼傳送用戶名和密碼。它要求一個UsernamePasswordCredentials實例,可以指定服務器端的訪問空間或采用默認的登錄信息。
      Digest
      是在HTTP1.1中增加的一個方案,雖然不如Basic得到的軟件支持多,但還是有廣泛的使用。Digest方案比Basic方案安全得多,因它根本就不通過網絡傳送實際的密碼,傳送的是利用這個密碼對從服務器傳來的一個隨機數(nonce)的加密串。它要求一個UsernamePasswordCredentials實例,可以指定服務器端的訪問空間或采用默認的登錄信息。
      NTLM
      這是HttpClient支持的最復雜的認證協議。它M$設計的一個私有協議,沒有公開的規范說明。一開始由于設計的缺陷,NTLM的安全性比Digest差,后來經過一個ServicePack補丁后,安全性則比較Digest高。NTLM需要一個NTCredentials實例. 注意,由于NTLM不使用訪問空間(realms)的概念,HttpClient利用服務器的域名作訪問空間的名字。還需要注意,提供給NTCredentials的用戶名,不要用域名的前綴 - 如: "adrian" 是正確的,而 "DOMAIN\adrian" 則是錯的.
      NTLM認證的工作機制與basic和digest有很大的差別。這些差別一般由HttpClient處理,但理解這些差別有助避免在使用NTLM認證時出現錯誤。
    1. 從HttpClientAPI的角度來看,NTLM與其它認證方式一樣的工作,差別是需要提供'NTCredentials'實例而不是'UsernamePasswordCredentials'(其實,前者只是擴展了后者)
    2. 對NTLM認證,訪問空間是連接到的機器的域名,這對多域名主機會有一些麻煩.只有HttpClient連接中指定的域名才是認證用的域名。建議將realm設為null以使用默認的設置。
    3. NTLM只是認證了一個連接而不是一請求,所以每當一個新的連接建立就要進行一次認證,且在認證的過程中保持連接是非常重要的。 因此,NTLM不能同時用于代理認證服務器認證,也不能用于http1.0連接或服務器不支持持久連接的情況。

    6、重定向


      由于技術限制,以及為保證2.0發布版API的穩定,HttpClient還不能自動處重定向,但對重定向到同一主機、同一端口且采用同一協議的情況HttpClient可以支持。不能自動的處理的情況,包括需要人工交互的情況,或超出httpclient的能力。
      當服務器重定向指令指到不同的主機時,HttpClient只是簡單地將重定向狀態碼作為應答狀態。所有的300到399(包含兩端)的返回碼,都表示是重定向應答。常見的有:
    1. 301 永久移動. HttpStatus.SC_MOVED_PERMANENTLY
    2. 302 臨時移動. HttpStatus.SC_MOVED_TEMPORARILY
    3. 303 See Other. HttpStatus.SC_SEE_OTHER
    4. 307 臨時重定向. HttpStatus.SC_TEMPORARY_REDIRECT

      當收到簡單的重定向時,程序應從HttpMethod對象中抽取新的URL并將其下載。另外,限制一下重定向次數是個好的主意,這可以避免遞歸循環。新的URL可以從頭字段Location中抽取,如下:
    String redirectLocation;
    Header locationHeader = method.getResponseHeader("location");
    if (locationHeader != null) {
    redirectLocation = locationHeader.getValue();
    } else {
    // The response is invalid and did not provide the new location for
    // the resource. Report an error or possibly handle the response
    // like a 404 Not Found error.
    }

    特殊重定向:
    1. 300 多重選擇. HttpStatus.SC_MULTIPLE_CHOICES
    2. 304 沒有改動. HttpStatus.SC_NO T_MODIFIED
    3. 305 使用代理. HttpStatus.SC_USE_PROXY
      

    7、字符編碼(character encoding)


      一個HTTP協議的請求或應答的頭部(在http協議中,數據包分為兩部分,一部分是頭部,由一些名值對構成,一部分是主體(body),是真正傳辦理的數據(如HTML頁面等)),必須以US-ASCII編碼,這是因為頭部不傳數據而只描述被要傳輸的數據的一些信息,一個例外是cookie,它是數據但是通過頭部進行傳輸的,所以它也要用US-ASCII編碼。
      HTTP數據包的主體部分,可以用任何一種方式進行編碼,默認是ISO-8859-1,具體可以用頭部字段Content-Type指定??梢岳?addRequestHeader方法,設定編碼方式;用 getResponseCharSet取得編碼方式。對HTML或XML等類型的文檔,它們的本身的Content-Type也可以指定編碼方式,主要區分兩者的作用范圍以得到正確實的解碼。
      URL的編碼標準,由RFC1738指定為,只能是由可打印8位/字節的us-ascii字符組成,80-ff不是us-ascii字符,而00-1F是控制字符,這兩個區域中用的字符都須加以編碼(encoded)。
      

    8、Cookies


       HttpClient能自動管理cookie,包括允許服務器設置cookie并在需要的時候自動將cookie返回服務器,它也支持手工設置cookie后發送到服務器端。不幸的是,對如何處理cookie,有幾個規范互相沖突:Netscape Cookie 草案, RFC2109, RFC2965,而且還有很大數量的軟件商的cookie實現不遵循任何規范. 為了處理這種狀況,HttpClient提供了策略驅動的cookie管理方式。HttpClient支持的cookie規范有:
    1. Netscape cookie草案,是最早的cookie規范,基于rfc2109。盡管這個規范與rc2109有較大的差別,這樣做可以與一些服務器兼容。
    2. rfc2109,是w3c發布的第一個官方cookie規范。理論上講,所有的服務器在處理cookie(版本1)時,都要遵循此規范,正因如此,HttpClient將其設為默認的規范。遺憾的是,這個規范太嚴格了,以致很多服務器不正確的實施了該規范或仍在作用Netscape規范。在這種情況下,應使用兼容規范。
    3. 兼容性規范,設計用來兼容盡可能多的服務器,即使它們并沒有遵循標準規范。當解析cookie出現問題時,應考慮采用兼容性規范。

       RFC2965規范暫時沒有被HttpClient支持(在以后的版本為會加上),它定義了cookie版本2,并說明了版本1cookie的不足,RFC2965有意有久取代rfc2109.
      在HttpClient中,有兩種方法來指定cookie規范的使用,
    1. HttpClient client = new HttpClient();
      client.getState().setCookiePolicy(CookiePolicy.COMPATIBILITY);
      這種方法設置的規范只對當前的HttpState有效,參數可取值CookiePolicy.COMPATIBILITY,CookiePolicy.NETSCAPE_DRAFT或CookiePolicy.RFC2109。
    2. System.setProperty("apache.commons.httpclient.cookiespec", "COMPATIBILITY");
      此法指的規范,對以后每個新建立的HttpState對象都有效,參數可取值"COMPATIBILITY","NETSCAPE_DRAFT"或"RFC2109"。
        常有不能解析cookie的問題,但更換到兼容規范大都能解決。
      

    9、使用HttpClient遇到問題怎么辦?


    1. 用一個瀏覽器訪問服務器,以確認服務器應答正常
    2. 如果在使代理,關掉代理試試
    3. 另找一個服務器來試試(如果運行著不同的服務器軟件更好)
    4. 檢查代碼是否按教程中講的思路編寫
    5. 設置log級別為debug,找出問題出現的原因
    6. 打開wiretrace,來追蹤客戶端與服務器的通信,以確實問題出現在什么地方
    7. 用telnet或netcat手工將信息發送到服務器,適合于猜測已經找到了原因而進行試驗時
    8. 將netcat以監聽方式運行,用作服務器以檢查httpclient如何處理應答的。
    9. 利用最新的httpclient試試,bug可能在最新的版本中修復了
    10. 向郵件列表求幫助
    11. 向bugzilla報告bug.
      

    10、SSL


      借助Java Secure Socket Extension (JSSE),HttpClient全面支持Secure Sockets Layer (SSL)或IETF Transport Layer Security (TLS)協議上的HTTP。JSSE已經jre1.4及以后的版本中,以前的版本則需要手工安裝設置,具體過程參見java.sun.com/products/jsse/doc/guide/API_users_guide.html#Installation">Sun網站或本學習筆記。
      HttpClient中使用SSL非常簡單,參考下面兩個例子:
    HttpClient httpclient = new HttpClient();
    GetMethod httpget = new GetMethod("https://www.verisign.com/");
    httpclient.executeMethod(httpget);
    System.out.println(httpget.getStatusLine().toString());
    ,如果通過需要授權的代理,則如下:
    HttpClient httpclient = new HttpClient();
    httpclient.getHostConfiguration().setProxy("myproxyhost", 8080);
    httpclient.getState().setProxyCredentials("my-proxy-realm", " myproxyhost",
    new UsernamePasswordCredentials("my-proxy-username", "my-proxy-password"));
    GetMethod httpget = new GetMethod("https://www.verisign.com/");
    httpclient.executeMethod(httpget);
    System.out.println(httpget.getStatusLine().toString());

      在HttpClient中定制SSL的步驟如下:
    1. 提供了一個實現了org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory接口的socket factory。這個 socket factory負責打一個到服務器的端口,使用標準的或第三方的SSL函數庫,并進行象連接握手等初始化操作。通常情況下,這個初始化操作在端口被創建時自動進行的。
    2. 實例化一個org.apache.commons.httpclient.protocol.Protocol對象。創建這個實例時,需要一個合法的協議類型(如https),一個定制的socket factory,和一個默認的端中號(如https的443端口).
      Protocol myhttps = new Protocol("https", new MySSLSocketFactory(), 443);
      然后,這個實例可被設置為協議的處理器。
      HttpClient httpclient = new HttpClient();
      httpclient.getHostConfiguration().setHost("www.whatever.com", 443, myhttps);
      GetMethod httpget = new GetMethod("/");
      httpclient.executeMethod(httpget);

    3. 通過調用Protocol.registerProtocol方法,將此定制的實例,注冊為某一特定協議的默認的處理器。由此,可以很方便地定制自己的協議類型(如myhttps)。
      Protocol.registerProtocol("myhttps",
      new Protocol("https", new MySSLSocketFactory(), 9443));
      ...
      HttpClient httpclient = new HttpClient();
      GetMethod httpget = new GetMethod("myhttps://www.whatever.com/");
      httpclient.executeMethod(httpget);
      如果想用自己定制的處理器取代https默認的處理器,只需要將其注冊為"https"即可。
      Protocol.registerProtocol("https",
      new Protocol("https", new MySSLSocketFactory(), 443));
      HttpClient httpclient = new HttpClient();
      GetMethod httpget = new GetMethod("https://www.whatever.com/");
      httpclient.executeMethod(httpget);

      已知的限制和問題
    1. 持續的SSL連接在Sun的低于1.4JVM上不能工作,這是由于JVM的bug造成。
    2. 通過代理訪問服務器時,非搶先認證( Non-preemptive authentication)會失敗,這是由于HttpClient的設計缺陷造成的,以后的版本中會修改。

      遇到問題的處理
      很多問題,特別是在jvm低于1.4時,是由jsse的安裝造成的。
      下面的代碼,可作為最終的檢測手段。
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.Writer;
    import java.net.Socket; import javax.net.ssl.SSLSocketFactory; public class Test {

    public static final String TARGET_HTTPS_SERVER = "www.verisign.com";
    public static final int TARGET_HTTPS_PORT = 443;

    public static void main(String[] args) throws Exception {

    Socket socket = SSLSocketFactory.getDefault().
    createSocket(TARGET_HTTPS_SERVER, TARGET_HTTPS_PORT);
    try {
    Writer out = new OutputStreamWriter(
    socket.getOutputStream(), "ISO-8859-1");
    out.write("GET / HTTP/1.1\r\n");
    out.write("Host: " + TARGET_HTTPS_SERVER + ":" +
    TARGET_HTTPS_PORT + "\r\n");
    out.write("Agent: SSL-TEST\r\n");
    out.write("\r\n");
    out.flush();
    BufferedReader in = new BufferedReader(
    new InputStreamReader(socket.getInputStream(), "ISO-8859-1"));
    String line = null;
    while ((line = in.readLine()) != null) {
    System.out.println(line);
    }
    } finally {
    socket.close();
    }
    }
    }

      

    11、httpclient的多線程處理


      使用多線程的主要目的,是為了實現并行的下載。在httpclient運行的過程中,每個http協議的方法,使用一個HttpConnection實例。由于連接是一種有限的資源,每個連接在某一時刻只能供一個線程和方法使用,所以需要確保在需要時正確地分配連接。HttpClient采用了一種類似jdbc連接池的方法來管理連接,這個管理工作由 MultiThreadedHttpConnectionManager完成。
    MultiThreadedHttpConnectionManager connectionManager =
    new MultiThreadedHttpConnectionManager();
    HttpClient client = new HttpClient(connectionManager);
    此是,client可以在多個線程中被用來執行多個方法。每次調用HttpClient.executeMethod() 方法,都會去鏈接管理器申請一個連接實例,申請成功這個鏈接實例被簽出(checkout),隨之在鏈接使用完后必須歸還管理器。管理器支持兩個設置:
    maxConnectionsPerHost 每個主機的最大并行鏈接數,默認為2
    maxTotalConnections 客戶端總并行鏈接最大數,默認為20

      管理器重新利用鏈接時,采取早歸還者先重用的方式(least recently used approach)。
      由于是使用HttpClient的程序而不是HttpClient本身來讀取應答包的主體,所以HttpClient無法決定什么時間連接不再使用了,這也就要求在讀完應答包的主體后必須手工顯式地調用releaseConnection()來釋放申請的鏈接。
    MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
    HttpClient client = new HttpClient(connectionManager);
    ...
    // 在某個線程中。
    GetMethod get = new GetMethod("http://jakarta.apache.org/");
    try {
    client.executeMethod(get);
    // print response to stdout
    System.out.println(get.getResponseBodyAsStream());
    } finally {
    // be sure the connection is released back to the connection
    // manager
    get.releaseConnection();
    }
    對每一個HttpClient.executeMethod須有一個method.releaseConnection()與之匹配.

    12、HTTP方法


      HttpClient支持的HTTP方法有8種,下面分述之。

      1、Options

      HTTP方法Options用來向服務器發送請求,希望獲得針對由請求URL(request url)標志的資源在請求/應答的通信過程可以使用的功能選項。通過這個方法,客戶端可以在采取具體行動之前,就可對某一資源決定采取什么動作和/或以及一些必要條件,或者了解服務器提供的功能。這個方法最典型的應用,就是用來獲取服務器支持哪些HTTP方法。
      HttpClient中有一個類叫OptionsMethod,來支持這個HTTP方法,利用這個類的getAllowedMethods方法,就可以很簡單地實現上述的典型應用。
    OptionsMethod options = new OptionsMethod("http://jakarta.apache.org");
    // 執行方法并做相應的異常處理
    ...
    Enumeration allowedMethods = options.getAllowedMethods();
    options.releaseConnection();

      2、Get

       HTTP方法GET用來取回請求URI(request-URI)標志的任何信息(以實體(entity)的形式),"get"這個單詞本意就是”獲取“的意思。如果請求URI指向的一個數據處理過程,那這個過程生成的數據,在應答中以實體的形式被返回,而不是將這個過程的代碼的返回。
      如果HTTP包中含有If-ModifiedSince, If-Unmodified-Since, If-Match, If-None-Match, 或 If-Range等頭字段,則GET也就變成了”條件GET“,即只有滿足上述字段描述的條件的實體才被取回,這樣可以減少一些非必需的網絡傳輸,或者減少為獲取某一資源的多次請求(如第一次檢查,第二次下載)。(一般的瀏覽器,都有一個臨時目錄,用來緩存一些網頁信息,當再次瀏覽某個頁面的時候,只下載那些修改過的內容,以加快瀏覽速度,就是這個道理。至于檢查,則常用比GET更好的方法HEAD來實現。)如果HTTP包中含有Range頭字段,那么請求URI指定的實體中,只有決定范圍條件的那部分才被取回來。(用過多線程下載工具的朋友,可能比較容易理解這一點)
      這個方法的典型應用,用來從web服務器下載文檔。HttpClient定義了一個類叫GetMethod來支持這個方法,用GetMethod類中getResponseBody, getResponseBodyAsStream 或 getResponseBodyAsString函數就可以取到應答包包體中的文檔(如HTML頁面)信息。這這三個函數中,getResponseBodyAsStream通常是最好的方法,主要是因為它可以避免在處理下載的文檔之前緩存所有的下載的數據。
    GetMethod get = new GetMethod("http://jakarta.apache.org");
    // 執行方法,并處理失敗的請求.
    ...
    InputStream in = get.getResponseBodyAsStream();
    // 利用輸入流來處理信息。
    get.releaseConnection();

      對GetMethod的最常見的不正確的使用,是沒有將全部的應答主體的數據讀出來。還有,必須注意要手工明確地將鏈接釋放。

      3、Head

      HTTP的Head方法,與Get方法完全一致,唯一的差別是服務器不能在應答包中包含主體(message-body),而且一定不能包含主體。使用這個方法,可以使得客戶無需將資源下載回就可就以得到一些關于它的基本信息。這個方法常用來檢查超鏈的可訪問性以及資源最近有沒有被修改。
      HTTP的head方法最典型的應用,是獲取資源的基本信息。HttpClient定義了HeadMethod類支持這個方法,HeadMethod類與其它*Method類一樣,用 getResponseHeaders()取回頭部信息,而沒有自己的特殊方法。
    HeadMethod head = new HeadMethod("http://jakarta.apache.org");
    // 執行方法,并處理失敗的請求.
    ...
    // 取回應答包的頭字段信息.
    Header[] headers = head.getResponseHeaders(); // 只取回最后修改日期字段的信息.
    String lastModified = head.getResponseHeader("last-modified").getValue();


      4、Post

      Post在英文有“派駐”的意思,HTTP方法POST就是要求服務器接受請求包中的實體,并將其作為請求URI的下屬資源。從本質上說,這意味著服務器要保存這個實體信息,而且通常由服務器端的程序進行處理。Post方法的設計意圖,是要以一種統一的方式實現下列功能:
    1. 對已有的資源做評注
    2. 將信息發布到BBS、新聞組、郵件列表,或類似的文章組中
    3. 將一塊數據,提交給數據處理進程
    4. 通過追加操作,來擴展一個數據庫
      這些都操作期待著在服務器端產生一定的“副作用”,如修改了數據庫等。
      HttpClient定義PostMethod類以支持該HTTP方法,在httpclient中,使用post方法有兩個基本的步驟:為請求包準備數據,然后讀取服務器來的應答包的信息。通過調用 setRequestBody()函數,來為請求包提供數據,它可以接收三類參數:輸入流、名值對數組或字符串。至于讀取應答包需要調用 getResponseBody* 那一系列的方法,與GET方法處理應答包的方法相同。
      常見問題是,沒有將全部應答讀?。o論它對程序是否有用),或沒有釋放鏈接資源。

    posted on 2008-02-18 17:49 劉錚 閱讀(410) 評論(0)  編輯  收藏 所屬分類: JAVA General

    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    導航

    統計

    留言簿(1)

    文章分類(141)

    文章檔案(147)

    搜索

    最新評論

    主站蜘蛛池模板: 亚洲精品无播放器在线播放 | 波多野结衣免费一区视频| 欧洲精品免费一区二区三区 | 在线观看免费亚洲| 亚洲国产精品无码观看久久| 免费看片免费播放| 国产精品观看在线亚洲人成网| 扒开双腿猛进入爽爽免费视频 | 黑人粗长大战亚洲女2021国产精品成人免费视频 | 亚洲欧美日韩一区二区三区| 特级做A爰片毛片免费69 | 成年轻人网站色免费看| 亚洲精品无AMM毛片| 国产伦精品一区二区三区免费迷| 国产亚洲福利精品一区二区| 亚洲一区视频在线播放| 国产色爽免费无码视频| 亚洲精品午夜视频| 在线jlzzjlzz免费播放| 成人嫩草影院免费观看| 亚洲V无码一区二区三区四区观看 亚洲αv久久久噜噜噜噜噜 | 国产一级高青免费| 亚洲经典在线观看| 日本特黄特色免费大片| 一道本不卡免费视频| 久热综合在线亚洲精品| 永久免费av无码网站韩国毛片| 亚洲精品第一国产综合亚AV| www国产亚洲精品久久久日本| 免费一级不卡毛片| 亚洲精品无码久久久久秋霞 | 亚洲精品视频免费观看| 无码国产精品一区二区免费vr | 免费视频淫片aa毛片| A毛片毛片看免费| 色在线亚洲视频www| 国产亚洲情侣一区二区无码AV| 亚洲毛片免费观看| 无套内谢孕妇毛片免费看看| 亚洲无删减国产精品一区| 日本免费中文字幕在线看|