github.com/wangyougui/gf/v2@v2.6.5/net/ghttp/ghttp_response_cors.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/wangyougui/gf. 6 // 7 8 package ghttp 9 10 import ( 11 "net/http" 12 "net/url" 13 14 "github.com/wangyougui/gf/v2/text/gstr" 15 "github.com/wangyougui/gf/v2/util/gconv" 16 ) 17 18 // CORSOptions is the options for CORS feature. 19 // See https://www.w3.org/TR/cors/ . 20 type CORSOptions struct { 21 AllowDomain []string // Used for allowing requests from custom domains 22 AllowOrigin string // Access-Control-Allow-Origin 23 AllowCredentials string // Access-Control-Allow-Credentials 24 ExposeHeaders string // Access-Control-Expose-Headers 25 MaxAge int // Access-Control-Max-Age 26 AllowMethods string // Access-Control-Allow-Methods 27 AllowHeaders string // Access-Control-Allow-Headers 28 } 29 30 var ( 31 // defaultAllowHeaders is the default allowed headers for CORS. 32 // It defined another map for better header key searching performance. 33 defaultAllowHeaders = "Origin,Content-Type,Accept,User-Agent,Cookie,Authorization,X-Auth-Token,X-Requested-With" 34 defaultAllowHeadersMap = make(map[string]struct{}) 35 ) 36 37 func init() { 38 array := gstr.SplitAndTrim(defaultAllowHeaders, ",") 39 for _, header := range array { 40 defaultAllowHeadersMap[header] = struct{}{} 41 } 42 } 43 44 // DefaultCORSOptions returns the default CORS options, 45 // which allows any cross-domain request. 46 func (r *Response) DefaultCORSOptions() CORSOptions { 47 options := CORSOptions{ 48 AllowOrigin: "*", 49 AllowMethods: supportedHttpMethods, 50 AllowCredentials: "true", 51 AllowHeaders: defaultAllowHeaders, 52 MaxAge: 3628800, 53 } 54 // Allow all client's custom headers in default. 55 if headers := r.Request.Header.Get("Access-Control-Request-Headers"); headers != "" { 56 array := gstr.SplitAndTrim(headers, ",") 57 for _, header := range array { 58 if _, ok := defaultAllowHeadersMap[header]; !ok { 59 options.AllowHeaders += "," + header 60 } 61 } 62 } 63 // Allow all anywhere origin in default. 64 if origin := r.Request.Header.Get("Origin"); origin != "" { 65 options.AllowOrigin = origin 66 } else if referer := r.Request.Referer(); referer != "" { 67 if p := gstr.PosR(referer, "/", 6); p != -1 { 68 options.AllowOrigin = referer[:p] 69 } else { 70 options.AllowOrigin = referer 71 } 72 } 73 return options 74 } 75 76 // CORS sets custom CORS options. 77 // See https://www.w3.org/TR/cors/ . 78 func (r *Response) CORS(options CORSOptions) { 79 if r.CORSAllowedOrigin(options) { 80 r.Header().Set("Access-Control-Allow-Origin", options.AllowOrigin) 81 } 82 if options.AllowCredentials != "" { 83 r.Header().Set("Access-Control-Allow-Credentials", options.AllowCredentials) 84 } 85 if options.ExposeHeaders != "" { 86 r.Header().Set("Access-Control-Expose-Headers", options.ExposeHeaders) 87 } 88 if options.MaxAge != 0 { 89 r.Header().Set("Access-Control-Max-Age", gconv.String(options.MaxAge)) 90 } 91 if options.AllowMethods != "" { 92 r.Header().Set("Access-Control-Allow-Methods", options.AllowMethods) 93 } 94 if options.AllowHeaders != "" { 95 r.Header().Set("Access-Control-Allow-Headers", options.AllowHeaders) 96 } 97 // No continue service handling if it's OPTIONS request. 98 // Note that there's special checks in previous router searching, 99 // so if it goes to here it means there's already serving handler exist. 100 if gstr.Equal(r.Request.Method, "OPTIONS") { 101 if r.Status == 0 { 102 r.Status = http.StatusOK 103 } 104 // No continue serving. 105 r.Request.ExitAll() 106 } 107 } 108 109 // CORSAllowedOrigin CORSAllowed checks whether the current request origin is allowed cross-domain. 110 func (r *Response) CORSAllowedOrigin(options CORSOptions) bool { 111 if options.AllowDomain == nil { 112 return true 113 } 114 origin := r.Request.Header.Get("Origin") 115 if origin == "" { 116 return true 117 } 118 parsed, err := url.Parse(origin) 119 if err != nil { 120 return false 121 } 122 for _, v := range options.AllowDomain { 123 if gstr.IsSubDomain(parsed.Host, v) { 124 return true 125 } 126 } 127 return false 128 } 129 130 // CORSDefault sets CORS with default CORS options, 131 // which allows any cross-domain request. 132 func (r *Response) CORSDefault() { 133 r.CORS(r.DefaultCORSOptions()) 134 }