1// Copyright 2017 Istio Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package env
16
17import (
18	"fmt"
19	"io/ioutil"
20	"log"
21	"net"
22	"net/http"
23	"strconv"
24	"sync"
25	"time"
26)
27
28// If HTTP header has non empty FailHeader,
29// HTTP server will fail the request with 400 with FailBody in the response body.
30const (
31	FailHeader = "x-istio-backend-fail"
32	FailBody   = "Bad request from backend."
33)
34
35const publicKey = `
36{
37    "keys": [
38        {
39            "alg": "RS256",
40            "e": "AQAB",
41            "kid": "62a93512c9ee4c7f8067b5a216dade2763d32a47",
42            "kty": "RSA",
43            "n": "` +
44	"0YWnm_eplO9BFtXszMRQNL5UtZ8HJdTH2jK7vjs4XdLkPW7YBkkm_2xNgcaVpkW0VT2l4mU3KftR-6" +
45	"s3Oa5Rnz5BrWEUkCTVVolR7VYksfqIB2I_x5yZHdOiomMTcm3DheUUCgbJRv5OKRnNqszA4xHn3tA3" +
46	"Ry8VO3X7BgKZYAUh9fyZTFLlkeAh0-bLK5zvqCmKW5QgDIXSxUTJxPjZCgfx1vmAfGqaJb-nvmrORX" +
47	"Q6L284c73DUL7mnt6wj3H6tVqPKA27j56N0TB1Hfx4ja6Slr8S4EB3F1luYhATa1PKUSH8mYDW11Ho" +
48	"lzZmTQpRoLV8ZoHbHEaTfqX_aYahIw" +
49	`",
50            "use": "sig"
51        },
52        {
53            "alg": "RS256",
54            "e": "AQAB",
55            "kid": "b3319a147514df7ee5e4bcdee51350cc890cc89e",
56            "kty": "RSA",
57            "n": "` +
58	"qDi7Tx4DhNvPQsl1ofxxc2ePQFcs-L0mXYo6TGS64CY_2WmOtvYlcLNZjhuddZVV2X88m0MfwaSA16w" +
59	"E-RiKM9hqo5EY8BPXj57CMiYAyiHuQPp1yayjMgoE1P2jvp4eqF-BTillGJt5W5RuXti9uqfMtCQdag" +
60	"B8EC3MNRuU_KdeLgBy3lS3oo4LOYd-74kRBVZbk2wnmmb7IhP9OoLc1-7-9qU1uhpDxmE6JwBau0mDS" +
61	"wMnYDS4G_ML17dC-ZDtLd1i24STUw39KH0pcSdfFbL2NtEZdNeam1DDdk0iUtJSPZliUHJBI_pj8M-2" +
62	"Mn_oA8jBuI8YKwBqYkZCN1I95Q" +
63	`",
64            "use": "sig"
65        }
66    ]
67}
68`
69
70// HTTPServer stores data for a HTTP server.
71type HTTPServer struct {
72	port uint16
73	lis  net.Listener
74
75	reqHeaders http.Header
76	mu         sync.Mutex
77}
78
79func pubkeyHandler(w http.ResponseWriter, _ *http.Request) {
80	_, _ = fmt.Fprintf(w, "%v", publicKey)
81}
82
83// handle handles a request and sends response. If ?delay=n is in request URL, then sleeps for
84// n second and sends response.
85func (s *HTTPServer) handle(w http.ResponseWriter, r *http.Request) {
86	body, err := ioutil.ReadAll(r.Body)
87	if err != nil {
88		http.Error(w, err.Error(), http.StatusInternalServerError)
89		return
90	}
91
92	// Fail if there is such header.
93	if r.Header.Get(FailHeader) != "" {
94		w.WriteHeader(http.StatusBadRequest)
95		_, _ = w.Write([]byte(FailBody))
96		return
97	}
98
99	// echo back the Content-Type and Content-Length in the response
100	for _, k := range []string{"Content-Type", "Content-Length"} {
101		if v := r.Header.Get(k); v != "" {
102			w.Header().Set(k, v)
103		}
104	}
105
106	if delay := r.URL.Query().Get("delay"); delay != "" {
107		delaySeconds, err := strconv.ParseInt(delay, 10, 64)
108		if err != nil {
109			w.WriteHeader(http.StatusBadRequest)
110			_, _ = w.Write([]byte("Bad request parameter: delay"))
111			return
112		}
113		time.Sleep(time.Duration(delaySeconds) * time.Second)
114	}
115
116	w.WriteHeader(http.StatusOK)
117
118	reqHeaders := make(http.Header)
119	reqHeaders[":method"] = []string{r.Method}
120	reqHeaders[":authority"] = []string{r.Host}
121	reqHeaders[":path"] = []string{r.URL.String()}
122	for name, headers := range r.Header {
123		reqHeaders[name] = append(reqHeaders[name], headers...)
124	}
125
126	s.mu.Lock()
127	s.reqHeaders = reqHeaders
128	s.mu.Unlock()
129
130	_, _ = w.Write(body)
131}
132
133// NewHTTPServer creates a new HTTP server.
134func NewHTTPServer(port uint16) (*HTTPServer, error) {
135	log.Printf("Http server listening on port %v\n", port)
136	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
137	if err != nil {
138		log.Fatal(err)
139		return nil, err
140	}
141	return &HTTPServer{
142		port: port,
143		lis:  lis,
144	}, nil
145}
146
147// Start starts the server
148func (s *HTTPServer) Start() <-chan error {
149	errCh := make(chan error)
150	go func() {
151		http.HandleFunc("/", s.handle)
152		http.HandleFunc("/pubkey", pubkeyHandler)
153		errCh <- http.Serve(s.lis, nil)
154	}()
155	go func() {
156		url := fmt.Sprintf("http://localhost:%v/echo", s.port)
157		errCh <- WaitForHTTPServer(url)
158	}()
159
160	return errCh
161}
162
163// Stop shutdown the server
164func (s *HTTPServer) Stop() {
165	log.Printf("Close HTTP server\n")
166	_ = s.lis.Close()
167	log.Printf("Close HTTP server -- Done\n")
168}
169
170// LastRequestHeaders returns the headers from the last request and clears the value
171func (s *HTTPServer) LastRequestHeaders() http.Header {
172	s.mu.Lock()
173	out := s.reqHeaders
174	s.reqHeaders = nil
175	s.mu.Unlock()
176
177	return out
178}
179