Skip to content

Commit 51a9eee

Browse files
authored
Merge pull request #13 from moriyoshi/add-x-http-redirect
Add x-http-redirect: URL scheme
2 parents fbcffdc + f03fec6 commit 51a9eee

File tree

5 files changed

+144
-16
lines changed

5 files changed

+144
-16
lines changed

example.yml

+4
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,7 @@ hosts:
3838
X-Cgi-Script-Filename: /document_root$2
3939
X-Cgi-Script-Name: $2
4040
X-Cgi-Path-Info: $3
41+
http://redirect.example.com:
42+
- ^/no-scheme$: x-http-redirect://example.com/?a=2%20#23424
43+
- ^/with-scheme$: x-http-redirect:https://example.com/?a=2%20#23424
44+
- ^/with-scheme-and-status-code$: x-http-redirect:302:https://example.com/?a=2%20#23424

go.sum

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ github.com/moriyoshi/mimetypes v1.0.0 h1:nESQmdWurua/+7QzWnxMpbHYUd0mMsi6zKAkq3Z
66
github.com/moriyoshi/mimetypes v1.0.0/go.mod h1:BhYJSMKygU9xeSsBmDuWn+ger4JXdBwCzDazbMBPt/0=
77
github.com/moriyoshi/simplefiletx v1.0.0 h1:pgibEbJTapZ0BXKB0Xr/3HttANFvTx/CSlKVyHKJpkQ=
88
github.com/moriyoshi/simplefiletx v1.0.0/go.mod h1:HR8wOdlEVGQNjKZdpuATLQi2pxMHKljiUov0F157BD4=
9-
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
109
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
1110
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
1211
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

main.go

+1
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ func (ctx *DevProxy) newHttpTransport() *httpx.Transport {
189189
}
190190
transport.RegisterProtocol("fastcgi", &fastCGIRoundTripper{Logger: ctx.Logger})
191191
transport.RegisterProtocol("file", NewFileTransport(ctx.Config.FileTransport))
192+
transport.RegisterProtocol("x-http-redirect", &redirector{Logger: ctx.Logger})
192193
return transport
193194
}
194195

redirector.go

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright (c) 2016 Moriyoshi Koizumi
3+
* Copyright (c) 2012 Elazar Leibovich.
4+
* Copyright (c) 2012 The Go Authors.
5+
*
6+
* All rights reserved.
7+
*
8+
* Redistribution and use in source and binary forms, with or without
9+
* modification, are permitted provided that the following conditions are
10+
* met:
11+
*
12+
* * Redistributions of source code must retain the above copyright
13+
* notice, this list of conditions and the following disclaimer.
14+
* * Redistributions in binary form must reproduce the above
15+
* copyright notice, this list of conditions and the following disclaimer
16+
* in the documentation and/or other materials provided with the
17+
* distribution.
18+
* * Neither the name of Elazar Leibovich. nor the names of its
19+
* contributors may be used to endorse or promote products derived from
20+
* this software without specific prior written permission.
21+
*
22+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33+
*/
34+
package main
35+
36+
import (
37+
"bytes"
38+
"fmt"
39+
"io/ioutil"
40+
"net/http"
41+
"strconv"
42+
"strings"
43+
44+
"github.com/sirupsen/logrus"
45+
)
46+
47+
var redirStatusCodes = map[int]string{
48+
301: "Moved Permanently",
49+
302: "Found",
50+
303: "See Other",
51+
307: "Temporary Redirect",
52+
308: "Permanent edirect",
53+
}
54+
55+
type redirector struct {
56+
Logger *logrus.Logger
57+
}
58+
59+
var emptyReadCloser = ioutil.NopCloser(&bytes.Reader{})
60+
61+
func (redir *redirector) RoundTrip(req *http.Request) (*http.Response, error) {
62+
redirStatusCode := 302
63+
statusText := ""
64+
var location string
65+
66+
if req.URL.Host == "" {
67+
components := strings.Split(req.URL.Opaque, ":")
68+
if len(components) < 1 {
69+
return nil, fmt.Errorf("invalid URL: %v", req.URL)
70+
}
71+
if components[0] != "http" && components[0] != "https" {
72+
var err error
73+
redirStatusCode, err = strconv.Atoi(components[0])
74+
if err != nil {
75+
return nil, fmt.Errorf("invalid URL: %v", req.URL)
76+
}
77+
var ok bool
78+
statusText, ok = redirStatusCodes[redirStatusCode]
79+
if !ok {
80+
return nil, fmt.Errorf("invalid status code %d in URL %v", redirStatusCode, req.URL)
81+
}
82+
components = components[1:]
83+
} else {
84+
redirStatusCode = 302
85+
}
86+
if len(components) < 1 {
87+
return nil, fmt.Errorf("invalid URL: %v", req.URL)
88+
}
89+
location = strings.Join(components, ":")
90+
if req.URL.RawQuery != "" {
91+
location += "&" + req.URL.RawQuery
92+
}
93+
if req.URL.Fragment != "" {
94+
location += "#" + req.URL.Fragment
95+
}
96+
} else {
97+
proxyCtx := req.Context().Value(proxyContextKey).(*OurProxyCtx)
98+
redirStatusCode = 302
99+
_location := req.URL
100+
_location.Scheme = proxyCtx.OrigReq.URL.Scheme
101+
location = _location.String()
102+
}
103+
if statusText == "" {
104+
statusText = redirStatusCodes[redirStatusCode]
105+
}
106+
return &http.Response{
107+
Status: fmt.Sprintf("%d %s", redirStatusCode, statusText),
108+
StatusCode: redirStatusCode,
109+
Proto: req.Proto,
110+
ProtoMajor: req.ProtoMajor,
111+
ProtoMinor: req.ProtoMinor,
112+
Header: http.Header{
113+
"Location": []string{location},
114+
},
115+
Trailer: http.Header{},
116+
ContentLength: 0,
117+
Body: emptyReadCloser,
118+
Close: true,
119+
Request: req,
120+
TLS: req.TLS,
121+
}, nil
122+
}

server.go

+17-15
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ type ResponseWriter struct {
8686
protoMajor, protoMinor int
8787
}
8888

89+
var proxyContextKey string = "devproxy:proxyCtx"
90+
8991
func makeHttp10Response(header string, body string) string {
9092
return header + "\r\n" + fmt.Sprintf("Content-Length: %d\r\n", len(body)) + "Connection: close\r\n\r\n" + body
9193
}
@@ -369,6 +371,13 @@ func (proxyCtx *OurProxyCtx) HandleConnect(r *http.Request, proxyClient net.Conn
369371

370372
nestedProxyCtx := new(OurProxyCtx)
371373
*nestedProxyCtx = *proxyCtx
374+
req = req.WithContext(
375+
context.WithValue(
376+
r.Context(),
377+
proxyContextKey,
378+
nestedProxyCtx,
379+
),
380+
)
372381
nestedProxyCtx.OrigReq = req
373382
nestedProxyCtx.Req = req
374383

@@ -453,6 +462,13 @@ func (proxy *OurProxyHttpServer) ServeHTTP(w http.ResponseWriter, r *http.Reques
453462
Tr: proxy.Tr,
454463
ResponseFilters: proxy.ResponseFilters,
455464
}
465+
r = r.WithContext(
466+
context.WithValue(
467+
r.Context(),
468+
proxyContextKey,
469+
proxyCtx,
470+
),
471+
)
456472
if r.Method == "CONNECT" {
457473
hij, ok := w.(http.Hijacker)
458474
if !ok {
@@ -558,20 +574,6 @@ func (proxy *OurProxyHttpServer) ConnectDial(netCtx context.Context, addr string
558574
return conn, err
559575
}
560576

561-
func CloneRequest(r *http.Request) *http.Request {
562-
newRequest := new(http.Request)
563-
*newRequest = *r
564-
newRequest.URL = new(url.URL)
565-
*newRequest.URL = *r.URL
566-
if r.Header != nil {
567-
newRequest.Header = httpx.CloneHeader(r.Header)
568-
}
569-
if r.Trailer != nil {
570-
newRequest.Trailer = httpx.CloneHeader(r.Trailer)
571-
}
572-
return newRequest
573-
}
574-
575577
func FilterRequest(perHostConfig *PerHostConfig, r *http.Request, proxyCtx *OurProxyCtx) (*http.Request, *http.Response) {
576578
newUrlString := ""
577579
headerSets := (http.Header)(nil)
@@ -613,7 +615,7 @@ func FilterRequest(perHostConfig *PerHostConfig, r *http.Request, proxyCtx *OurP
613615
if newUrl.RawQuery == "" {
614616
newUrl.RawQuery = r.URL.RawQuery
615617
}
616-
newRequest := CloneRequest(r)
618+
newRequest := r.Clone(r.Context())
617619
newRequest.URL = newUrl
618620
for headerName, headers := range headerSets {
619621
if headers == nil {

0 commit comments

Comments
 (0)