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