jws: added parsing from http requests
diff --git a/jws/errors.go b/jws/errors.go
index 6120bc1..0512a0e 100644
--- a/jws/errors.go
+++ b/jws/errors.go
@@ -56,4 +56,7 @@
 	// ErrNotEnoughValidSignatures means the JWS did not meet the required
 	// number of signatures.
 	ErrNotEnoughValidSignatures = errors.New("not enough valid signatures in the JWS")
+
+	// ErrNoTokenInRequest means there's no token present inside the *http.Request.
+	ErrNoTokenInRequest = errors.New("no token present in request")
 )
diff --git a/jws/jws.go b/jws/jws.go
index 6673c9c..e84db43 100644
--- a/jws/jws.go
+++ b/jws/jws.go
@@ -3,6 +3,8 @@
 import (
 	"bytes"
 	"encoding/json"
+	"net/http"
+	"strings"
 
 	"github.com/SermoDigital/jose"
 	"github.com/SermoDigital/jose/crypto"
@@ -302,7 +304,7 @@
 	return parseCompact(encoded, false)
 }
 
-func parseCompact(encoded []byte, jwt bool) (*jws, error) {
+func parseCompact(encoded []byte, jwt bool, u ...json.Unmarshaler) (*jws, error) {
 
 	// This section loosely follows
 	// https://tools.ietf.org/html/rfc7519#section-7.2
@@ -329,8 +331,13 @@
 		return nil, err
 	}
 
+	var pl payload
+	if len(u) > 0 {
+		pl.u = u[0]
+	}
+
 	j := jws{
-		payload: &payload{},
+		payload: &pl,
 		plcache: parts[1],
 		sb:      []sigHead{s},
 		isJWT:   jwt,
@@ -354,6 +361,79 @@
 	return &j, nil
 }
 
+var (
+	// JWSFormKey is the form "key" which should be used inside
+	// ParseFromRequest if the request is a multipart.Form.
+	JWSFormKey = "access_token"
+
+	// MaxMemory is maximum amount of memory which should be used
+	// inside ParseFromRequest while parsing the multipart.Form
+	// if the request is a multipart.Form.
+	MaxMemory int64 = 10e6
+)
+
+// Format specifies which "format" the JWS is in -- Flat, General,
+// or compact. Additionally, constants for JWT/Unknown are added.
+type Format uint8
+
+const (
+	// Unknown format.
+	Unknown Format = iota
+
+	// Flat format.
+	Flat
+
+	// General format.
+	General
+
+	// Compact format.
+	Compact
+)
+
+var parseJumpTable = [^uint8(0)]func([]byte, ...json.Unmarshaler) (JWS, error){
+	Unknown: Parse,
+	Flat:    ParseFlat,
+	General: ParseGeneral,
+	Compact: ParseCompact,
+}
+
+func init() {
+	for i := range parseJumpTable {
+		if parseJumpTable[i] == nil {
+			parseJumpTable[i] = Parse
+		}
+	}
+}
+
+func fromHeader(req *http.Request) ([]byte, bool) {
+	if ah := req.Header.Get("Authorization"); ah != "" && len(ah) > 6 && strings.EqualFold(ah[0:6], "BEARER") {
+		return []byte(ah[:7]), true
+	}
+	return nil, false
+}
+
+func fromForm(req *http.Request) ([]byte, bool) {
+	if err := req.ParseMultipartForm(MaxMemory); err != nil {
+		return nil, false
+	}
+	if tokStr := req.Form.Get(JWSFormKey); tokStr != "" {
+		return []byte(tokStr), true
+	}
+	return nil, false
+}
+
+// ParseFromRequest tries to find the JWS in an http.Request.
+// This method will call ParseMultipartForm if there's no token in the header.
+func ParseFromRequest(req *http.Request, format Format, u ...json.Unmarshaler) (JWS, error) {
+	if b, ok := fromHeader(req); ok {
+		return parseJumpTable[format](b, u...)
+	}
+	if b, ok := fromForm(req); ok {
+		return parseJumpTable[format](b, u...)
+	}
+	return nil, ErrNoTokenInRequest
+}
+
 // IgnoreDupes should be set to true if the internal duplicate header key check
 // should ignore duplicate Header keys instead of reporting an error when
 // duplicate Header keys are found.
diff --git a/jws/jwt.go b/jws/jwt.go
index 51090b4..67f18f7 100644
--- a/jws/jwt.go
+++ b/jws/jwt.go
@@ -1,6 +1,7 @@
 package jws
 
 import (
+	"net/http"
 	"time"
 
 	"github.com/SermoDigital/jose/crypto"
@@ -33,6 +34,18 @@
 	return nil
 }
 
+// ParseJWTFromRequest tries to find the JWT in an http.Request.
+// This method will call ParseMultipartForm if there's no token in the header.
+func ParseJWTFromRequest(req *http.Request) (jwt.JWT, error) {
+	if b, ok := fromHeader(req); ok {
+		return ParseJWT(b)
+	}
+	if b, ok := fromForm(req); ok {
+		return ParseJWT(b)
+	}
+	return nil, ErrNoTokenInRequest
+}
+
 // ParseJWT parses a serialized jwt.JWT into a physical jwt.JWT.
 // If its payload isn't a set of claims (or able to be coerced into
 // a set of claims) it'll return an error stating the