121 lines
3.2 KiB
Go
121 lines
3.2 KiB
Go
|
package server
|
||
|
|
||
|
import (
|
||
|
"net/http"
|
||
|
"sync"
|
||
|
|
||
|
"github.com/asim/go-micro/client"
|
||
|
log "github.com/cihub/seelog"
|
||
|
)
|
||
|
|
||
|
var ctxs = struct {
|
||
|
sync.Mutex
|
||
|
m map[*http.Request]*serverContext
|
||
|
}{
|
||
|
m: make(map[*http.Request]*serverContext),
|
||
|
}
|
||
|
|
||
|
// A server context interface
|
||
|
type Context interface {
|
||
|
Request() Request // the request made to the server
|
||
|
Headers() Headers // the response headers
|
||
|
NewRequest(string, string, interface{}) client.Request // a new scoped client request
|
||
|
NewProtoRequest(string, string, interface{}) client.Request // a new scoped client request
|
||
|
NewJsonRequest(string, string, interface{}) client.Request // a new scoped client request
|
||
|
}
|
||
|
|
||
|
// context represents the context of an in-flight HTTP request.
|
||
|
// It implements the appengine.Context and http.ResponseWriter interfaces.
|
||
|
type serverContext struct {
|
||
|
req *serverRequest
|
||
|
outCode int
|
||
|
outHeader http.Header
|
||
|
outBody []byte
|
||
|
}
|
||
|
|
||
|
// Copied from $GOROOT/src/pkg/net/http/transfer.go. Some response status
|
||
|
// codes do not permit a response body (nor response entity headers such as
|
||
|
// Content-Length, Content-Type, etc).
|
||
|
func bodyAllowedForStatus(status int) bool {
|
||
|
switch {
|
||
|
case status >= 100 && status <= 199:
|
||
|
return false
|
||
|
case status == 204:
|
||
|
return false
|
||
|
case status == 304:
|
||
|
return false
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func getServerContext(req *http.Request) *serverContext {
|
||
|
ctxs.Lock()
|
||
|
c := ctxs.m[req]
|
||
|
ctxs.Unlock()
|
||
|
|
||
|
if c == nil {
|
||
|
// Someone passed in an http.Request that is not in-flight.
|
||
|
panic("NewContext passed an unknown http.Request")
|
||
|
}
|
||
|
return c
|
||
|
}
|
||
|
|
||
|
func (c *serverContext) NewRequest(service, method string, request interface{}) client.Request {
|
||
|
req := client.NewRequest(service, method, request)
|
||
|
// TODO: set headers and scope
|
||
|
req.Headers().Set("X-User-Session", c.Request().Session("X-User-Session"))
|
||
|
return req
|
||
|
}
|
||
|
|
||
|
func (c *serverContext) NewProtoRequest(service, method string, request interface{}) client.Request {
|
||
|
req := client.NewProtoRequest(service, method, request)
|
||
|
// TODO: set headers and scope
|
||
|
req.Headers().Set("X-User-Session", c.Request().Session("X-User-Session"))
|
||
|
return req
|
||
|
}
|
||
|
|
||
|
func (c *serverContext) NewJsonRequest(service, method string, request interface{}) client.Request {
|
||
|
req := client.NewJsonRequest(service, method, request)
|
||
|
// TODO: set headers and scope
|
||
|
req.Headers().Set("X-User-Session", c.Request().Session("X-User-Session"))
|
||
|
return req
|
||
|
}
|
||
|
|
||
|
// The response headers
|
||
|
func (c *serverContext) Headers() Headers {
|
||
|
return c.outHeader
|
||
|
}
|
||
|
|
||
|
// The response headers
|
||
|
func (c *serverContext) Header() http.Header {
|
||
|
return c.outHeader
|
||
|
}
|
||
|
|
||
|
// The request made to the server
|
||
|
func (c *serverContext) Request() Request {
|
||
|
return c.req
|
||
|
}
|
||
|
|
||
|
func (c *serverContext) Write(b []byte) (int, error) {
|
||
|
if c.outCode == 0 {
|
||
|
c.WriteHeader(http.StatusOK)
|
||
|
}
|
||
|
if len(b) > 0 && !bodyAllowedForStatus(c.outCode) {
|
||
|
return 0, http.ErrBodyNotAllowed
|
||
|
}
|
||
|
c.outBody = append(c.outBody, b...)
|
||
|
return len(b), nil
|
||
|
}
|
||
|
|
||
|
func (c *serverContext) WriteHeader(code int) {
|
||
|
if c.outCode != 0 {
|
||
|
log.Errorf("WriteHeader called multiple times on request.")
|
||
|
return
|
||
|
}
|
||
|
c.outCode = code
|
||
|
}
|
||
|
|
||
|
func GetContext(r *http.Request) *serverContext {
|
||
|
return getServerContext(r)
|
||
|
}
|