日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁(yè) > 科技  > 軟件

如何基于 Golang 標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)插件功能

來(lái)源: 責(zé)編: 時(shí)間:2024-06-19 15:36:40 214觀看
導(dǎo)讀什么是插件簡(jiǎn)單來(lái)說(shuō),插件就是可以被其他軟件加載的軟件,通常用于擴(kuò)展應(yīng)用程序的功能和外觀,插件的開發(fā)人員甚至可以不直接修改基礎(chǔ)應(yīng)用程序。你很可能在日常生活中使用過(guò)插件,也許用的是其他名稱,如擴(kuò)展(extensions)或附加

什么是插件

簡(jiǎn)單來(lái)說(shuō),插件就是可以被其他軟件加載的軟件,通常用于擴(kuò)展應(yīng)用程序的功能和外觀,插件的開發(fā)人員甚至可以不直接修改基礎(chǔ)應(yīng)用程序。ueS28資訊網(wǎng)——每日最新資訊28at.com

你很可能在日常生活中使用過(guò)插件,也許用的是其他名稱,如擴(kuò)展(extensions)或附加組件(add-ons)。最常見的例子就是 VSCode 擴(kuò)展,你應(yīng)該用過(guò) VSCode,對(duì)吧?畢竟這是最受程序員歡迎的文本編輯器。如果你用過(guò),一定會(huì)同意 VSCode 本身就是一個(gè)文本編輯器,而不是集成開發(fā)環(huán)境。其基本功能非常簡(jiǎn)單,幾乎不支持集成開發(fā)環(huán)境中常見的功能,如調(diào)試、自動(dòng)完成和測(cè)試導(dǎo)航等。不過(guò),通過(guò)編輯器的擴(kuò)展市場(chǎng),可以找到支持這些功能以及其他更多功能的各種插件。事實(shí)上,插件已成為編輯器的主要賣點(diǎn)之一,促使工具開發(fā)人員集中精力為編輯器制作專用插件,有時(shí)甚至超越了編碼本身的范疇,就像 Figma 所做的那樣[2]。ueS28資訊網(wǎng)——每日最新資訊28at.com

ueS28資訊網(wǎng)——每日最新資訊28at.com

對(duì)于 VSCode 而言,插件是用 JavaScript 編寫的,但也有基于 Go 編寫插件的情況。例如 Terraform(云提供商基礎(chǔ)設(shè)施即代碼服務(wù)),它允許用戶為其工具編寫插件[3],從而與多個(gè)云供應(yīng)商(AWS、GCP、Azure......)進(jìn)行交互。ueS28資訊網(wǎng)——每日最新資訊28at.com

另一個(gè)例子是 API 網(wǎng)關(guān)服務(wù) Kong,它允許開發(fā)人員使用不同語(yǔ)言(包括 Go)編寫插件,這些插件[4]可以在將請(qǐng)求轉(zhuǎn)發(fā)給底層服務(wù)之前,對(duì)接收到的請(qǐng)求進(jìn)行處理。ueS28資訊網(wǎng)——每日最新資訊28at.com

免責(zé)聲明

  • 本文假設(shè)你至少對(duì) Go 語(yǔ)言有基本的了解。如果還不了解,建議先了解一下 Go[5],然后再來(lái)閱讀。
  • 本文示例代碼中的某些功能要求至少使用 Go 1.21.0 版本。
  • Windows 機(jī)器尚未支持 Go 的插件功能。如果你用的是 Windows,建議使用 WSL[6]。
  • 本文生成的代碼可在 Github 代碼庫(kù)[7]中找到。

插件的基礎(chǔ)設(shè)施

我們將插件基礎(chǔ)架構(gòu)分為三個(gè)部分:協(xié)議/API、實(shí)現(xiàn)和插件加載器。請(qǐng)注意,這種劃分不是官方標(biāo)準(zhǔn),也不是紙上談兵,而是在實(shí)際應(yīng)用中的通常做法。ueS28資訊網(wǎng)——每日最新資訊28at.com

ueS28資訊網(wǎng)——每日最新資訊28at.com

協(xié)議/API

協(xié)議是我們?nèi)我庠O(shè)置的定義和默認(rèn)值,這樣就可以在各組件之間進(jìn)行簡(jiǎn)潔的通信。和任何協(xié)議一樣,需要設(shè)定插件和基礎(chǔ)應(yīng)用程序之間的通信方式。為此,我們可以使用不同的方法,既可以通過(guò)簡(jiǎn)單的文檔解釋期望的方法,也可以定義接口庫(kù)(編程接口,如 class foo implements bar)。只要插件的實(shí)現(xiàn)遵循這些準(zhǔn)則,應(yīng)用就能調(diào)用插件代碼。ueS28資訊網(wǎng)——每日最新資訊28at.com

實(shí)現(xiàn)

我們需要編碼來(lái)實(shí)現(xiàn)協(xié)議設(shè)定的功能。也就是說(shuō),需要在插件代碼中實(shí)現(xiàn)預(yù)期的函數(shù)和變量,以便主應(yīng)用程序可以調(diào)用。ueS28資訊網(wǎng)——每日最新資訊28at.com

提醒一下,插件代碼并不局限于這些實(shí)現(xiàn)方式。ueS28資訊網(wǎng)——每日最新資訊28at.com

ueS28資訊網(wǎng)——每日最新資訊28at.com

插件加載器

這是需要由主應(yīng)用程序執(zhí)行的部分,有兩個(gè)職責(zé):查找插件并在代碼中加載其功能。ueS28資訊網(wǎng)——每日最新資訊28at.com

插件是主程序項(xiàng)目的外部組件,因此需要一種方法來(lái)查找該程序的所有插件。我們可以簡(jiǎn)單的在文件系統(tǒng)中定義一個(gè)固定的文件夾來(lái)存放所有插件,但最好是允許應(yīng)用程序用戶通過(guò)配置文件來(lái)指向他們的插件,或者兩種方式同時(shí)支持。ueS28資訊網(wǎng)——每日最新資訊28at.com

安裝所有插件后,需要在應(yīng)用程序中訪問(wèn)它們的應(yīng)用程序接口。這通常是通過(guò)鉤子實(shí)現(xiàn)的:運(yùn)行時(shí)調(diào)用插件(或插件的一部分)的部分。以 VSCode 為例,"文件加載時(shí)"就是這樣一個(gè)鉤子,因此插件可以使用這個(gè)鉤子捕捉加載的文件并據(jù)此運(yùn)行。實(shí)現(xiàn)哪些鉤子以及何時(shí)實(shí)現(xiàn)鉤子與應(yīng)用程序的邏輯有內(nèi)在聯(lián)系,只能具體問(wèn)題具體分析。ueS28資訊網(wǎng)——每日最新資訊28at.com

我們?cè)跇?gòu)建什么

學(xué)習(xí)編程的最佳方式莫過(guò)于動(dòng)手實(shí)踐。因此我們來(lái)創(chuàng)建一個(gè)使用插件的簡(jiǎn)單應(yīng)用程序。ueS28資訊網(wǎng)——每日最新資訊28at.com

我們要構(gòu)建的是一個(gè)基于插件的 HTTP 重定向服務(wù)。這是一個(gè)簡(jiǎn)單的 HTTP 服務(wù),監(jiān)聽端口中的請(qǐng)求并將其重定向到另一個(gè)服務(wù)器,同時(shí)將響應(yīng)傳遞給原始客戶端。有了這項(xiàng)服務(wù),我們就可以接入請(qǐng)求并對(duì)其進(jìn)行修改。在本例中,我們將通過(guò)插件獲取請(qǐng)求并打印。ueS28資訊網(wǎng)——每日最新資訊28at.com

至于插件加載部分,我們使用Go庫(kù)作為協(xié)議,并通過(guò)配置文件來(lái)定位插件。ueS28資訊網(wǎng)——每日最新資訊28at.com

1.開發(fā)插件協(xié)議

我們首先定義插件協(xié)議。為此,我們定義一個(gè) go 庫(kù)組件。ueS28資訊網(wǎng)——每日最新資訊28at.com

在定義該模塊之前,我們先定義應(yīng)用程序組件:ueS28資訊網(wǎng)——每日最新資訊28at.com

# From a folder you want to keep the project:mkdir http-redirectcd http-redirectgo work initgo mod init github.com/<your_github_username>/http-redirectgo work use .

當(dāng)然,你可以自行決定應(yīng)用名稱。因?yàn)樾枰鄠€(gè)模塊進(jìn)行交互,因此我們決定使用 go 工作區(qū)。要了解更多相關(guān)信息,請(qǐng)查看文檔[8]。ueS28資訊網(wǎng)——每日最新資訊28at.com

接下來(lái)可以創(chuàng)建庫(kù)組件了:ueS28資訊網(wǎng)——每日最新資訊28at.com

# From http-redirectmkdir protocolcd protocolgo mod init github.com/<your_github_username>/http-redirect/protocolgo work use . # Add new module to workspace

接下來(lái)創(chuàng)建一些文件,整個(gè)文件樹應(yīng)該是這樣的:ueS28資訊網(wǎng)——每日最新資訊28at.com

ueS28資訊網(wǎng)——每日最新資訊28at.com

我們將在 protocol.go 中開展工作。我們希望在協(xié)議中為每個(gè)請(qǐng)求調(diào)用函數(shù)。因此,我們要為插件實(shí)現(xiàn)一個(gè)名為 PreRequestHook 的函數(shù),看起來(lái)是這樣的:ueS28資訊網(wǎng)——每日最新資訊28at.com

// protocol.gopackage protocolimport "net/http"http:// Plugins should export a variable called "Plugin" which implements this interfacetype HttpRedirectPlugin interface {  PreRequestHook(*http.Request)}

代碼很簡(jiǎn)單,我們只需獲取指向 http.Request 類型的指針(因?yàn)榭赡芨恼?qǐng)求),然后將每個(gè) HTTP 請(qǐng)求傳遞給我們的服務(wù)器。我們使用的是標(biāo)準(zhǔn)庫(kù)定義的類型,但請(qǐng)注意,也可以根據(jù)應(yīng)用需求使用不同的類型。ueS28資訊網(wǎng)——每日最新資訊28at.com

就是這樣!但不要被例子的簡(jiǎn)單性所迷惑。對(duì)于大型應(yīng)用來(lái)說(shuō),這可能是一個(gè)相當(dāng)大的文件,其中包含不同的接口、默認(rèn)實(shí)現(xiàn)、配置和其他亂七八糟的東西。ueS28資訊網(wǎng)——每日最新資訊28at.com

2.實(shí)現(xiàn)插件

現(xiàn)在有了一個(gè)可遵循的協(xié)議,就可以創(chuàng)建并實(shí)現(xiàn)插件了。ueS28資訊網(wǎng)——每日最新資訊28at.com

同樣,我們?yōu)椴寮?chuàng)建一個(gè)新組件,并為其創(chuàng)建一個(gè)文件。ueS28資訊網(wǎng)——每日最新資訊28at.com

# From http-redirectmkdir log-plugincd log-plugingo mod init github.com/<your_github_username>/http-redirect/log-plugingo work use . # Add new module to workspacetouch plugin.go

現(xiàn)在的文件樹應(yīng)該是這樣的:ueS28資訊網(wǎng)——每日最新資訊28at.com

ueS28資訊網(wǎng)——每日最新資訊28at.com

我們來(lái)編寫插件!首先,創(chuàng)建一個(gè)函數(shù)來(lái)打印請(qǐng)求。ueS28資訊網(wǎng)——每日最新資訊28at.com

// log-plugin/plugin.gopackage mainimport (  "log/slog"  "net/http"  "net/http/httputil")func logRequest(req *http.Request) {  result, err := httputil.DumpRequest(req, true)  if err != nil {    slog.Error("Failed to print request", "err", err)  }  slog.Info("Request sent:", "req", result)}func logRequestLikeCUrl(req *http.Request) {  panic("Unimplemented!")}func main() { /*empty because it does nothing*/ }

這里的未實(shí)現(xiàn)函數(shù)只是為了顯示我們可以為更復(fù)雜的協(xié)議添加更多功能,只是目前還無(wú)法正確配置,因此不會(huì)使用。ueS28資訊網(wǎng)——每日最新資訊28at.com

我們要用到的是 logRequest 函數(shù),它通過(guò) go 標(biāo)準(zhǔn)庫(kù)的結(jié)構(gòu)化日志組件打印請(qǐng)求。這就完成了我們的功能,但現(xiàn)在需要導(dǎo)出插件,使其滿足協(xié)議要求。ueS28資訊網(wǎng)——每日最新資訊28at.com

你可能注意到了,有一個(gè)什么也不做的 main 函數(shù)。這是 go 編譯器的要求,因?yàn)槟承┕δ苄枰粋€(gè)入口點(diǎn)。雖然這個(gè)編譯包中存在 main 函數(shù),但不會(huì)作為可執(zhí)行文件被調(diào)用。ueS28資訊網(wǎng)——每日最新資訊28at.com

我們需要導(dǎo)入庫(kù)。一般情況下,可以使用 go get 來(lái)恢復(fù)這個(gè)庫(kù),但由于我們是在本地機(jī)器上開發(fā),因此只需在 go.mod 文件中添加庫(kù)路徑即可:ueS28資訊網(wǎng)——每日最新資訊28at.com

replace github.com/profusion/http-redirect/protocol => ../protocol

接下來(lái)我們創(chuàng)建一個(gè)實(shí)現(xiàn) HttpRedirectPlugin 接口的結(jié)構(gòu)體,并調(diào)用日志函數(shù)。ueS28資訊網(wǎng)——每日最新資訊28at.com

// log-plugin/plugin.gopackage mainimport (  //…  "github.com/<your_github_username>/http-redirect/protocol")// … previous code …type PluginStr struct{}// Compile time check for// PreRequestHook implements protocol.HttpRedirectPlugin.var _ protocol.HttpRedirectPlugin = PluginStr{}// PreRequestHook implements protocol.HttpRedirectPlugin.func (p PluginStr) PreRequestHook(req *http.Request) {  logRequest(req)}var Plugin = PluginStr{}

這就是需要的所有代碼。我們只需將其作為插件構(gòu)建即可。為此,我們只需向 go 編譯器傳遞 buildmode 標(biāo)志:ueS28資訊網(wǎng)——每日最新資訊28at.com

# From http-redirect/log-plugingo build -buildmode=plugin -o plugin.so plugin.go

瞧!我們有了一個(gè)插件!現(xiàn)在只需將其加載到應(yīng)用程序就行了。ueS28資訊網(wǎng)——每日最新資訊28at.com

3.加載插件

我們需要一個(gè)應(yīng)用程序來(lái)加載插件。這不是本文的重點(diǎn),但以下是 Go 中 HTTP 重定向服務(wù)器代碼,我們可以對(duì)其進(jìn)行修改。ueS28資訊網(wǎng)——每日最新資訊28at.com

// cmd/main.gopackage mainimport (  "flag"  "fmt"  "io"  "log/slog"  "net/http"  "strings")var from intvar to stringfunc init() {  flag.IntVar(&from, "from", 5555, "Local port to get requests")  flag.StringVar(&to, "to", "", "Target server to redirect request to")}func main() {  flag.Parse()  Listen()}type proxy struct{}func Listen() {  p := &proxy{}  srvr := http.Server{    Addr: fmt.Sprintf(":%d", from),    Handler: p,  }  if err := srvr.ListenAndServe(); err != nil {    slog.Error("Server is down", "Error", err)  }}// ServeHTTP implements http.Handler.func (p *proxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {  // Remove original URL for redirect  req.RequestURI = ""  // Set URL accordingly  req.URL.Host = to  if req.TLS == nil {    req.URL.Scheme = "http"  } else {    req.URL.Scheme = "https"  }  // Remove connection headers  // (will be replaced by redirect client)  DropHopHeaders(&req.Header)  // Register Proxy Request  SetProxyHeader(req)  // Resend request  client := &http.Client{}  resp, err := client.Do(req)  if err != nil {    http.Error(rw, "Server Error: Redirect failed", http.StatusInternalServerError)  }  defer resp.Body.Close()  // Once again, remove connection headers  DropHopHeaders(&resp.Header)    // Prepare and send response  CopyHeaders(rw.Header(), &resp.Header)  rw.WriteHeader(resp.StatusCode)  if _, err = io.Copy(rw, resp.Body); err != nil {    slog.Error("Error writing response", "error", err)  }}func CopyHeaders(src http.Header, dst *http.Header) {  for headingName, headingValues := range src {    for _, value := range headingValues {      dst.Add(headingName, value)    }  }}// Hop-by-hop headers. These are removed when sent to the backend.// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.htmlvar hopHeaders = []string{  "Connection",  "Keep-Alive",  "Proxy-Authenticate",  "Proxy-Authorization",  "Te", // canonicalized version of "TE"  "Trailers",  "Transfer-Encoding",  "Upgrade",}func DropHopHeaders(head *http.Header) {  for _, header := range hopHeaders {    head.Del(header)  }}func SetProxyHeader(req *http.Request) {  headerName := "X-Forwarded-for"  target := to  if prior, ok := req.Header[headerName]; ok {    // Not first proxy, append    target = strings.Join(prior, ", ") + ", " + target  }  req.Header.Set(headerName, target)}

首先需要找到插件的位置。為此,我們將用 JSON 定義配置文件,在里面定義路徑列表,在本文中列表里只有一項(xiàng),但請(qǐng)注意,這是一個(gè)為插件定義配置的機(jī)會(huì)。ueS28資訊網(wǎng)——每日最新資訊28at.com

// config.json[  "log-plugin/plugin.so"]

這就足夠了。然后我們編寫讀取該文件內(nèi)容的代碼,為了保持整潔,將在另一個(gè)文件中進(jìn)行插件加載。ueS28資訊網(wǎng)——每日最新資訊28at.com

// cmd/plugin.gopackage mainimport (  "encoding/json"  "os")// global but private, safe usage here in this filevar pluginPathList []stringfunc LoadConfig() {  f, err := os.ReadFile("config.json")  if err != nil {    // NOTE: in real cases, deal with this error    panic(err)  }  json.Unmarshal(f, &pluginPathList)}

然后加載插件本身,為此我們將使用標(biāo)準(zhǔn)庫(kù)中的 golang 插件組件[9]。ueS28資訊網(wǎng)——每日最新資訊28at.com

// cmd/plugin.gopackage mainimport (  //…  "plugin")// ...previous code...var pluginList []*plugin.Pluginfunc LoadPlugins() {  // Allocate a list for storing all our plugins  pluginList = make([]*plugin.Plugin, 0, len(pluginPathList))  for _, p := range pluginPathList {    // We use plugin.Open to load the plugin by path    plg, err := plugin.Open(p)    if err != nil {      // NOTE: in real cases, deal with this error      panic(err)    }    pluginList = append(pluginList, plg)  }}// Let's throw this here so it loads the plugins as soon as we import this modulefunc init() {  LoadConfig()  LoadPlugins()}

插件加載后,就可以訪問(wèn)其符號(hào)了,包括我們?cè)趨f(xié)議中定義的變量 Plugin。我們修改之前的代碼,保存這個(gè)變量,而不是整個(gè)插件。現(xiàn)在,我們的文件看起來(lái)是這樣的:ueS28資訊網(wǎng)——每日最新資訊28at.com

// cmd/plugin.goimport (  //…  "protocol"  "net/http")//…// Substitute previous codevar pluginList []*protocol.HttpRedirectPluginfunc LoadPlugins() {  // Allocate a list for storing all our plugins  pluginList = make([]*protocol.HttpRedirectPlugin, 0, len(pluginPathList))  for _, p := range pluginPathList {    // We use plugin.Open to load plugins by path    plg, err := plugin.Open(p)    if err != nil {      // NOTE: in real cases, deal with this error      panic(err)    }      // Search for variable named "Plugin"    v, err := plg.Lookup("Plugin")    if err != nil {      // NOTE: in real cases, deal with this error      panic(err)    }        // Cast symbol to protocol type    castV, ok := v.(protocol.HttpRedirectPlugin)    if !ok {      // NOTE: in real cases, deal with this error      panic("Could not cast plugin")    }        pluginList = append(pluginList, &castV)  }}// …

很好,現(xiàn)在 pluginList 中的所有變量都是正常的 golang 變量,可以直接訪問(wèn),就好像從一開始就是代碼的一部分。然后,我們構(gòu)建鉤子函數(shù),在發(fā)送請(qǐng)求前調(diào)用所有插件鉤子。ueS28資訊網(wǎng)——每日最新資訊28at.com

// cmd/plugin.go//…func PreRequestHook(req *http.Request) {  for _, plg := range pluginList {    // Plugin is a list of pointers, we need to dereference them    // to use the proper function    (*plg).PreRequestHook(req)  }}

最后,在主代碼中調(diào)用鉤子:ueS28資訊網(wǎng)——每日最新資訊28at.com

// cmd/main.go//…// ServeHTTP implements http.Handler.func (p *proxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {  PreRequestHook(req)// …

就是這樣!我們創(chuàng)建了一個(gè)應(yīng)用程序和一個(gè)插件,將插件加載到應(yīng)用中,然后針對(duì)收到的每個(gè)請(qǐng)求運(yùn)行插件代碼,并記錄這些請(qǐng)求。ueS28資訊網(wǎng)——每日最新資訊28at.com

想要測(cè)試?直接運(yùn)行就行:ueS28資訊網(wǎng)——每日最新資訊28at.com

# From http-redirectgo run cmd/*.go -from <port> -to <url>

ueS28資訊網(wǎng)——每日最新資訊28at.com

結(jié)論

我們?cè)诒疚闹杏懻摿耸裁词遣寮⒉寮挠猛荆约叭绾位?Go 標(biāo)準(zhǔn)庫(kù)創(chuàng)建支持插件的應(yīng)用程序的能力。在未來(lái)的工作中,請(qǐng)考慮通過(guò)這種基礎(chǔ)架構(gòu)為解決方案提供更好的可擴(kuò)展性,從而幫助其他開發(fā)人員可以更廣泛的使用我們的工具和應(yīng)用。ueS28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://m.www897cc.com/showinfo-26-94846-0.html如何基于 Golang 標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)插件功能

聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: 提高數(shù)值精度:掌握 C++ 中的 setprecision

下一篇: 為什么高手都要用非阻塞IO?

標(biāo)簽:
  • 熱門焦點(diǎn)
Top 日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不
亚洲人精品午夜| 国产精品一区二区a| 久久精品免费看| 久久久精彩视频| 欧美成人综合| 欧美视频中文字幕在线| 国产午夜精品一区二区三区欧美| 激情欧美日韩| 日韩视频不卡| 久久av一区二区三区漫画| 蜜臀久久99精品久久久久久9| 欧美日韩美女在线| 国产综合在线看| 日韩一区二区精品| 久久国产精品99国产| 欧美黄污视频| 国产欧美日韩视频一区二区| 亚洲高清在线观看| 亚洲欧美日韩精品久久亚洲区 | 一本色道久久精品| 欧美在线一区二区| 欧美人与禽猛交乱配视频| 国产日韩欧美日韩| 亚洲精品一区二区三| 欧美一区二区三区视频免费| 欧美激情第9页| 国产午夜精品全部视频在线播放| 亚洲日本在线观看| 久久爱91午夜羞羞| 欧美视频在线免费| 在线日韩日本国产亚洲| 午夜国产精品视频| 欧美日韩不卡合集视频| 精品88久久久久88久久久| 亚洲一区二区av电影| 另类激情亚洲| 国产日韩精品电影| 一区二区av| 欧美成人69| 国产综合视频在线观看| 亚洲一区二区视频在线观看| 蜜桃av久久久亚洲精品| 国产一区久久| 先锋影音网一区二区| 欧美日韩国产欧| 1024成人网色www| 欧美在线视频二区| 国产精品美女主播| 一本色道久久综合狠狠躁篇的优点 | 欧美日韩精品欧美日韩精品| 今天的高清视频免费播放成人| 亚洲欧美日韩另类精品一区二区三区| 欧美精品午夜| 亚洲国产高清一区二区三区| 久久精品中文字幕一区| 国产精品一区二区久久久久| 在线一区亚洲| 欧美日韩亚洲一区二区| 亚洲精品三级| 欧美91大片| 亚洲高清在线视频| 久久精品夜夜夜夜久久| 国产精品亚洲综合天堂夜夜| 正在播放亚洲一区| 欧美日韩性视频在线| 日韩视频一区二区三区在线播放 | 欧美福利影院| 亚洲高清免费在线| 猫咪成人在线观看| 亚洲成色最大综合在线| 久久青青草原一区二区| 国产亚洲综合性久久久影院| 欧美一区二区三区喷汁尤物| 国产欧美精品在线播放| 性欧美video另类hd性玩具| 国产精品亚洲精品| 午夜久久美女| 国产综合精品一区| 久久蜜桃香蕉精品一区二区三区| 国内精品伊人久久久久av影院| 久久成人免费| 国语自产精品视频在线看| 久久精品夜色噜噜亚洲a∨| 国产自产在线视频一区| 久久久午夜精品| 国产日韩欧美一区二区三区在线观看 | 欧美激情亚洲自拍| 亚洲免费av片| 欧美少妇一区| 亚洲一区二区成人在线观看| 国产精品久久久久一区二区| 亚洲欧美中文字幕| 国产一区二区三区最好精华液| 久久久久久综合网天天| 在线精品观看| 欧美精品999| 一区二区毛片| 国产精品一二三视频| 久久黄色级2电影| 亚洲国产va精品久久久不卡综合| 欧美激情综合在线| 亚洲一区二区三区成人在线视频精品 | 欧美成人精品影院| 99热在这里有精品免费| 国产精品久久久久久久久久直播| 亚洲欧美日韩天堂| 国内视频精品| 欧美大尺度在线观看| 一本色道久久99精品综合| 国产精品视频自拍| 久久天天狠狠| 99国产精品久久久久久久久久| 国产精品久久777777毛茸茸| 欧美与欧洲交xxxx免费观看 | 欧美在线视频在线播放完整版免费观看| 国产中文一区| 欧美精品在线观看91| 亚洲欧美国产三级| 伊人春色精品| 欧美四级在线| 久久久人成影片一区二区三区| 最新国产拍偷乱拍精品| 国产精品地址| 久久琪琪电影院| 一区二区三区www| 国产一区二区三区的电影| 欧美成人在线免费观看| 亚洲欧美成人一区二区在线电影| 国产主播精品| 欧美色精品在线视频| 久久精品国产2020观看福利| 91久久精品国产91久久性色| 国产精品区免费视频| 麻豆精品91| 亚洲亚洲精品三区日韩精品在线视频| 国产综合色在线| 欧美日韩色婷婷| 久久精品综合一区| 中国女人久久久| 伊人精品视频| 国产精品久久久久9999高清| 裸体一区二区| 欧美亚洲网站| 日韩特黄影片| 影音先锋中文字幕一区| 欧美日韩极品在线观看一区| 久久gogo国模裸体人体| 一区二区三区欧美亚洲| 在线不卡中文字幕| 国产精品视区| 欧美日韩91| 久久只有精品| 欧美影院一区| 亚洲视频欧美视频| 亚洲片国产一区一级在线观看| 国产日韩欧美在线看| 欧美午夜a级限制福利片| 久久综合久久综合这里只有精品 | 午夜久久99| 亚洲免费精彩视频| 亚洲成人在线视频播放| 国产日韩欧美另类| 国产精品久久影院| 欧美日韩三区四区| 欧美国产日本在线| 久久这里只精品最新地址| 久久福利视频导航| 亚洲女ⅴideoshd黑人| av不卡在线| 亚洲人成亚洲人成在线观看图片| 狠狠入ady亚洲精品| 国产麻豆精品theporn| 国产精品大片| 欧美日韩在线精品| 欧美国产欧美综合| 免费久久久一本精品久久区| 久久精品日产第一区二区| 性感少妇一区| 亚洲欧美激情视频在线观看一区二区三区 | 久久久综合网站| 欧美一区二区三区免费大片| 中文欧美日韩| 一本色道综合亚洲| 日韩视频在线你懂得| 亚洲国产日韩在线| 1024成人| 亚洲国产黄色| 亚洲第一页自拍| 亚洲成人在线观看视频| 韩国精品一区二区三区| 国产一区99| 国产一区二区三区观看| 国产精品综合不卡av| 国产精品久久久久久户外露出 | 欧美一区视频| 欧美一区二区三区另类| 香港久久久电影| 欧美一级免费视频| 欧美与欧洲交xxxx免费观看| 亚洲欧美日韩直播| 先锋影音一区二区三区| 欧美一区永久视频免费观看| 欧美中文字幕精品| 久久久精品国产免费观看同学|