Skip to content

Commit

Permalink
Merge pull request #18 for v0.11.0 Release
Browse files Browse the repository at this point in the history
  • Loading branch information
jeevatkm committed Jul 7, 2018
2 parents f9b651b + 85d0444 commit d4d049f
Show file tree
Hide file tree
Showing 16 changed files with 493 additions and 529 deletions.
5 changes: 2 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,16 @@ branches:
# skip tags build, we are building branch and master that is enough for
# consistenty check and release. Let's use Travis CI resources optimally
# for aah framework.
- /^v[0-9]\.[0-9]/
- /^v[0-9.]+$/

go:
- 1.8
- 1.9
- "1.10"
- tip

go_import_path: aahframework.org/ahttp.v0

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

script:
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2016-2017 Jeevanandam M., https://myjeeva.com <jeeva@myjeeva.com>
Copyright (c) 2016-2018 Jeevanandam M., https://myjeeva.com <jeeva@myjeeva.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
22 changes: 13 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
# ahttp - aah framework
[![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/twitter-@aahframework-55acee.svg)](https://twitter.com/aahframework)
<p align="center">
<img src="https://cdn.aahframework.org/assets/img/aah-logo-64x64.png" />
<h2 align="center">HTTP extension library by aah framework</h2>
</p>
<p align="center">
<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/twitter-@aahframework-55acee.svg" alt="Twitter @aahframework"></a></p>
</p>

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

HTTP Library built to process, manipulate Request and Response (headers, body, gzip, etc).
### News

*`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.*
* `v0.11.0` [released](https://github.com/go-aah/ahttp/releases/latest) and tagged on Jul 06, 2018.

## Installation

# Installation
#### Stable Version - Production Ready
```bash
# install the library
go get -u aahframework.org/ahttp.v0
```

Visit official website https://aahframework.org to learn more.
Visit official website https://aahframework.org to learn more about `aah` framework.
82 changes: 81 additions & 1 deletion ahttp.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (c) Jeevanandam M (https://github.com/jeevatkm)
// go-aah/ahttp source code and usage is governed by a MIT style
// aahframework.org/ahttp source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.

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

import (
"io"
"net"
"net/http"
"strings"

"aahframework.org/essentials.v0"
)

// HTTP Method names
Expand All @@ -24,6 +28,13 @@ const (
MethodTrace = http.MethodTrace
)

// URI Protocol scheme names
const (
SchemeHTTP = "http"
SchemeHTTPS = "https"
SchemeFTP = "ftp"
)

// TimeFormat is the time format to use when generating times in HTTP
// headers. It is like time.RFC1123 but hard-codes GMT as the time
// zone. The time being formatted must be in UTC for Format to
Expand Down Expand Up @@ -53,6 +64,7 @@ func AcquireRequest(r *http.Request) *Request {
// ReleaseRequest method resets the instance value and puts back to pool.
func ReleaseRequest(r *Request) {
if r != nil {
r.cleanupMutlipart()
r.Reset()
requestPool.Put(r)
}
Expand Down Expand Up @@ -83,3 +95,71 @@ func WrapGzipWriter(w io.Writer) ResponseWriter {
gr.r = w.(*Response)
return gr
}

// Scheme 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, returns as-is
//
// - `X-Forwarded-Protocol` is not empty, returns as-is
//
// - `http.Request.TLS` is not nil or `X-Forwarded-Ssl == on` returns `https`
//
// - `X-Url-Scheme` is not empty, returns as-is
//
// - returns `http`
func Scheme(r *http.Request) string {
if scheme := r.Header.Get(HeaderXForwardedProto); scheme != "" {
return scheme
}

if scheme := r.Header.Get(HeaderXForwardedProtocol); scheme != "" {
return scheme
}

if r.TLS != nil || r.Header.Get(HeaderXForwardedSsl) == "on" {
return "https"
}

if scheme := r.Header.Get(HeaderXUrlScheme); scheme != "" {
return scheme
}

return "http"
}

// Host method is to correct Host value from HTTP request.
func Host(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 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 ""
}
32 changes: 19 additions & 13 deletions content_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,26 +36,32 @@ var (

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

type (
// ContentType is represents request and response content type values
ContentType struct {
Mime string
Exts []string
Params map[string]string
}
// ContentTypeJavascript content type.
ContentTypeJavascript = parseMediaType("application/javascript; charset=utf-8")

// ContentTypeEventStream Server-Sent Events content type.
ContentTypeEventStream = parseMediaType("text/event-stream")
)

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Content-Type methods
// Content-Type
//___________________________________

// IsEqual method compares give Content-Type string with current instance.
// ContentType is represents request and response content type values
type ContentType struct {
Mime string
Exts []string
Params map[string]string
}

// IsEqual method returns true if its equals to current content-type instance
// otherwise false.
// E.g.:
// contentType.IsEqual("application/json")
// contentType.IsEqual("application/json; charset=utf-8")
func (c *ContentType) IsEqual(contentType string) bool {
return strings.HasPrefix(c.String(), strings.ToLower(contentType))
return strings.HasPrefix(contentType, c.Mime)
}

// Charset method returns charset of content-type
Expand All @@ -65,7 +71,7 @@ func (c *ContentType) IsEqual(contentType string) bool {
//
// Method returns `utf-8`
func (c *ContentType) Charset(defaultCharset string) string {
if v, ok := c.Params["charset"]; ok {
if v, found := c.Params["charset"]; found {
return v
}
return defaultCharset
Expand Down Expand Up @@ -117,6 +123,6 @@ func (c *ContentType) Raw() string {
}

// String is stringer interface
func (c *ContentType) String() string {
func (c ContentType) String() string {
return c.Raw()
}
8 changes: 6 additions & 2 deletions content_type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package ahttp

import (
"net/url"
"runtime"
"testing"

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

req = createRawHTTPRequest(HeaderAccept, "application/json")
req.URL, _ = url.Parse("http://localhost:8080/testpath.html")
Expand Down
52 changes: 11 additions & 41 deletions gzip_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,8 @@ import (
"net"
"net/http"
"sync"

"aahframework.org/essentials.v0"
)

// GzipResponse extends `ahttp.Response` and provides gzip for response
// bytes before writing them to the underlying response.
type GzipResponse struct {
r *Response
gw *gzip.Writer
}

var (
// GzipLevel holds value from app config.
GzipLevel int
Expand All @@ -39,31 +30,16 @@ var (
)

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Package methods
// GzipResponse
//___________________________________

// TODO for old method cleanup

// GetGzipResponseWriter wraps `http.ResponseWriter`, returns aah framework response
// writer that allows to advantage of response process.
// Deprecated use `WrapGzipWriter` instead.
func GetGzipResponseWriter(w ResponseWriter) ResponseWriter {
gr := grPool.Get().(*GzipResponse)
gr.gw = acquireGzipWriter(w)
gr.r = w.(*Response)
return gr
}

// PutGzipResponseWiriter method resets and puts the gzip writer into pool.
// Deprecated use `ReleaseResponseWriter` instead.
func PutGzipResponseWiriter(rw ResponseWriter) {
releaseGzipResponse(rw.(*GzipResponse))
// GzipResponse extends `ahttp.Response` to provides gzip compression for response
// bytes to the underlying response.
type GzipResponse struct {
r *Response
gw *gzip.Writer
}

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Response methods
//___________________________________

// Status method returns HTTP response status code. If status is not yet written
// it reurns 0.
func (g *GzipResponse) Status() int {
Expand All @@ -82,9 +58,7 @@ func (g *GzipResponse) Header() http.Header {

// Write method writes bytes into Response.
func (g *GzipResponse) Write(b []byte) (int, error) {
g.r.setContentTypeIfNotSet(b)
g.r.WriteHeader(http.StatusOK)

size, err := g.gw.Write(b)
g.r.bytesWritten += size
return size, err
Expand All @@ -97,8 +71,9 @@ func (g *GzipResponse) BytesWritten() int {

// Close method closes the writer if possible.
func (g *GzipResponse) Close() error {
ess.CloseQuietly(g.gw)
g.gw = nil
if err := g.gw.Close(); err != nil {
return err
}
return g.r.Close()
}

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

// releaseGzipResponse method resets and puts the gzip response into pool.
func releaseGzipResponse(gw *GzipResponse) {
releaseGzipWriter(gw.gw)
releaseResponse(gw.r)
_ = gw.Close()
gwPool.Put(gw.gw)
releaseResponse(gw.r)
grPool.Put(gw)
}

Expand All @@ -158,8 +133,3 @@ func acquireGzipWriter(w io.Writer) *gzip.Writer {
ngw.Reset(w)
return ngw
}

func releaseGzipWriter(gw *gzip.Writer) {
_ = gw.Close()
gwPool.Put(gw)
}
6 changes: 3 additions & 3 deletions gzip_response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import (
func TestHTTPGzipWriter(t *testing.T) {
handler := func(w http.ResponseWriter, r *http.Request) {
GzipLevel = gzip.BestSpeed
gw := GetGzipResponseWriter(GetResponseWriter(w))
defer PutGzipResponseWiriter(gw)
gw := WrapGzipWriter(AcquireResponseWriter(w))
defer ReleaseResponseWriter(gw)

gw.Header().Set(HeaderVary, HeaderAcceptEncoding)
gw.Header().Set(HeaderContentEncoding, "gzip")
Expand Down Expand Up @@ -92,7 +92,7 @@ func TestHTTPGzipHijack(t *testing.T) {
ngw, _ := gzip.NewWriterLevel(w, GzipLevel)
gwPool.Put(ngw)
}
gw := WrapGzipWriter(GetResponseWriter(w))
gw := WrapGzipWriter(AcquireResponseWriter(w))

con, rw, err := gw.(http.Hijacker).Hijack()
assert.FailOnError(t, err, "")
Expand Down
Loading

0 comments on commit d4d049f

Please sign in to comment.