mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
HTTP trigger http-stream tests (#1241)
This commit is contained in:
33
vendor/github.com/fnproject/fdk-go/circle.yml
generated
vendored
Normal file
33
vendor/github.com/fnproject/fdk-go/circle.yml
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/golang:1.11.0
|
||||
working_directory: ~/fdk-go
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: true
|
||||
- run: docker version
|
||||
- run: docker pull fnproject/fnserver
|
||||
# installing Fn CLI and starting the Fn server
|
||||
- run:
|
||||
command: |
|
||||
curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh
|
||||
- run:
|
||||
command: fn build
|
||||
working_directory: examples/hello
|
||||
- deploy:
|
||||
command: |
|
||||
if [[ "${CIRCLE_BRANCH}" == "master" && -z "${CIRCLE_PR_REPONAME}" ]]; then
|
||||
func_version=$(awk '/^version:/ { print $2; }' func.yaml)
|
||||
printenv DOCKER_PASS | docker login -u ${DOCKER_USER} --password-stdin
|
||||
git config --global user.email "ci@fnproject.com"
|
||||
git config --global user.name "CI"
|
||||
git branch --set-upstream-to=origin/${CIRCLE_BRANCH} ${CIRCLE_BRANCH}
|
||||
docker tag "hello:${func_version}" "fnproject/fdk-go-hello:${func_version}"
|
||||
docker tag "hello:${func_version}" "fnproject/fdk-go-hello:latest"
|
||||
docker push "fnproject/fdk-go-hello:${func_version}"
|
||||
docker push "fnproject/fdk-go-hello:latest"
|
||||
fi
|
||||
working_directory: examples/hello
|
||||
2
vendor/github.com/fnproject/fdk-go/examples/hello/Gopkg.lock
generated
vendored
2
vendor/github.com/fnproject/fdk-go/examples/hello/Gopkg.lock
generated
vendored
@@ -8,7 +8,7 @@
|
||||
".",
|
||||
"utils"
|
||||
]
|
||||
revision = "5d768b2006f11737b6a69a758ddd6d2fac04923e"
|
||||
revision = "c6ce6afbce0935ffdf0e228471406d0f966736a5"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
|
||||
17
vendor/github.com/fnproject/fdk-go/examples/hello/func.go
generated
vendored
17
vendor/github.com/fnproject/fdk-go/examples/hello/func.go
generated
vendored
@@ -13,20 +13,17 @@ func main() {
|
||||
fdk.Handle(fdk.HandlerFunc(myHandler))
|
||||
}
|
||||
|
||||
func myHandler(ctx context.Context, in io.Reader, out io.Writer) {
|
||||
var person struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
json.NewDecoder(in).Decode(&person)
|
||||
if person.Name == "" {
|
||||
person.Name = "World"
|
||||
}
|
||||
type Person struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func myHandler(ctx context.Context, in io.Reader, out io.Writer) {
|
||||
p := &Person{Name: "World"}
|
||||
json.NewDecoder(in).Decode(p)
|
||||
msg := struct {
|
||||
Msg string `json:"message"`
|
||||
}{
|
||||
Msg: fmt.Sprintf("Hello %s", person.Name),
|
||||
Msg: fmt.Sprintf("Hello %s", p.Name),
|
||||
}
|
||||
|
||||
json.NewEncoder(out).Encode(&msg)
|
||||
}
|
||||
|
||||
6
vendor/github.com/fnproject/fdk-go/examples/hello/func.yaml
generated
vendored
Normal file
6
vendor/github.com/fnproject/fdk-go/examples/hello/func.yaml
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
schema_version: 20180708
|
||||
name: hello
|
||||
version: 0.0.1
|
||||
runtime: go
|
||||
entrypoint: ./func
|
||||
format: http-stream
|
||||
16
vendor/github.com/fnproject/fdk-go/fdk.go
generated
vendored
16
vendor/github.com/fnproject/fdk-go/fdk.go
generated
vendored
@@ -24,6 +24,7 @@ func (f HandlerFunc) Serve(ctx context.Context, in io.Reader, out io.Writer) {
|
||||
func Context(ctx context.Context) *Ctx {
|
||||
utilsCtx := utils.Context(ctx)
|
||||
return &Ctx{
|
||||
HTTPHeader: utilsCtx.HTTPHeader,
|
||||
Header: utilsCtx.Header,
|
||||
Config: utilsCtx.Config,
|
||||
RequestURL: utilsCtx.RequestURL,
|
||||
@@ -33,6 +34,7 @@ func Context(ctx context.Context) *Ctx {
|
||||
|
||||
func WithContext(ctx context.Context, fnctx *Ctx) context.Context {
|
||||
utilsCtx := &utils.Ctx{
|
||||
HTTPHeader: fnctx.HTTPHeader,
|
||||
Header: fnctx.Header,
|
||||
Config: fnctx.Config,
|
||||
RequestURL: fnctx.RequestURL,
|
||||
@@ -43,7 +45,12 @@ func WithContext(ctx context.Context, fnctx *Ctx) context.Context {
|
||||
|
||||
// Ctx provides access to Config and Headers from fn.
|
||||
type Ctx struct {
|
||||
Header http.Header
|
||||
// Header are the unmodified headers as sent to the container, see
|
||||
// HTTPHeader for specific trigger headers
|
||||
Header http.Header
|
||||
// HTTPHeader are the request headers as they appear on the original HTTP request,
|
||||
// for an http trigger.
|
||||
HTTPHeader http.Header
|
||||
Config map[string]string
|
||||
RequestURL string
|
||||
Method string
|
||||
@@ -78,5 +85,12 @@ func WriteStatus(out io.Writer, status int) {
|
||||
// function and fn server via any of the supported formats.
|
||||
func Handle(handler Handler) {
|
||||
format, _ := os.LookupEnv("FN_FORMAT")
|
||||
|
||||
path := os.Getenv("FN_LISTENER")
|
||||
if path != "" {
|
||||
utils.StartHTTPServer(handler, path, format)
|
||||
return
|
||||
}
|
||||
|
||||
utils.Do(handler, format, os.Stdin, os.Stdout)
|
||||
}
|
||||
|
||||
168
vendor/github.com/fnproject/fdk-go/utils/httpstream.go
generated
vendored
Normal file
168
vendor/github.com/fnproject/fdk-go/utils/httpstream.go
generated
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// in case we go over the timeout, need to use a pool since prev buffer may not be freed
|
||||
var bufPool = &sync.Pool{New: func() interface{} { return new(bytes.Buffer) }}
|
||||
|
||||
type HTTPHandler struct {
|
||||
handler Handler
|
||||
}
|
||||
|
||||
func (h *HTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
buf := bufPool.Get().(*bytes.Buffer)
|
||||
defer bufPool.Put(buf)
|
||||
|
||||
resp := Response{
|
||||
Writer: buf,
|
||||
Status: 200,
|
||||
Header: make(http.Header), // XXX(reed): pool these too
|
||||
}
|
||||
|
||||
ctx := WithContext(r.Context(), &Ctx{
|
||||
Config: BuildConfig(),
|
||||
})
|
||||
|
||||
ctx, cancel := decapHeaders(ctx, r)
|
||||
defer cancel()
|
||||
|
||||
h.handler.Serve(ctx, r.Body, &resp)
|
||||
|
||||
encapHeaders(w, resp)
|
||||
|
||||
// XXX(reed): 504 if ctx is past due / handle errors with 5xx? just 200 for now
|
||||
// copy response from user back up now with headers in place...
|
||||
io.Copy(w, buf)
|
||||
|
||||
// XXX(reed): handle streaming, we have to intercept headers but not necessarily body (ie no buffer)
|
||||
}
|
||||
|
||||
func encapHeaders(fn http.ResponseWriter, user Response) {
|
||||
fnh := fn.Header()
|
||||
fnh.Set("Fn-Http-Status", strconv.Itoa(user.Status))
|
||||
|
||||
for k, vs := range user.Header {
|
||||
switch k {
|
||||
case "Content-Type":
|
||||
// don't modify this one...
|
||||
default:
|
||||
// prepend this guy
|
||||
k = "Fn-Http-H-" + k
|
||||
}
|
||||
|
||||
for _, v := range vs {
|
||||
fnh.Add(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO can make this the primary means of context construction
|
||||
func decapHeaders(ctx context.Context, r *http.Request) (_ context.Context, cancel func()) {
|
||||
rctx := Context(ctx)
|
||||
var deadline string
|
||||
|
||||
// copy the original headers in then reduce for http headers
|
||||
rctx.Header = r.Header
|
||||
rctx.HTTPHeader = make(http.Header, len(r.Header)) // XXX(reed): oversized, esp if not http
|
||||
|
||||
// find things we need, and for http headers add them to the httph bucket
|
||||
|
||||
for k, vs := range r.Header {
|
||||
switch k {
|
||||
case "Fn-Deadline":
|
||||
deadline = vs[0]
|
||||
case "Fn-Call-Id":
|
||||
rctx.callId = vs[0]
|
||||
case "Content-Type":
|
||||
// just leave this one instead of deleting
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(k, "Fn-Http-") {
|
||||
// XXX(reed): we need 2 header buckets on ctx, one for these and one for the 'original req' headers
|
||||
// for now just nuke so the headers are clean...
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case k == "Fn-Http-Request-Url":
|
||||
rctx.RequestURL = vs[0]
|
||||
case k == "Fn-Http-Method":
|
||||
rctx.Method = vs[0]
|
||||
case strings.HasPrefix(k, "Fn-Http-H-"):
|
||||
for _, v := range vs {
|
||||
rctx.HTTPHeader.Add(strings.TrimPrefix(k, "Fn-Http-H-"), v)
|
||||
}
|
||||
default:
|
||||
// XXX(reed): just delete it? how is it here? maybe log/panic
|
||||
}
|
||||
}
|
||||
|
||||
return CtxWithDeadline(ctx, deadline)
|
||||
}
|
||||
|
||||
func StartHTTPServer(handler Handler, path, format string) {
|
||||
|
||||
uri, err := url.Parse(path)
|
||||
if err != nil {
|
||||
log.Fatalln("url parse error: ", path, err)
|
||||
}
|
||||
|
||||
server := http.Server{
|
||||
Handler: &HTTPHandler{
|
||||
handler: handler,
|
||||
},
|
||||
}
|
||||
|
||||
// try to remove pre-existing UDS: ignore errors here
|
||||
phonySock := filepath.Join(filepath.Dir(uri.Path), "phony"+filepath.Base(uri.Path))
|
||||
if uri.Scheme == "unix" {
|
||||
os.Remove(phonySock)
|
||||
os.Remove(uri.Path)
|
||||
}
|
||||
|
||||
listener, err := net.Listen(uri.Scheme, phonySock)
|
||||
if err != nil {
|
||||
log.Fatalln("net.Listen error: ", err)
|
||||
}
|
||||
|
||||
if uri.Scheme == "unix" {
|
||||
sockPerm(phonySock, uri.Path)
|
||||
}
|
||||
|
||||
err = server.Serve(listener)
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalln("serve error: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func sockPerm(phonySock, realSock string) {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
// somehow this is the best way to get a permissioned sock file, don't ask questions, life is sad and meaningless
|
||||
err := os.Chmod(phonySock, 0666)
|
||||
if err != nil {
|
||||
log.Fatalln("error giving sock file a perm", err)
|
||||
}
|
||||
|
||||
err = os.Link(phonySock, realSock)
|
||||
if err != nil {
|
||||
log.Fatalln("error linking fake sock to real sock", err)
|
||||
}
|
||||
}
|
||||
15
vendor/github.com/fnproject/fdk-go/utils/utils.go
generated
vendored
15
vendor/github.com/fnproject/fdk-go/utils/utils.go
generated
vendored
@@ -26,12 +26,25 @@ func WithContext(ctx context.Context, fnctx *Ctx) context.Context {
|
||||
|
||||
// Ctx provides access to Config and Headers from fn.
|
||||
type Ctx struct {
|
||||
Header http.Header
|
||||
// Header are the unmodified headers as sent to the container, see
|
||||
// HTTPHeader for specific trigger headers
|
||||
Header http.Header
|
||||
|
||||
// HTTPHeader are the request headers as they appear on the original HTTP request,
|
||||
// for an http trigger.
|
||||
HTTPHeader http.Header
|
||||
Config map[string]string
|
||||
RequestURL string
|
||||
Method string
|
||||
|
||||
// XXX(reed): should turn this whole mess into some kind of event that we can
|
||||
// morph into another type of an event after http/json/default die
|
||||
// XXX(reed): should strip out eg FN_APP_NAME, etc as fields so Config is actually the config not config + fn's env vars
|
||||
callId string
|
||||
}
|
||||
|
||||
func (c Ctx) CallId() string { return c.callId }
|
||||
|
||||
type key struct{}
|
||||
|
||||
var ctxKey = new(key)
|
||||
|
||||
Reference in New Issue
Block a user