diff --git a/ahttp.go b/ahttp.go index 3a6a028..0f71c09 100644 --- a/ahttp.go +++ b/ahttp.go @@ -8,7 +8,11 @@ package ahttp import ( "io" + "net" "net/http" + "strings" + + "aahframework.org/essentials.v0" ) // HTTP Method names @@ -91,3 +95,54 @@ func WrapGzipWriter(w io.Writer) ResponseWriter { gr.r = w.(*Response) return gr } + +// IdentifyScheme method is to identify value of protocol value. It's is derived +// one, Go language doesn't provide directly. +// - `X-Forwarded-Proto` is not empty return value as is +// - `http.Request.TLS` is not nil value is `https` +// - `http.Request.TLS` is nil value is `http` +func IdentifyScheme(r *http.Request) string { + scheme := r.Header.Get("X-Forwarded-Proto") + if scheme == "" { + if r.TLS == nil { + return "http" + } + return "https" + } + return scheme +} + +// IdentifyHost method is to correct Hosyt source value from HTTP request. +func IdentifyHost(r *http.Request) string { + if r.URL.Host == "" { + return r.Host + } + return r.URL.Host +} + +// ClientIP method returns remote Client IP address aka Remote IP. +// It parses in the order of given set of headers otherwise it uses default +// default header set `X-Forwarded-For`, `X-Real-IP`, "X-Appengine-Remote-Addr" +// and finally `http.Request.RemoteAddr`. +func ClientIP(r *http.Request, hdrs ...string) string { + if len(hdrs) == 0 { + hdrs = []string{"X-Forwarded-For", "X-Real-IP", "X-Appengine-Remote-Addr"} + } + + for _, hdrKey := range hdrs { + if hv := r.Header.Get(hdrKey); !ess.IsStrEmpty(hv) { + index := strings.Index(hv, ",") + if index == -1 { + return strings.TrimSpace(hv) + } + return strings.TrimSpace(hv[:index]) + } + } + + // Remote Address + if remoteAddr, _, err := net.SplitHostPort(r.RemoteAddr); err == nil { + return strings.TrimSpace(remoteAddr) + } + + return "" +} diff --git a/content_type.go b/content_type.go index 5b441b9..eeb4257 100644 --- a/content_type.go +++ b/content_type.go @@ -39,6 +39,9 @@ var ( // ContentTypeJavascript content type. ContentTypeJavascript = parseMediaType("application/javascript; charset=utf-8") + + // ContentTypeEventStream Server-Sent Events content type. + ContentTypeEventStream = parseMediaType("text/event-stream") ) //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ diff --git a/request.go b/request.go index bc57234..b41c0e5 100644 --- a/request.go +++ b/request.go @@ -9,7 +9,6 @@ import ( "fmt" "io" "mime/multipart" - "net" "net/http" "net/url" "os" @@ -21,10 +20,8 @@ import ( ) const ( - jsonpReqParamKey = "callback" - ajaxHeaderValue = "XMLHttpRequest" - wsHdrVal = "websocket" - connHdrValPartial = "upgrade" + jsonpReqParamKey = "callback" + ajaxHeaderValue = "XMLHttpRequest" ) var requestPool = &sync.Pool{New: func() interface{} { return &Request{} }} @@ -36,8 +33,8 @@ var requestPool = &sync.Pool{New: func() interface{} { return &Request{} }} // ParseRequest method populates the given aah framework `ahttp.Request` // instance from Go HTTP request. func ParseRequest(r *http.Request, req *Request) *Request { - req.Scheme = identifyScheme(r) - req.Host = host(r) + req.Scheme = IdentifyScheme(r) + req.Host = IdentifyHost(r) req.Proto = r.Proto req.Method = r.Method req.Path = r.URL.Path @@ -153,26 +150,7 @@ func (r *Request) SetAcceptEncoding(encoding *AcceptSpec) *Request { // could configure headers of their choice. For e.g. // X-Appengine-Remote-Addr, etc. func (r *Request) ClientIP() string { - // Header X-Forwarded-For - if fwdFor := r.Header.Get(HeaderXForwardedFor); !ess.IsStrEmpty(fwdFor) { - index := strings.Index(fwdFor, ",") - if index == -1 { - return strings.TrimSpace(fwdFor) - } - return strings.TrimSpace(fwdFor[:index]) - } - - // Header X-Real-Ip - if realIP := r.Header.Get(HeaderXRealIP); !ess.IsStrEmpty(realIP) { - return strings.TrimSpace(realIP) - } - - // Remote Address - if remoteAddr, _, err := net.SplitHostPort(r.Unwrap().RemoteAddr); err == nil { - return strings.TrimSpace(remoteAddr) - } - - return "" + return ClientIP(r.Unwrap()) } // Cookie method returns a named cookie from HTTP request otherwise error. @@ -226,12 +204,6 @@ func (r *Request) IsAJAX() bool { return r.Header.Get(HeaderXRequestedWith) == ajaxHeaderValue } -// IsWebSocket method returns true if request is WebSocket otherwise false. -func (r *Request) IsWebSocket() bool { - return r.Header.Get(HeaderUpgrade) == wsHdrVal && - strings.Contains(strings.ToLower(r.Header.Get(HeaderConnection)), connHdrValPartial) -} - // URL method return underlying request URL instance. func (r *Request) URL() *url.URL { return r.Unwrap().URL @@ -452,29 +424,6 @@ func (p *Params) FormFile(key string) (multipart.File, *multipart.FileHeader, er // Unexported methods //___________________________________ -// identifyScheme method is to identify value of protocol value. It's is derived -// one, Go language doesn't provide directly. -// - `X-Forwarded-Proto` is not empty return value as is -// - `http.Request.TLS` is not nil value is `https` -// - `http.Request.TLS` is nil value is `http` -func identifyScheme(r *http.Request) string { - scheme := r.Header.Get(HeaderXForwardedProto) - if scheme == "" { - if r.TLS == nil { - return SchemeHTTP // "http" - } - return SchemeHTTPS // "https" - } - return scheme -} - -func host(r *http.Request) string { - if r.URL.Host == "" { - return r.Host - } - return r.URL.Host -} - func getReferer(hdr http.Header) string { referer := hdr.Get(HeaderReferer) if referer == "" { diff --git a/request_test.go b/request_test.go index fbb59a5..5967866 100644 --- a/request_test.go +++ b/request_test.go @@ -91,7 +91,6 @@ func TestHTTPParseRequest(t *testing.T) { assert.Nil(t, err) assert.False(t, aahReq.IsJSONP()) assert.False(t, aahReq.IsAJAX()) - assert.False(t, aahReq.IsWebSocket()) aahReq.SetAcceptContentType(nil) assert.NotNil(t, aahReq.AcceptContentType()) @@ -186,19 +185,19 @@ func TestHTTPRequestCookies(t *testing.T) { func TestRequestSchemeDerived(t *testing.T) { req := httptest.NewRequest("GET", "http://127.0.0.1:8080/welcome.html", nil) - scheme1 := identifyScheme(req) + scheme1 := IdentifyScheme(req) assert.Equal(t, "http", scheme1) req.TLS = &tls.ConnectionState{} - scheme2 := identifyScheme(req) + scheme2 := IdentifyScheme(req) assert.Equal(t, "https", scheme2) req.Header.Set(HeaderXForwardedProto, "https") - scheme3 := identifyScheme(req) + scheme3 := IdentifyScheme(req) assert.Equal(t, "https", scheme3) req.Header.Set(HeaderXForwardedProto, "http") - scheme4 := identifyScheme(req) + scheme4 := IdentifyScheme(req) assert.Equal(t, "http", scheme4) }