1package oidcrp
2
3import (
4 "bytes"
5 "fmt"
6 "io"
7 "net/http"
8 "net/url"
9 "strconv"
10)
11
12type inProcess struct {
13 host string
14 h http.Handler
15}
16
17func InProcess(host string, h http.Handler) http.RoundTripper {
18 return inProcess{host: host, h: h}
19}
20
21func (t inProcess) RoundTrip(req *http.Request) (*http.Response, error) {
22 if req.URL.Host != t.host {
23 if req.Body != nil {
24 req.Body.Close()
25 }
26 return nil, fmt.Errorf("oidcrp: %s is not served in process", req.URL.Host)
27 }
28 body := req.Body
29 if body == nil {
30 body = http.NoBody
31 }
32 defer body.Close()
33 sreq := req.Clone(req.Context())
34 sreq.Host = req.URL.Host
35 sreq.URL = &url.URL{Path: req.URL.Path, RawPath: req.URL.RawPath, RawQuery: req.URL.RawQuery}
36 sreq.RequestURI = req.URL.RequestURI()
37 sreq.Proto, sreq.ProtoMajor, sreq.ProtoMinor = "HTTP/1.1", 1, 1
38 sreq.Body = body
39 rec := &recorder{header: http.Header{}}
40 t.h.ServeHTTP(rec, sreq)
41 return rec.response(req), nil
42}
43
44type recorder struct {
45 header http.Header
46 written http.Header
47 status int
48 body bytes.Buffer
49}
50
51func (r *recorder) Header() http.Header { return r.header }
52
53func (r *recorder) WriteHeader(status int) {
54 if r.status != 0 {
55 return
56 }
57 r.status = status
58 r.written = r.header.Clone()
59}
60
61func (r *recorder) Write(b []byte) (int, error) {
62 if r.status == 0 {
63 r.WriteHeader(http.StatusOK)
64 }
65 return r.body.Write(b)
66}
67
68func (r *recorder) response(req *http.Request) *http.Response {
69 if r.status == 0 {
70 r.WriteHeader(http.StatusOK)
71 }
72 h := r.written
73 h.Del("Content-Length")
74 h.Set("Content-Length", strconv.Itoa(r.body.Len()))
75 return &http.Response{
76 Status: strconv.Itoa(r.status) + " " + http.StatusText(r.status),
77 StatusCode: r.status,
78 Proto: "HTTP/1.1",
79 ProtoMajor: 1,
80 ProtoMinor: 1,
81 Header: h,
82 Body: io.NopCloser(bytes.NewReader(r.body.Bytes())),
83 ContentLength: int64(r.body.Len()),
84 Request: req,
85 }
86}