HTTP Handler for functions

The function handler will be the handler for each http trigger.  It
will look up the function service map to find a service for the
function (and call the pool manager to create a new service if one
doesn't already exist, though this isn't implemented yet).  It'll then
proxy the request to the service.
This commit is contained in:
Soam Vasani
2016-08-30 16:41:16 -07:00
parent a33f4e48e9
commit 2b0b2e28ae
3 changed files with 201 additions and 0 deletions
+70
View File
@@ -0,0 +1,70 @@
/*
Copyright 2016 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package router
import (
"log"
"net/http"
"net/http/httputil"
"errors"
"net/url"
)
type functionHandler struct {
fmap *functionServiceMap
function
}
func getService(f *function) (*url.URL, error) {
return nil, errors.New("not implemented")
}
func (fh *functionHandler) handler(responseWriter http.ResponseWriter, request *http.Request) {
serviceUrl, err := fh.fmap.lookup(&fh.function)
if (err != nil) {
// Cache miss: request the Pool Manager to make a new service.
serviceUrl, poolErr := getService(&fh.function)
if (poolErr != nil) {
// now we're really screwed
log.Printf("Failed to get service for function (%v,%v): %v",
fh.function.name, fh.function.uid, poolErr);
responseWriter.WriteHeader(500) // TODO: make this smarter based on the actual error
return
}
// add it to the map
fh.fmap.assign(&fh.function, serviceUrl)
}
// Proxy off our request to the serviceUrl, and send the response back.
// TODO: As an optimization we may want to cache proxies too -- this would get us
// connection reuse and possibly better performance
director := func(req *http.Request) {
// send this request to serviceurl
req.URL.Scheme = serviceUrl.Scheme
req.URL.Host = serviceUrl.Host
req.URL.Path = serviceUrl.Path
// leave the query string intact (req.URL.RawQuery)
if _, ok := req.Header["User-Agent"]; !ok {
// explicitly disable User-Agent so it's not set to default value
req.Header.Set("User-Agent", "")
}
}
proxy := &httputil.ReverseProxy{Director: director}
proxy.ServeHTTP(responseWriter, request)
}
+61
View File
@@ -0,0 +1,61 @@
/*
Copyright 2016 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package router
import (
"log"
"testing"
"net/http"
// "net/http/httputil"
"net/http/httptest"
"net/url"
)
func createBackendService(testResponseString string) *url.URL {
backendServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(testResponseString))
}))
backendURL, err := url.Parse(backendServer.URL)
if err != nil {
panic("error parsing url")
}
return backendURL
}
/*
1. Create a service at some URL
2. Add it to the function service map
3. Create a http server with some trigger url pointed at function handler
4. Send a request to that server, ensure it reaches the first service.
*/
func TestFunctionProxying(t *testing.T) {
testResponseString := "hi"
backendURL := createBackendService(testResponseString)
log.Printf("Created backend svc at %v", backendURL)
fn := &function{name: "foo", uid: "xxx"}
fmap := makeFunctionServiceMap()
fmap.assign(fn, backendURL)
fh := &functionHandler{fmap: fmap, function: *fn}
functionHandlerServer := httptest.NewServer(http.HandlerFunc(fh.handler))
fhURL := functionHandlerServer.URL
testRequest(fhURL, testResponseString)
}
+70
View File
@@ -0,0 +1,70 @@
/*
Copyright 2016 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
This is the Fission Router package.
Its job is to:
1. Keep track of HTTP triggers and their mappings to functions
Use the controller API to get and watch this state.
2. Given a function, get a reference to a routable function run service
Use the ContainerPoolManager API to get a service backed by one
or more function run containers. The container(s) backing the
service may be newly created, or they might be reused. The only
requirement is that one or more containers backs the service.
3. Forward the request to the service, and send the response back.
Plain ol HTTP.
*/
package router
import (
// "fmt"
// "net/http"
)
type (
function struct {
name string
uid string
}
httptrigger struct {
urlPattern string
function
}
options struct {
port int
poolManagerUrl string
//...
}
)
// request url ---[trigger]---> function(name,uid) ----[pool mgr]----> k8s service url
// request url ---[trigger]---> function(name, deployment) ----[deployment]----> function(name, uid) ----[pool mgr]---> k8s service url