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

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

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

    上善若水
    In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
    posts - 146,comments - 147,trackbacks - 0

    概述

    Servlet是Server Applet的縮寫,即在服務器端運行的小程序,而Servlet框架則是對HTTP服務器(Servlet Container)和用戶小程序中間層的標準化和抽象。這一層抽象隔離了HTTP服務器的實現細節,而Servlet規范定義了各個類的行為,從而保證了這些“服務器端運行的小程序”對服務器實現的無關性(即提升了其可移植性)。
    在Servlet規范有以下幾個核心類(接口):
    ServletContext:定義了一些可以和Servlet Container交互的方法。
    Registration:實現Filter和Servlet的動態注冊。
    ServletRequest(HttpServletRequest):對HTTP請求消息的封裝。
    ServletResponse(HttpServletResponse):對HTTP響應消息的封裝。
    RequestDispatcher:將當前請求分發給另一個URL,甚至ServletContext以實現進一步的處理。
    Servlet(HttpServlet):所有“服務器小程序”要實現了接口,這些“服務器小程序”重寫doGet、doPost、doPut、doHead、doDelete、doOption、doTrace等方法(HttpServlet)以實現響應請求的相關邏輯。
    Filter(FilterChain):在進入Servlet前以及出Servlet以后添加一些用戶自定義的邏輯,以實現一些橫切面相關的功能,如用戶驗證、日志打印等功能。
    AsyncContext:實現異步請求處理。

    ServletContext

    Context在這里是指一個Web Application的上下文(Web Application是一個Server子URL下的Servlet和資源的集合),即它包含了這個Web Application級別的信息,如當前Web Application對應的根路徑、使用的Servlet版本、使用的ClassLoader等,在一個JVM中的一個Web Application只能有一個Context(一個JVM可以包含多個Web Application,它們包含不同的根路徑,即不同的Context路徑,Context路徑可以是空("/")即這個JVM只能包含一個Web Application)。ServletContext則是對這個Context的抽象,它還定義了一些和Servlet Container交互的方法,如獲取文件的MINE Type、Dispatch請求到另一個URL或Context、將日志寫入文件、根據提供的路徑獲取Resource實例、向這個ServletContext注冊并獲取Servlet或Filter、向這個ServletContext注冊并獲取Attribute或初始參數、向這個ServletContext注冊或獲取相關Listener等。對Distributed的Web Application來說,每個JVM下的Web Application都有一個獨立的ServletContext,因而ServletContext不可以作為全局信息存儲的地方,因而它并沒有分布式信息同步的功能,即它只是本地的ServletContext。在Servlet中,使用ServletConfig實例可以獲取ServletContext實例。
    類圖如下:

    ServletContext的接口定義如下:
    public interface ServletContext {
        // Servlet Container為當前Web Application設置臨時目錄,并將該臨時目錄的值存儲到當前ServletContext的屬性中使用的屬性名。
        
    // Jetty使用WebInfConfiguration(在preConfig ure()方法中)來設置該值,設置temp目錄的規則:
        // 1. 如果存在WEB-INF/work目錄,則temp目錄的值為:WEB-INF/work/Jetty_<host>_<port>__<resourceBase>_<context>_<virtualhost+base36_hashcode_of_whole_string>
        // 2. 如果"javax.servlet.context.tempdir"已經在外部被設置,并且該目錄存在且可寫,則temp目錄直接設置為該目錄實例。
        // 3. 如果系統變量中"jetty.home"目錄下存在"work"目錄且可寫,則設置temp目錄的值為:${jetty.home}/work/Jetty_<host>_<port>__<resourceBase>_<context>_<virtualhost+base36_hashcode_of_whole_string>
        // 4. 如果存在"org.eclipse.jetty.webapp.basetempdir"的屬性,且該目錄存在并可寫,設置temp目錄為:${org.eclipse.jetty.webapp.basetempdir}/Jetty_<host>_<port>__<resourceBase>_<context>_<virtualhost+base36_hashcode_of_whole_string>
        // 5. 如果以上條件都不成立,則設置temp目錄為:${java.io.tmpdir}/Jetty_<host>_<port>__<resourceBase>_<context>_<virtualhost+base36_hashcode_of_whole_string>,且刪除已存在臨時目錄。
        // 注:對temp目錄的父目錄不是work,會注冊在JVM退出時刪除該temp目錄,并在temp目錄下創建.active目錄。

        public static final String TEMPDIR = "javax.servlet.context.tempdir";
        // Servlet 3.0中新引入的特性,即可以在WEB-INF/lib下的jar包中定義/META-INF/web-fragment配置響應的Servlet等。
        // 如果在web.xml文件中定義了absolute-ordering,或者在jar包中存在/META-INF/web-fragment.xml文件,且定義了ordering,
        // 則該屬性的值即為根據規范中定義的規則計算出來的讀取jar包中web-fragment.xml文件的jar包名順序,它時一個List<String>類型,包含jar包名字。
        // 在Jetty中使用Ordering來表示這種順序,它有兩個實現類:AbsoluteOrdering和RelativeOrdering用來分別表示在web.xml和web-fragment.xml中定義的absolute-ordering和ordering定義,
        /并且將最終的解析結果匯總到Metadata類中,并根據規范中定義的規則以及metadata-complete的定義來計算實際的解析順序
        /而對這兩種配置文件的解析由WebDescriptor和FragmentDescriptor來實現,它們都包含了metadata-complete解析,而真正的解析入口在WebXmlConfiguration和FragmentConfiguration中。
        // 該規范的定義參考:https://blogs.oracle.com/swchan/entry/servlet_3_0_web_fragment
        public static final String ORDERED_LIBS = "javax.servlet.context.orderedLibs";

        // 返回當前Web Application的Context Path,即當前Web Application的根路徑,Servlet Container根據該路徑以及一個Request URL的匹配情況來選擇一個Request應該交給那個Web Application處理該請求。
        // Context Path以"/"開始,但是不能以"/"結尾,對默認的根Context,它返回"",而不是"/"。該值在配置Jetty的ContextHandler中設置。
        // 有些Servlet Container支持多個Context Path指向同一個Context,此時可以使用HttpServletRequest中的getContextPath()來獲取該Request實際對應的Context Path,此時這兩個Context Path的值可能會不同,但是ServletContext中返回的Context Path值是主要的值。另外Jetty也不支持這種特性。
        public String getContextPath();

        // 通過給定一個Context Path以在當前Servlet Container中找到其對應的ServletContext實例。
        // 可以通過該方法獲取Servlet Container中定義的另一個Web Application的ServletContext實例,并獲得其RequestDispatcher,并將當前請求Dispatch到那個Web Application中做進一步的處理。這里的uripath必須以"/"開頭,且其路徑相對于當前Server的根路徑。出于安全考慮,該方法可能會返回null。
        // 在Jetty的實現中,這里uripath可以是一個具體的路徑,并且支持查找最準確的匹配。如:對uripath為/foo/goo/abc.html,在Server中由以下Context Path定義:"/", "/foo", "/foo/goo",則最終查找到的ServletContext為"/foo/goo"作為Context Path對應的ServletContext實例。
        public ServletContext getContext(String uripath);
        
        //返回Servlet規范的主版本,如3
        public int getMajorVersion();
        // 返回Servlet規范的次版本,如0
        public int getMinorVersion();
        // 返回當前Web Application基于的Servlet規范的主版本,如在web.xml文件中定義的version(<web-app version="..." ...>...</web-app>,即Jetty中的實現)
        public int getEffectiveMajorVersion();
        /返回當前Web Application基于的Servlet規范的次版本,如在web.xml文件中定義的version(<web-app version="..." ...>...</web-app>,即Jetty中的實現)
        public int getEffectiveMinorVersion();


        // 返回給定file的MIME type,返回null如果無法計算出其MIME type。這個映射關系由Servlet Container定義或在web.xml文件中定義(mime-mapping, extension, mine-type)。
        // 常見的MIME type有:text/html, image/gif等。Jetty使用MimeTypes類來封裝所有和MIME type相關的操作,MimeTypes類中定義了所有默認支持的MIME type以及編碼類型,
        // 并且默認從org/eclipse/jetty/http/mime.properties文件中加載默認的映射,如css=text/css, doc=application/msword等,使用addMimeMapping()方法向該類注冊web.xml中定義的文件名擴展名到MIME type的映射。
        // 而從org/eclipse/jetty/http/encoding.properties文件中加載MIME type的默認編碼類型,如text/xml=UTF-8等。
        // 在使用文件名查找MIME type時,根據文件名的擴展名查找已注冊或默認注冊的MIME type。用戶自定義的映射優先。用戶定義的MIME type映射支持extension為"*",表示任意擴展名。
        public String getMimeType(String file);
        
        // 對于給定目錄,返回該目錄下所有的資源以及目錄。path必須以"/"開頭,如果它不是指向一個目錄,則返回空的Set。所有返回的路徑都相對當前Web Application的根目錄,
        /或對于/WEB-INF/lib中jar包中的/META-INF/resources/目錄,如一個Web Application包含以下資源:/catalog/offers/music.html, /WEB-INF/web.xml,
        // 以及/WEB-INF/lib/catalog.jar!/META-INF/resources/catalog/moreOffers/books.html,則getResourcePaths("/catalog")返回{"/catalog/offers/", /catalog/moreOffers/"}
        // Jetty的實現中,在MetaInfConfiguration中,它會掃描WEB-INF/lib目錄下所有的jar包,如果發現在某個jar包中存在META-INF/resources/目錄,
        /就會將該目錄資源作為baseResource在WebInfConfiguration中注冊到ContextHandler(WebAppContext)中。從而實現jar包中的META-INF/resources/目錄作為根目錄的查找。  
        public Set<String> getResourcePaths(String path);
        
        // 返回給定path的URL,path必須以"/"開頭,它相對當前Web Application的根目錄或相對/WEB-INF/lib中jar包中的/META-INF/resources/目錄。
        /其中查找順序前者優先于后者,但是在/WEB-INF/lib/目錄下的jar包的查找順序未定義。該方法不同于Class.getResource()在于它不使用ClassLoader,如果沒有找到給定資源,返回null。
        // 在WebAppContext實現中,它還支持Alias查找,并且如果其extractWAR的變量為false,給定的資源在WAR包中,則該URL返回WAR包中的URL。    
        public URL getResource(String path) throws MalformedURLException;
        
        // 參考getResource(path)的查找實現,如果其返回的URL為null,則該方法返回null,否則返回URL對應的InputStream。
        public InputStream getResourceAsStream(String path);

        // 創建一個RequestDispatcher用于將一個Request、Response分發到path對應的URL中,這里path必須以"/"開頭,且它相對于當前Context Path。如果無法創建RequestDispatcher,返回null。
        // path可以包含query數據用于傳遞參數:uriInContext?param1=abc&param2=123....該方法可以和getContext(uripath)一起使用,以將當前請求分發到另一個Web Application中。
        // 該方法的另一種用法是先有一個Servlet或Filter處理基本的邏輯,然后使用這個RequestDispatcher將當前請求forward到另一個URL中或include一個JSP文件生成響應頁面,如果在處理過程中出錯,則將其當前請求分發到錯誤處理的流程中。
        // RequestDispatcher支持兩種類型的分發:forward和include,唯一的區別是include只可以改變Response的內容,不可以改變其Header信息,forward則沒有這種限制。
        public RequestDispatcher getRequestDispatcher(String path);

        // 創建一個RequestDispatcher用于將一個Request、Response分發到name對應的Servlet(JSP)中。如果沒能找到響應的Servlet,返回null。
        public RequestDispatcher getNamedDispatcher(String name);    

        // 將msg打印到Servlet對應的log文件中,在Jetty中,使用INFO級別打印,logger名稱為web.xml定義的display-name,或者context path。
        /Jetty默認使用SLF4J作為日志打印框架,可以使用"org.eclipse.jetty.util.log.class"系統屬性改變其日志打印框架。
        public void log(String msg);   

        // 打印message和throwable到Servlet對應的log文件中,在Jetty中使用WARN級別打印該信息。
        public void log(String message, Throwable throwable);

        // 返回給定path在當前機器上操作系統對應的位置。對/META-INF/resources下的資源,除非他們已經解壓到本地目錄,否則對那些資源該方法返回null。
        // 在Jetty實現中,使用getResource()方法相同的實現獲取Resource實例,如果其getFile()返回不為null,則返回該File的canonical路徑。
        public String getRealPath(String path);

        // 返回Servlet Container的名稱和版本號,其格式為:<servername>/<versionnumber>,如:Jetty/8.1.9.v20130131。
        public String getServerInfo();

        // ServletContext級別的初始參數操作,可以在web.xml中使用context-param定義,也可以手動設置。在get中如果找不到對應的項,返回null,在set時,如果已存在name,則返回false,并且不更新相應的值。
        public String getInitParameter(String name);
        public Enumeration<String> getInitParameterNames();
        public boolean setInitParameter(String name, String value);

        // ServletContext級別的屬性操作,其中屬性名遵循包命名規則。在set中,如果object為null表示移除該屬性,如果name以存在,則會替換原有的值,如果注冊了ServletContextAttributeListener,則會出發相應的attributeRemoved、attributeReplaced、attributeAdded事件。在remove中,如果name存在且被移除了,則會觸發attributeRemoved事件。
        // 在Jetty中使用ContextHandler中的Context內部類實現ServletContext,在ContextHandler中定義了兩個相關字段:_attributes以及_contextAttributes,其中_attributes表示在Jetty內部通過ContextHandler設置的屬性,而_contextAttributes表示用戶設置的屬性,但是在獲取屬性值時,兩個字段的屬性都會考慮進去,在移除屬性時,如果是移除_attributes字段中的值,則不會觸發attributeRemoved事件。
        public Object getAttribute(String name);
        public Enumeration<String> getAttributeNames();
        public void setAttribute(String name, Object object);
        public void removeAttribute(String name);

        // 返回當前Web Application在web.xml中的display-name定義的ServletContext名字,在Jetty實現中,如果該值為null,則返回context path。
        public String getServletContextName();

        // 該部分具體的使用可以參考:http://www.tkk7.com/yongboy/archive/2010/12/30/346209.html 

        // 動態的向ServletContext中注冊Servlet,注冊的Servlet還可以通過返回的ServletRegistration繼續配置,如addMapping、setInitParameter等。
        // 在使用className實例話Servlet時,使用當前ServletContext相關聯的ClassLoader。在創建Servlet實例時,會根據該類中定義的以下Annotation做相應的配置:
        // javax.servlet.annotation.ServletSecurity、javax.servlet.annotation.MultipartConfig、javax.annotation.security.RunAs、javax.annotation.security.DeclareRoles
        public ServletRegistration.Dynamic addServlet(String servletName, String className);
        public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet);
        public ServletRegistration.Dynamic addServlet(String servletName, Class <? extends Servlet> servletClass);
        // 創建給定Servlet類的Servlet實例,并且會根據該類中定義的以下Annotation做相應的配置:
        // javax.servlet.annotation.ServletSecurity、javax.servlet.annotation.MultipartConfig、javax.annotation.security.RunAs、javax.annotation.security.DeclareRoles
        // 在創建Servlet實例后,一般還要調用addServlet()方法將其注冊到ServletContext中。
        public <T extends Servlet> T createServlet(Class<T> clazz) throws ServletException;
        // 根據servletName獲取ServletRegistration實例
        public ServletRegistration getServletRegistration(String servletName);
        // 獲取所有在ServletContext中注冊的servletName到ServletRegistration映射的Map。所有動態注冊和使用配置注冊的映射。
        public Map<String, ? extends ServletRegistration> getServletRegistrations();

        // 動態的向ServletContext中注冊Filter,注冊的Filter可以通過返回的FilterRegistration進一步配置,如addMappingForUrlPatterns、setInitParameter等
        public FilterRegistration.Dynamic addFilter(String filterName, String className);
        public FilterRegistration.Dynamic addFilter(String filterName, Filter filter);
        public FilterRegistration.Dynamic addFilter(String filterName, Class <? extends Filter> filterClass);
        // 創建給定Filter類實例的Filter實例,一般都會后繼調用addFilter將該實例注冊到ServletContext中。
        public <T extends Filter> T createFilter(Class<T> clazz) throws ServletException;
        // 根據filterName獲取FilterRegistration實例。
        public FilterRegistration getFilterRegistration(String filterName);
        // 返回所有filterName到FilterRegistration映射的Map,包括所有動態注冊和使用配置注冊的映射。
        public Map<String, ? extends FilterRegistration> getFilterRegistrations();

        // 返回SessionCookieConfig實例,用于session tracking的cookie屬性,多次調用該方法返回相同的實例。
        public SessionCookieConfig getSessionCookieConfig();
        // 設置session tracking模式,可以是URL、COOKIE、SSL。Jetty8只支持URL和COOKIE。
        public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes);
        // 返回當前ServletContext默認支持的session tracking模式。
        public Set<SessionTrackingMode> getDefaultSessionTrackingModes();
        // 返回當前ServletContext目前使用的session tracking模式。
        public Set<SessionTrackingMode> getEffectiveSessionTrackingModes();

        // 向ServletContext動態的注冊Listener,該Listener類或實例必須實現以下的一個或多個接口:
        // 
    ServletContextAttributeListener、ServletRequestListener、ServletRequestAttributeListener、HttpSessionListener、HttpSessionAttributeListener}</tt>
        // 如果這個ServletContext傳入ServletContainerInitializer的onStartup方法,那么這個Listener類或實例也可以實現ServletContextListener接口。
        // 注:動態注冊的ServletContextListener中的contextInitialized方法中不可以調用Servlet 3.0中定義的這些動態注冊Servlet、Filter、Listener等方法,不然會拋出UnsupportedOperationException,看起來像是出于一致性、安全性或是兼容性的考慮,但是具體是什么原因一直想不出來。而且在Jetty實現中,它在注冊EventListener實例是確取消了這種限制,而對注冊EventListener類實例和類名確有這種限制,不知道這是Jetty的bug還是其他什么原因。。。。。

        // 對于調用順序按定義順序來的EventListener(如ServletRequestListener、ServletContextListener、HttpSessionListener),那這個新的EventListener會添加到相應列表末尾。

        public void addListener(String className);
        public <T extends EventListener> void addListener(T t);
        public void addListener(Class <? extends EventListener> listenerClass);
        // 創建clazz對應的EventListener實例,一般這個新創建的EventListener會之后注冊到ServletContext中。
        public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException; 

        // 返回web.xml和web-fragment.xml配置文件中定義<jsp-config>的匯總,或返回null如果沒有相關配置。看起來像Jetty并沒有實現該方法。
        public JspConfigDescriptor getJspConfigDescriptor();

        // 返回當前Web Application對應的ClassLoader實例。
        public ClassLoader getClassLoader();

        // 定義角色名。角色名在ServletRegistration.Dynamic.setServletSecurity()和ServletRegistration.Dynamic.setRunAsRole()中默認定義,因而不需要重新使用這個方法定義。
        public void declareRoles(String roleNames);
    }

    ServletContext初始化

    ServletContext的初始化從ContextHandler的doStart()方法開始,在其startContext()方法快結束時,會調用注冊的ServletContextListener中的contextInitialized()方法,因而這里是用戶對ServletContext初始化時做一些自定義邏輯的擴展點。

    在Servlet 3.0中還引入了ServletContainerInitializer接口,它定義了onStartup()方法,該方法會在WebAppContext中的startContext方法中的configure方法中通過ContainerInitializerConfiguration.configure()中被調用,該方法的調用要早于所有ServletContextListener .contextInitialized()事件的觸發。
    public interface ServletContainerInitializer {
        // 當ServletContext對應的Web Application初始化時,該方法會被調用。其中c參數是所有繼承、實現在ServletContainerInitializer實現類定義的HandlesTypes注解中定義的類,
        // 如果該注解定義的類數組中有注解,那么c參數還包含所有存在這個注解的類。如果實現ServletContainerInitializer接口的類在WEB-INF/lib的jar包中綁定,
        // 那么該方法只會在對應的Web Application初始化被調用一次,如果實現ServletContainerInitializer接口的類在WEB-INF/lib以外定義,但是還可以被Servlet Container找到,
        // 那么意味這這個jar包是多個Web Application共享的,因而該方法會在每個Web Application初始化時被調用。如果ServletContainerInitializer實現類沒有定義HandlesTypes注解,
        // 那么c參數為null。ServletContainerInitializer實現類的查找使用運行時service provider機制,然而對于定義在fragment jar包中的ServletContainerInitializer實現類,
        // 但是該jar在absolute ordering中被exclude了,那么該jar包會被忽略,即在web.xml中的absolute-ordering中沒有包含相應的fragment名。
        // 在查找滿足HandlesTypes注解中定義的類數組的類實例集合時,由于有些JAR包是可選的,因而在加載Class時有時會遇到問題,此時Servlet Container可以選擇忽略該錯誤,
        // 但是需要提供配置已讓Servlet Container決定是否要將這些錯誤打印到日志文件中。 
        public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException; 
    }
    要自定義ServletContainerInitializer邏輯,首先需要META-INF/services/目錄下建立一個javax.servlet.ServletContainerInitializer文件,在該文件內部寫入用戶在定義的ServletContainerInitializer類,如:x.y.z.MyServletContainerInitializer,該MyServletContainerInitializer類必須實現ServletContainerInitializer接口,如果它有一些特定感興趣的類,可以向其定義HandlesTypes注解,該注解的值可以是類實例、接口實例、注解實例,它表示所有繼承自注解中定義的類、實現注解中定義的接口、有注解中定義的注解注解的類(注解在類、字段、方法中)都會收集成一個Set<Class<?>>類型,并傳入onStartup()方法中,如果沒有HandlesTypes注解,其onStartup()中Set<Class<?>>參數為null。

    在Jetty實現中,AnnotationConfiguration會查找到所有jar包中在META-INF/services/javax.servlet.ServletContainerInitializer中定義的ServletContainerInitializer(排除那些被absolute-ordering排除在外的fragment jar包中的定義),使用這些查找到的ServletContainerInitializer創建ContainerInitializer實例,使用HandlesTypes注解中定義的Class數組初始化InterestedTypes字段,如果這個Class數組中有注解類型,則將所有存在這個注解類型的類(這個注解可以注解在該類的類、字段、方法中)添加到ContainerInitializer實例中的_annotatedTypeNames集合中(使用ContainerInitializerAnnotationHandler實現);最后將他們注冊到Context的一個屬性中。同時在AnnotationConfiguration中還會向Context中注冊一個屬性,其值是Map,它包含了所有類對應的其子類或實現類。然后在ContainerInitializerConfiguration中,對每個在Context中注冊的ContainerInitializer實例,對所有注冊的_annotatedTypeNames,將該類以及該類的子類、實現類注冊到_applicableTypeNames集合中;對所有注冊的非注解類型的_interestedTypes,將其所有的子類、實現類注冊到_applicableTypeNames集合中(在Jetty當前版本的實現中沒有包含_interestedTypes中的類實例本身,在ServletContainerInitializer的注釋中確實也沒有說明要包含這些類本身,感覺這個有點不合理。。。);最后調用ContainerInitializer中的callStartup()方法,它加載_applicableTypeNames集合中的所有類,并將其傳入ServletContainerInitializer的onStartup()方法中(這里沒有根據規范忽略不能加載成功的類實例)。
    posted on 2014-05-11 01:22 DLevin 閱讀(4690) 評論(1)  編輯  收藏 所屬分類: Jetty

    FeedBack:
    # re: 深入Jetty源碼之Servlet框架及實現(ServletContext)
    2014-05-11 09:12 | 優佰電商
    主站蜘蛛池模板: 综合久久久久久中文字幕亚洲国产国产综合一区首 | 永久黄网站色视频免费| 青青操免费在线观看| 精品国产日韩亚洲一区91 | 很黄很黄的网站免费的| 国产免费久久久久久无码| 亚洲成av人片在www鸭子| 亚洲欧洲日产国码二区首页| 国产亚洲精品va在线| 亚洲国产精品无码久久青草| 在线免费视频一区二区| 午夜毛片不卡高清免费| 亚洲精品无码不卡在线播放HE| 韩国二级毛片免费播放| 中文字幕无码免费久久99| 69精品免费视频| 99爱视频99爱在线观看免费| 免费人成激情视频在线观看冫| 黄色网页在线免费观看| 九一在线完整视频免费观看| 美女被羞羞网站免费下载| 精品一区二区三区无码免费直播 | 少妇高潮太爽了在线观看免费| 人妻丰满熟妇无码区免费| 久久精品国产免费| a级毛片高清免费视频| 怡红院免费的全部视频| 在线看片免费人成视频久网下载| 2022免费国产精品福利在线| 一级午夜免费视频| 久久高潮一级毛片免费| 三级网站免费观看| 久久国产精品免费专区| 免费的涩涩视频在线播放| 久久精品免费一区二区喷潮 | 亚洲人成影院77777| 亚洲一线产区二线产区精华| 国产日本亚洲一区二区三区| 在线aⅴ亚洲中文字幕| 久久亚洲精品无码网站| 午夜不卡AV免费|