Skip to content

Commit

Permalink
Merge pull request #17 for v0.10 Release
Browse files Browse the repository at this point in the history
  • Loading branch information
jeevatkm authored Sep 1, 2017
2 parents 62452f6 + 7526990 commit f9b651b
Show file tree
Hide file tree
Showing 11 changed files with 278 additions and 25 deletions.
6 changes: 5 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ branches:

go:
- 1.8
- 1.8.x
- 1.9
- tip

go_import_path: aahframework.org/ahttp.v0

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

script:
- bash <(curl -s https://aahframework.org/go-test)

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# 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.9-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)
[![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)

***v0.9 [released](https://github.com/go-aah/ahttp/releases/latest) and tagged on Jul 21, 2017***
***v0.10 [released](https://github.com/go-aah/ahttp/releases/latest) and tagged on Sep 01, 2017***

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

Expand Down
3 changes: 0 additions & 3 deletions ahttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ import (
"net/http"
)

// Version no. of aah framework ahttp library
const Version = "0.9"

// HTTP Method names
const (
MethodGet = http.MethodGet
Expand Down
2 changes: 1 addition & 1 deletion content_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ type (
// E.g.:
// contentType.IsEqual("application/json")
func (c *ContentType) IsEqual(contentType string) bool {
return strings.HasPrefix(c.Raw(), strings.ToLower(contentType))
return strings.HasPrefix(c.String(), strings.ToLower(contentType))
}

// Charset method returns charset of content-type
Expand Down
4 changes: 2 additions & 2 deletions content_type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func TestHTTPParseContentType(t *testing.T) {

req5 := createRawHTTPRequest(HeaderContentType, "text/html;charset")
contentType = ParseContentType(req5)
assert.Equal(t, "", contentType.Mime)
assert.Equal(t, "", contentType.String())
assert.True(t, (contentType.Mime == "" || contentType.Mime == "text/html"))
assert.True(t, (contentType.String() == "" || contentType.String() == "text/html"))
assert.Equal(t, "iso-8859-1", contentType.Charset("iso-8859-1"))
}
2 changes: 1 addition & 1 deletion gzip_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ var (

// GetGzipResponseWriter wraps `http.ResponseWriter`, returns aah framework response
// writer that allows to advantage of response process.
// Deprecated use `AcquireResponseWriter` instead.
// Deprecated use `WrapGzipWriter` instead.
func GetGzipResponseWriter(w ResponseWriter) ResponseWriter {
gr := grPool.Get().(*GzipResponse)
gr.gw = acquireGzipWriter(w)
Expand Down
3 changes: 3 additions & 0 deletions header.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ const (
HeaderLocation = "Location"
HeaderOrigin = "Origin"
HeaderMethod = "Method"
HeaderPublicKeyPins = "Public-Key-Pins"
HeaderRange = "Range"
HeaderReferer = "Referer"
HeaderReferrerPolicy = "Referrer-Policy"
HeaderRetryAfter = "Retry-After"
HeaderServer = "Server"
HeaderSetCookie = "Set-Cookie"
Expand All @@ -80,6 +82,7 @@ const (
HeaderXForwardedServer = "X-Forwarded-Server"
HeaderXFrameOptions = "X-Frame-Options"
HeaderXHTTPMethodOverride = "X-HTTP-Method-Override"
HeaderXPermittedCrossDomainPolicies = "X-Permitted-Cross-Domain-Policies"
HeaderXRealIP = "X-Real-Ip"
HeaderXRequestedWith = "X-Requested-With"
HeaderXRequestID = "X-Request-Id"
Expand Down
102 changes: 89 additions & 13 deletions request.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,25 @@
package ahttp

import (
"errors"
"fmt"
"io"
"mime/multipart"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"sync"

"aahframework.org/essentials.v0"
)

const (
jsonpReqParamKey = "callback"
ajaxHeaderValue = "XMLHttpRequest"
jsonpReqParamKey = "callback"
ajaxHeaderValue = "XMLHttpRequest"
websocketHeaderValue = "websocket"
)

var requestPool = &sync.Pool{New: func() interface{} { return &Request{} }}
Expand All @@ -35,6 +40,9 @@ type (
// Host value of the HTTP 'Host' header (e.g. 'example.com:8080').
Host string

// Proto value of the current HTTP request protocol. (e.g. HTTP/1.1, HTTP/2.0)
Proto string

// Method request method e.g. `GET`, `POST`, etc.
Method string

Expand All @@ -60,10 +68,6 @@ type (
// Most quailfied one based on quality factor.
AcceptEncoding *AcceptSpec

// Payload holds the value from HTTP request for `Content-Type`
// JSON and XML.
Payload []byte

// Params contains values from Path, Query, Form and File.
Params *Params

Expand All @@ -88,7 +92,7 @@ type (
// Raw an object of Go HTTP server, direct interaction with
// raw object is not encouraged.
//
// Raw field to be unexported on v1 release, use `Req.Unwarp()` instead.
// DEPRECATED: Raw field to be unexported on v1 release, use `Req.Unwarp()` instead.
Raw *http.Request
}

Expand All @@ -110,6 +114,7 @@ type (
func ParseRequest(r *http.Request, req *Request) *Request {
req.Scheme = identifyScheme(r)
req.Host = host(r)
req.Proto = r.Proto
req.Method = r.Method
req.Path = r.URL.Path
req.Header = r.Header
Expand All @@ -132,12 +137,12 @@ func ParseRequest(r *http.Request, req *Request) *Request {

// Cookie method returns a named cookie from HTTP request otherwise error.
func (r *Request) Cookie(name string) (*http.Cookie, error) {
return r.Raw.Cookie(name)
return r.Unwrap().Cookie(name)
}

// Cookies method returns all the cookies from HTTP request.
func (r *Request) Cookies() []*http.Cookie {
return r.Raw.Cookies()
return r.Unwrap().Cookies()
}

// IsJSONP method returns true if request URL query string has "callback=function_name".
Expand All @@ -146,12 +151,17 @@ func (r *Request) IsJSONP() bool {
return !ess.IsStrEmpty(r.QueryValue(jsonpReqParamKey))
}

// IsAJAX methods returns true if the request header `X-Requested-With` is
// IsAJAX method returns true if request header `X-Requested-With` is
// `XMLHttpRequest` otherwise false.
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) == websocketHeaderValue
}

// PathValue method returns value for given Path param key otherwise empty string.
// For eg.: /users/:userId => PathValue("userId")
func (r *Request) PathValue(key string) string {
Expand Down Expand Up @@ -187,22 +197,78 @@ func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, e
return r.Params.FormFile(key)
}

//Unwrap returns the underlying http.Request
// Body method returns the HTTP request body.
func (r *Request) Body() io.ReadCloser {
return r.Unwrap().Body
}

// Unwrap method returns the underlying *http.Request.
func (r *Request) Unwrap() *http.Request {
return r.Raw
}

// SaveFile method saves an uploaded multipart file for given key from the HTTP
// request into given destination
func (r *Request) SaveFile(key, dstFile string) (int64, error) {
if ess.IsStrEmpty(dstFile) || ess.IsStrEmpty(key) {
return 0, errors.New("ahttp: key or dstFile is empty")
}

if ess.IsDir(dstFile) {
return 0, errors.New("ahttp: dstFile should not be a directory")
}

uploadedFile, _, err := r.FormFile(key)
if err != nil {
return 0, err
}
defer ess.CloseQuietly(uploadedFile)

return saveFile(uploadedFile, dstFile)
}

// SaveFiles method saves an uploaded multipart file(s) for the given key
// from the HTTP request into given destination directory. It uses the filename
// as uploaded filename from the request
func (r *Request) SaveFiles(key, dstPath string) ([]int64, []error) {
if !ess.IsDir(dstPath) {
return []int64{0}, []error{fmt.Errorf("ahttp: destination path, '%s' is not a directory", dstPath)}
}

if ess.IsStrEmpty(key) {
return []int64{0}, []error{fmt.Errorf("ahttp: form file key, '%s' is empty", key)}
}

var errs []error
var sizes []int64
for _, file := range r.Params.File[key] {
uploadedFile, err := file.Open()
if err != nil {
sizes = append(sizes, 0)
errs = append(errs, err)
continue
}

if size, err := saveFile(uploadedFile, filepath.Join(dstPath, file.Filename)); err != nil {
sizes = append(sizes, size)
errs = append(errs, err)
}
ess.CloseQuietly(uploadedFile)
}
return sizes, errs
}

// Reset method resets request instance for reuse.
func (r *Request) Reset() {
r.Scheme = ""
r.Host = ""
r.Proto = ""
r.Method = ""
r.Path = ""
r.Header = nil
r.ContentType = nil
r.AcceptContentType = nil
r.AcceptEncoding = nil
r.Payload = nil
r.Params = nil
r.Referer = ""
r.UserAgent = ""
Expand Down Expand Up @@ -269,7 +335,7 @@ func (p *Params) FormFile(key string) (multipart.File, *multipart.FileHeader, er
f, err := fh[0].Open()
return f, fh[0], err
}
return nil, nil, fmt.Errorf("error file is missing: %s", key)
return nil, nil, fmt.Errorf("ahttp: no such key/file: %s", key)
}
return nil, nil, nil
}
Expand Down Expand Up @@ -351,3 +417,13 @@ func isGzipAccepted(req *Request, r *http.Request) bool {
}
return false
}

func saveFile(r io.Reader, destFile string) (int64, error) {
f, err := os.OpenFile(destFile, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
if err != nil {
return 0, fmt.Errorf("ahttp: %s", err)
}
defer ess.CloseQuietly(f)

return io.Copy(f, r)
}
Loading

0 comments on commit f9b651b

Please sign in to comment.