반응형
웹 프론트 프로젝트를 할 때 호스트가 다른 서버로 API를 요청하는 경우 CORS 오류를 많이 겪어봤을 것이다.
이를 위해 중간에 CORS Proxy 서버를 두고 테스트를 하는 경우가 많은데 node로 만들어져 npm에 등록된 프로젝트도 많이 보였다.
Go를 조금씩 공부하고 있어서 Go의 httpuil 패키지의 reverse proxy 모듈을 활용하여 간단한 CORS Proxy 서버를 만들어보았다.
타겟 서버가 세션 쿠키를 사용하는 경우를 대비해서, 쿠키 내용을 파싱하여 호스트 정보를 바꾸는 부분 또한 추가했으며 타겟 서버의 주소와 access control allow origin에 들어갈 현재 클라이언트의 호스트 정보, 프로그램의 포트 정보 등 부가 정보는 .env파일로 따로 작성하여 불러오도록 하였다.
.env 파일 구조는 다음과 같다.
# Reverse Proxy Target Host Information TARGET_HOST= # This Proxy Server Port LOCAL_PORT= # CORS Allow Origin Information. It is Client host information (i.e https://192.168.93.1:4000) ACCESS_CONTROL_ALLOWS_ORIGIN= # Credentials Flag for Sharing Cookie between origin resource server, Must Set as true WITH_CREDENTIALS= # Optional, Session Cookie Name In Resource Server (If, not using cookie, let that empty) SESSION_COOKIE_NAME= # SSL Key File Path For Running Proxy Server as Https SSL_KEY_PATH= # SSL Cert File Path For Running Proxy Server as Https SSL_CERT_PATH=
리버스 프록시를 통해 cors 기능을 제공하는 go 코드는 다음과 같다.
package main import ( "crypto/tls" "log" "net/http" "net/http/httputil" "net/url" "os" "strings" "github.com/joho/godotenv" ) func main() { err := godotenv.Load() if err != nil { log.Fatal("Error Loading .env File") } remote, err := url.Parse(os.Getenv("TARGET_HOST")) if err != nil { panic(err) } log.Printf("Forwarding Target : %s://%s", remote.Scheme, remote.Host) proxy := httputil.NewSingleHostReverseProxy(remote) proxy.ModifyResponse = corsHeaderModify http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} http.HandleFunc("/", handler(proxy)) err = http.ListenAndServeTLS(":"+os.Getenv("LOCAL_PORT"), os.Getenv("SSL_CERT_PATH"), os.Getenv("SSL_KEY_PATH"), nil) if err != nil { panic(err) } } func handler(p *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) { return func(resp http.ResponseWriter, r *http.Request) { // If, current request is pre-flight if r.Method == "OPTIONS" { resp.Header().Set("Access-Control-Allow-Origin", os.Getenv("ACCESS_CONTROL_ALLOWS_ORIGIN")) resp.Header().Set("Access-Control-Allow-Headers", "Access-Control-Allow-Origin, content-type") resp.Header().Set("Access-Control-Allow-Methods", "*") resp.Header().Set("Access-Control-Expose-Headers", "Set-Cookie, Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Credential, Authorization") resp.Header().Set("Vary", "Origin") resp.Header().Set("Vary", "Access-Control-Request-Method") resp.Header().Set("Vary", "Access-Control-Request-Headers") resp.Header().Set("Access-Control-Allow-Credentials", "true") return } else { log.Printf("%s %s -> Cros_Proxy -> %s", r.Method, r.Host+r.URL.RequestURI(), os.Getenv("TARGET_HOST")+r.URL.RequestURI()) p.ServeHTTP(resp, r) } } } func corsHeaderModify(resp *http.Response) error { // Set Basic Cors related header resp.Header.Set("Access-Control-Allow-Origin", os.Getenv("ACCESS_CONTROL_ALLOWS_ORIGIN")) resp.Header.Set("Access-Control-Allow-Headers", "Access-Control-Allow-Origin, content-type") resp.Header.Set("Access-Control-Allow-Methods", "*") resp.Header.Set("Access-Control-Expose-Headers", "Set-Cookie, Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Credential, Authorization") resp.Header.Set("Vary", "Origin") resp.Header.Set("Vary", "Access-Control-Request-Method") resp.Header.Set("Vary", "Access-Control-Request-Headers") resp.Header.Set("Access-Control-Allow-Credentials", "true") // Parsing cookie in header for _, value := range strings.Split(resp.Header.Get("Set-Cookie"), ";") { // If remove the domain value, the client host information is automatically set to the domain value by the browser. if strings.Contains(value, "Domain=") { var newCookie = strings.Replace(resp.Header.Get("Set-Cookie"), value, "", 1) resp.Header.Set("Set-Cookie", newCookie) } } return nil }
심플하게 다음과 같이 프로그램을 실행시킬 수 있다.
go run main.go
깃헙 github.com/LoveAndCode/SIMPLE_CORS_GO_PROXY
반응형
'Programming > Go' 카테고리의 다른 글
[Go] Hello World (0) | 2019.10.03 |
---|---|
[Go] Window 환경에서 Go 설치하기 (2) | 2019.10.03 |