Skip to content

Commit

Permalink
Fixed significant bug where proxy wouldn;t work with virtual host ser…
Browse files Browse the repository at this point in the history
…vers like heroku
  • Loading branch information
Martin Buhr committed Jul 30, 2014
1 parent 9c08218 commit 82695eb
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 10 deletions.
2 changes: 2 additions & 0 deletions api_definition_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"regexp"
"strings"
"time"
"net/url"
)

// APIDefinition represents the configuration for a single proxied API and it's versions.
Expand Down Expand Up @@ -101,6 +102,7 @@ type APISpec struct {
APIDefinition
RxPaths map[string][]URLSpec
WhiteListEnabled map[string]bool
target *url.URL
}

// APIDefinitionLoader will load an Api definition from a storage system. It has two methods LoadDefinitionsFromMongo()
Expand Down
5 changes: 3 additions & 2 deletions apps/app_sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"not_versioned": true,
"versions": {
"v1": {
"name": "v1",
"name": "Default",
"expires": "3000-01-02 15:04",
"paths": {
"ignored": [],
Expand All @@ -25,6 +25,7 @@
},
"proxy": {
"listen_path": "/tyk-api-test/",
"target_url": "http://jive.ly"
"target_url": "http://jive.ly",
"strip_listen_path": true
}
}
32 changes: 32 additions & 0 deletions apps/quickstart.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "Tyk Test API",
"api_id": "1",
"org_id": "default",
"definition": {
"location": "",
"key": ""
},
"use_keyless": true,
"auth": {
"auth_header_name": ""
},
"version_data": {
"not_versioned": true,
"versions": {
"Default": {
"name": "Default",
"expires": "3000-01-02 15:04",
"paths": {
"ignored": [],
"white_list": [],
"black_list": []
}
}
}
},
"proxy": {
"listen_path": "/quickstart/",
"target_url": "http://httpbin.org/",
"strip_listen_path": true
}
}
2 changes: 2 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type Config struct {
Secret string `json:"secret"`
TemplatePath string `json:"template_path"`
UseDBAppConfigs bool `json:"use_db_app_configs"`
AppPath string `json:"app_path"`
Storage struct {
Type string `json:"type"`
Host string `json:"host"`
Expand All @@ -35,6 +36,7 @@ func WriteDefaultConf(configStruct *Config) {
configStruct.Secret = "352d20ee67be67f6340b4c0605b044b7"
configStruct.TemplatePath = "/etc/tyk/templates"
configStruct.Storage.Type = "memory"
configStruct.AppPath = "/etc/tyk/apps/"
configStruct.Storage.Host = "localhost"
configStruct.Storage.Username = "user"
configStruct.Storage.Password = "password"
Expand Down
11 changes: 6 additions & 5 deletions handler_success.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ type SuccessHandler struct {
// final destination, this is invoked by the ProxyHandler or right at the start of a request chain if the URL
// Spec states the path is Ignored
func (s SuccessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

// Make sure we get the correct target URL
if s.Spec.APIDefinition.Proxy.StripListenPath {
r.URL.Path = strings.Replace(r.URL.Path, s.Spec.Proxy.ListenPath, "", 1)
}

if config.EnableAnalytics {
t := time.Now()

Expand All @@ -51,11 +57,6 @@ func (s SuccessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
version = "Non Versioned"
}

// Make sure we get the correct target URL
if s.Spec.APIDefinition.Proxy.StripListenPath {
r.URL.Path = strings.Replace(r.URL.Path, s.Spec.Proxy.ListenPath, "", 1)
}

// If OAuth, we need to grab it from the session, which may or may not exist
OauthClientID := ""
thisSessionState := context.Get(r, SessionData)
Expand Down
6 changes: 3 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"html/template"
"net"
"net/http"
"net/http/httputil"
"net/url"
"os"
"strconv"
Expand Down Expand Up @@ -107,7 +106,7 @@ func getAPISpecs() []APISpec {
log.Info("Using App Configuration from Mongo DB")
APISpecs = thisAPILoader.LoadDefinitionsFromMongo()
} else {
APISpecs = thisAPILoader.LoadDefinitions("./apps/")
APISpecs = thisAPILoader.LoadDefinitions(config.AppPath)
}

return APISpecs
Expand Down Expand Up @@ -177,7 +176,8 @@ func loadApps(APISpecs []APISpec, Muxer *http.ServeMux) {
addOAuthHandlers(spec, Muxer, false)
}

proxy := httputil.NewSingleHostReverseProxy(remote)
proxy := TykNewSingleHostReverseProxy(remote)
spec.target = remote

proxyHandler := http.HandlerFunc(ProxyHandler(proxy, spec))
tykMiddleware := TykMiddleware{spec, proxy}
Expand Down
23 changes: 23 additions & 0 deletions quickstart.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"listen_port": 5000,
"secret": "352d20ee67be67f6340b4c0605b044b7",
"use_db_app_configs": false,
"template_path": "templates",
"app_path": "apps",
"storage": {
"type": "redis",
"host": "localhost",
"port": 6379,
"username": "user",
"password": "test"
},
"enable_analytics": false,
"analytics_config": {
"type": "csv",
"csv_dir": "/tmp/",
"purge_delay": 10,
"mongo_url": "",
"mongo_db_name": "",
"mongo_collection": ""
}
}
51 changes: 51 additions & 0 deletions tykReverseProxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// HTTP reverse proxy handler

package main

import (
"net/http"
"net/url"
"strings"
"net/http/httputil"
)


// Copied form the original stdlib for ReverseProxy
func singleJoiningSlash(a, b string) string {
aslash := strings.HasSuffix(a, "/")
bslash := strings.HasPrefix(b, "/")
switch {
case aslash && bslash:
return a + b[1:]
case !aslash && !bslash:
return a + "/" + b
}
return a + b
}

// TykNewSingleHostReverseProxy returns a new ReverseProxy that rewrites
// URLs to the scheme, host, and base path provided in target. If the
// target's path is "/base" and the incoming request was for "/dir",
// the target request will be for /base/dir. This version modifies the
// stdlib version by also setting the host to the target, this allows
// us to work with heroku and other such providers
func TykNewSingleHostReverseProxy(target *url.URL) *httputil.ReverseProxy {
targetQuery := target.RawQuery
director := func(req *http.Request) {
req.URL.Scheme = target.Scheme
req.URL.Host = target.Host
req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
req.Host = target.Host
if targetQuery == "" || req.URL.RawQuery == "" {
req.URL.RawQuery = targetQuery + req.URL.RawQuery
} else {
req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
}
}
return &httputil.ReverseProxy{Director: director}
}

0 comments on commit 82695eb

Please sign in to comment.