Skip to content

Commit d4d049f

Browse files
authored
Merge pull request #18 for v0.11.0 Release
2 parents f9b651b + 85d0444 commit d4d049f

16 files changed

+493
-529
lines changed

.travis.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,16 @@ branches:
77
# skip tags build, we are building branch and master that is enough for
88
# consistenty check and release. Let's use Travis CI resources optimally
99
# for aah framework.
10-
- /^v[0-9]\.[0-9]/
10+
- /^v[0-9.]+$/
1111

1212
go:
13-
- 1.8
1413
- 1.9
14+
- "1.10"
1515
- tip
1616

1717
go_import_path: aahframework.org/ahttp.v0
1818

1919
install:
20-
- git config --global http.https://aahframework.org.followRedirects true
2120
- go get -t -v ./...
2221

2322
script:

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2016-2017 Jeevanandam M., https://myjeeva.com <[email protected]>
3+
Copyright (c) 2016-2018 Jeevanandam M., https://myjeeva.com <[email protected]>
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1-
# ahttp - aah framework
2-
[![Build Status](https://travis-ci.org/go-aah/ahttp.svg?branch=master)](https://travis-ci.org/go-aah/ahttp) [![codecov](https://codecov.io/gh/go-aah/ahttp/branch/master/graph/badge.svg)](https://codecov.io/gh/go-aah/ahttp/branch/master) [![Go Report Card](https://goreportcard.com/badge/aahframework.org/ahttp.v0-unstable)](https://goreportcard.com/report/aahframework.org/ahttp.v0-unstable) [![Version](https://img.shields.io/badge/version-0.10-blue.svg)](https://github.com/go-aah/ahttp/releases/latest) [![GoDoc](https://godoc.org/aahframework.org/ahttp.v0-unstable?status.svg)](https://godoc.org/aahframework.org/ahttp.v0-unstable) [![License](https://img.shields.io/github/license/go-aah/ahttp.svg)](LICENSE) [![Twitter](https://img.shields.io/badge/[email protected])](https://twitter.com/aahframework)
1+
<p align="center">
2+
<img src="https://cdn.aahframework.org/assets/img/aah-logo-64x64.png" />
3+
<h2 align="center">HTTP extension library by aah framework</h2>
4+
</p>
5+
<p align="center">
6+
<p align="center"><a href="https://travis-ci.org/go-aah/ahttp"><img src="https://travis-ci.org/go-aah/ahttp.svg?branch=master" alt="Build Status"></a> <a href="https://codecov.io/gh/go-aah/ahttp/branch/master"><img src="https://codecov.io/gh/go-aah/ahttp/branch/master/graph/badge.svg" alt="Code Coverage"></a> <a href="https://goreportcard.com/report/aahframework.org/ahttp.v0"><img src="https://goreportcard.com/badge/aahframework.org/ahttp.v0" alt="Go Report Card"></a> <a href="https://github.com/go-aah/ahttp/releases/latest"><img src="https://img.shields.io/badge/version-0.11.0-blue.svg" alt="Release Version"></a> <a href="https://godoc.org/aahframework.org/ahttp.v0"><img src="https://godoc.org/aahframework.org/ahttp.v0?status.svg" alt="Godoc"></a> <a href="https://twitter.com/aahframework"><img src="https://img.shields.io/badge/[email protected]" alt="Twitter @aahframework"></a></p>
7+
</p>
38

4-
***v0.10 [released](https://github.com/go-aah/ahttp/releases/latest) and tagged on Sep 01, 2017***
9+
HTTP extension Library is used to handle/process Request and Response (headers, body, gzip, etc).
510

6-
HTTP Library built to process, manipulate Request and Response (headers, body, gzip, etc).
11+
### News
712

8-
*`ahttp` developed for aah framework. However, it's an independent library, can be used separately with any `Go` language project. Feel free to use it.*
13+
* `v0.11.0` [released](https://github.com/go-aah/ahttp/releases/latest) and tagged on Jul 06, 2018.
14+
15+
## Installation
916

10-
# Installation
11-
#### Stable Version - Production Ready
1217
```bash
13-
# install the library
1418
go get -u aahframework.org/ahttp.v0
1519
```
1620

17-
Visit official website https://aahframework.org to learn more.
21+
Visit official website https://aahframework.org to learn more about `aah` framework.

ahttp.go

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Copyright (c) Jeevanandam M (https://github.com/jeevatkm)
2-
// go-aah/ahttp source code and usage is governed by a MIT style
2+
// aahframework.org/ahttp source code and usage is governed by a MIT style
33
// license that can be found in the LICENSE file.
44

55
// Package ahttp is to cater HTTP helper methods for aah framework.
@@ -8,7 +8,11 @@ package ahttp
88

99
import (
1010
"io"
11+
"net"
1112
"net/http"
13+
"strings"
14+
15+
"aahframework.org/essentials.v0"
1216
)
1317

1418
// HTTP Method names
@@ -24,6 +28,13 @@ const (
2428
MethodTrace = http.MethodTrace
2529
)
2630

31+
// URI Protocol scheme names
32+
const (
33+
SchemeHTTP = "http"
34+
SchemeHTTPS = "https"
35+
SchemeFTP = "ftp"
36+
)
37+
2738
// TimeFormat is the time format to use when generating times in HTTP
2839
// headers. It is like time.RFC1123 but hard-codes GMT as the time
2940
// zone. The time being formatted must be in UTC for Format to
@@ -53,6 +64,7 @@ func AcquireRequest(r *http.Request) *Request {
5364
// ReleaseRequest method resets the instance value and puts back to pool.
5465
func ReleaseRequest(r *Request) {
5566
if r != nil {
67+
r.cleanupMutlipart()
5668
r.Reset()
5769
requestPool.Put(r)
5870
}
@@ -83,3 +95,71 @@ func WrapGzipWriter(w io.Writer) ResponseWriter {
8395
gr.r = w.(*Response)
8496
return gr
8597
}
98+
99+
// Scheme method is to identify value of protocol value. It's is derived
100+
// one, Go language doesn't provide directly.
101+
//
102+
// - `X-Forwarded-Proto` is not empty, returns as-is
103+
//
104+
// - `X-Forwarded-Protocol` is not empty, returns as-is
105+
//
106+
// - `http.Request.TLS` is not nil or `X-Forwarded-Ssl == on` returns `https`
107+
//
108+
// - `X-Url-Scheme` is not empty, returns as-is
109+
//
110+
// - returns `http`
111+
func Scheme(r *http.Request) string {
112+
if scheme := r.Header.Get(HeaderXForwardedProto); scheme != "" {
113+
return scheme
114+
}
115+
116+
if scheme := r.Header.Get(HeaderXForwardedProtocol); scheme != "" {
117+
return scheme
118+
}
119+
120+
if r.TLS != nil || r.Header.Get(HeaderXForwardedSsl) == "on" {
121+
return "https"
122+
}
123+
124+
if scheme := r.Header.Get(HeaderXUrlScheme); scheme != "" {
125+
return scheme
126+
}
127+
128+
return "http"
129+
}
130+
131+
// Host method is to correct Host value from HTTP request.
132+
func Host(r *http.Request) string {
133+
if r.URL.Host == "" {
134+
return r.Host
135+
}
136+
return r.URL.Host
137+
}
138+
139+
// ClientIP method returns remote Client IP address aka Remote IP.
140+
//
141+
// It parses in the order of given headers otherwise it uses default
142+
// default header set `X-Forwarded-For`, `X-Real-IP`, "X-Appengine-Remote-Addr"
143+
// and finally `http.Request.RemoteAddr`.
144+
func ClientIP(r *http.Request, hdrs ...string) string {
145+
if len(hdrs) == 0 {
146+
hdrs = []string{"X-Forwarded-For", "X-Real-IP", "X-Appengine-Remote-Addr"}
147+
}
148+
149+
for _, hdrKey := range hdrs {
150+
if hv := r.Header.Get(hdrKey); !ess.IsStrEmpty(hv) {
151+
index := strings.Index(hv, ",")
152+
if index == -1 {
153+
return strings.TrimSpace(hv)
154+
}
155+
return strings.TrimSpace(hv[:index])
156+
}
157+
}
158+
159+
// Remote Address
160+
if remoteAddr, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
161+
return strings.TrimSpace(remoteAddr)
162+
}
163+
164+
return ""
165+
}

content_type.go

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,26 +36,32 @@ var (
3636

3737
// ContentTypeOctetStream content type for bytes.
3838
ContentTypeOctetStream = parseMediaType("application/octet-stream")
39-
)
4039

41-
type (
42-
// ContentType is represents request and response content type values
43-
ContentType struct {
44-
Mime string
45-
Exts []string
46-
Params map[string]string
47-
}
40+
// ContentTypeJavascript content type.
41+
ContentTypeJavascript = parseMediaType("application/javascript; charset=utf-8")
42+
43+
// ContentTypeEventStream Server-Sent Events content type.
44+
ContentTypeEventStream = parseMediaType("text/event-stream")
4845
)
4946

5047
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
51-
// Content-Type methods
48+
// Content-Type
5249
//___________________________________
5350

54-
// IsEqual method compares give Content-Type string with current instance.
51+
// ContentType is represents request and response content type values
52+
type ContentType struct {
53+
Mime string
54+
Exts []string
55+
Params map[string]string
56+
}
57+
58+
// IsEqual method returns true if its equals to current content-type instance
59+
// otherwise false.
5560
// E.g.:
5661
// contentType.IsEqual("application/json")
62+
// contentType.IsEqual("application/json; charset=utf-8")
5763
func (c *ContentType) IsEqual(contentType string) bool {
58-
return strings.HasPrefix(c.String(), strings.ToLower(contentType))
64+
return strings.HasPrefix(contentType, c.Mime)
5965
}
6066

6167
// Charset method returns charset of content-type
@@ -65,7 +71,7 @@ func (c *ContentType) IsEqual(contentType string) bool {
6571
//
6672
// Method returns `utf-8`
6773
func (c *ContentType) Charset(defaultCharset string) string {
68-
if v, ok := c.Params["charset"]; ok {
74+
if v, found := c.Params["charset"]; found {
6975
return v
7076
}
7177
return defaultCharset
@@ -117,6 +123,6 @@ func (c *ContentType) Raw() string {
117123
}
118124

119125
// String is stringer interface
120-
func (c *ContentType) String() string {
126+
func (c ContentType) String() string {
121127
return c.Raw()
122128
}

content_type_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package ahttp
66

77
import (
88
"net/url"
9+
"runtime"
910
"testing"
1011

1112
"aahframework.org/essentials.v0"
@@ -41,8 +42,11 @@ func TestHTTPNegotiateContentType(t *testing.T) {
4142
req := createRawHTTPRequest(HeaderAccept, "application/json")
4243
req.URL, _ = url.Parse("http://localhost:8080/testpath.json")
4344
contentType = NegotiateContentType(req)
44-
assert.True(t, contentType.IsEqual("application/json"))
45-
assert.Equal(t, ".json", contentType.Exts[0])
45+
if runtime.GOOS != "windows" { // due to mime types not exists
46+
assert.NotNil(t, contentType)
47+
assert.True(t, contentType.IsEqual("application/json"))
48+
assert.Equal(t, ".json", contentType.Exts[0])
49+
}
4650

4751
req = createRawHTTPRequest(HeaderAccept, "application/json")
4852
req.URL, _ = url.Parse("http://localhost:8080/testpath.html")

gzip_response.go

Lines changed: 11 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,8 @@ import (
1111
"net"
1212
"net/http"
1313
"sync"
14-
15-
"aahframework.org/essentials.v0"
1614
)
1715

18-
// GzipResponse extends `ahttp.Response` and provides gzip for response
19-
// bytes before writing them to the underlying response.
20-
type GzipResponse struct {
21-
r *Response
22-
gw *gzip.Writer
23-
}
24-
2516
var (
2617
// GzipLevel holds value from app config.
2718
GzipLevel int
@@ -39,31 +30,16 @@ var (
3930
)
4031

4132
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
42-
// Package methods
33+
// GzipResponse
4334
//___________________________________
4435

45-
// TODO for old method cleanup
46-
47-
// GetGzipResponseWriter wraps `http.ResponseWriter`, returns aah framework response
48-
// writer that allows to advantage of response process.
49-
// Deprecated use `WrapGzipWriter` instead.
50-
func GetGzipResponseWriter(w ResponseWriter) ResponseWriter {
51-
gr := grPool.Get().(*GzipResponse)
52-
gr.gw = acquireGzipWriter(w)
53-
gr.r = w.(*Response)
54-
return gr
55-
}
56-
57-
// PutGzipResponseWiriter method resets and puts the gzip writer into pool.
58-
// Deprecated use `ReleaseResponseWriter` instead.
59-
func PutGzipResponseWiriter(rw ResponseWriter) {
60-
releaseGzipResponse(rw.(*GzipResponse))
36+
// GzipResponse extends `ahttp.Response` to provides gzip compression for response
37+
// bytes to the underlying response.
38+
type GzipResponse struct {
39+
r *Response
40+
gw *gzip.Writer
6141
}
6242

63-
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
64-
// Response methods
65-
//___________________________________
66-
6743
// Status method returns HTTP response status code. If status is not yet written
6844
// it reurns 0.
6945
func (g *GzipResponse) Status() int {
@@ -82,9 +58,7 @@ func (g *GzipResponse) Header() http.Header {
8258

8359
// Write method writes bytes into Response.
8460
func (g *GzipResponse) Write(b []byte) (int, error) {
85-
g.r.setContentTypeIfNotSet(b)
8661
g.r.WriteHeader(http.StatusOK)
87-
8862
size, err := g.gw.Write(b)
8963
g.r.bytesWritten += size
9064
return size, err
@@ -97,8 +71,9 @@ func (g *GzipResponse) BytesWritten() int {
9771

9872
// Close method closes the writer if possible.
9973
func (g *GzipResponse) Close() error {
100-
ess.CloseQuietly(g.gw)
101-
g.gw = nil
74+
if err := g.gw.Close(); err != nil {
75+
return err
76+
}
10277
return g.r.Close()
10378
}
10479

@@ -140,9 +115,9 @@ func (g *GzipResponse) Push(target string, opts *http.PushOptions) error {
140115

141116
// releaseGzipResponse method resets and puts the gzip response into pool.
142117
func releaseGzipResponse(gw *GzipResponse) {
143-
releaseGzipWriter(gw.gw)
144-
releaseResponse(gw.r)
145118
_ = gw.Close()
119+
gwPool.Put(gw.gw)
120+
releaseResponse(gw.r)
146121
grPool.Put(gw)
147122
}
148123

@@ -158,8 +133,3 @@ func acquireGzipWriter(w io.Writer) *gzip.Writer {
158133
ngw.Reset(w)
159134
return ngw
160135
}
161-
162-
func releaseGzipWriter(gw *gzip.Writer) {
163-
_ = gw.Close()
164-
gwPool.Put(gw)
165-
}

gzip_response_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import (
2121
func TestHTTPGzipWriter(t *testing.T) {
2222
handler := func(w http.ResponseWriter, r *http.Request) {
2323
GzipLevel = gzip.BestSpeed
24-
gw := GetGzipResponseWriter(GetResponseWriter(w))
25-
defer PutGzipResponseWiriter(gw)
24+
gw := WrapGzipWriter(AcquireResponseWriter(w))
25+
defer ReleaseResponseWriter(gw)
2626

2727
gw.Header().Set(HeaderVary, HeaderAcceptEncoding)
2828
gw.Header().Set(HeaderContentEncoding, "gzip")
@@ -92,7 +92,7 @@ func TestHTTPGzipHijack(t *testing.T) {
9292
ngw, _ := gzip.NewWriterLevel(w, GzipLevel)
9393
gwPool.Put(ngw)
9494
}
95-
gw := WrapGzipWriter(GetResponseWriter(w))
95+
gw := WrapGzipWriter(AcquireResponseWriter(w))
9696

9797
con, rw, err := gw.(http.Hijacker).Hijack()
9898
assert.FailOnError(t, err, "")

0 commit comments

Comments
 (0)