Finish first working version of reqlog

This commit is contained in:
David Stotijn
2020-09-19 01:27:55 +02:00
parent c5cf6ef867
commit 211c11be2b
10 changed files with 646 additions and 280 deletions

View File

@ -1,12 +1,9 @@
package main package main
import ( import (
"bytes"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"flag" "flag"
"fmt"
"io/ioutil"
"log" "log"
"net" "net"
"net/http" "net/http"
@ -44,51 +41,15 @@ func main() {
log.Fatalf("[FATAL] Could not parse CA: %v", err) log.Fatalf("[FATAL] Could not parse CA: %v", err)
} }
reqLogStore := reqlog.NewRequestLogStore() reqLogService := reqlog.NewService()
p, err := proxy.NewProxy(caCert, tlsCA.PrivateKey) p, err := proxy.NewProxy(caCert, tlsCA.PrivateKey)
if err != nil { if err != nil {
log.Fatalf("[FATAL] Could not create Proxy: %v", err) log.Fatalf("[FATAL] Could not create Proxy: %v", err)
} }
p.UseRequestModifier(func(next proxy.RequestModifyFunc) proxy.RequestModifyFunc { p.UseRequestModifier(reqLogService.RequestModifier)
return func(req *http.Request) { p.UseResponseModifier(reqLogService.ResponseModifier)
next(req)
clone := req.Clone(req.Context())
var body []byte
if req.Body != nil {
// TODO: Use io.LimitReader.
body, err := ioutil.ReadAll(req.Body)
if err != nil {
log.Printf("[ERROR] Could not read request body for logging: %v", err)
return
}
req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
}
reqLogStore.AddRequest(*clone, body)
}
})
p.UseResponseModifier(func(next proxy.ResponseModifyFunc) proxy.ResponseModifyFunc {
return func(res *http.Response) error {
if err := next(res); err != nil {
return err
}
clone := *res
var body []byte
if res.Body != nil {
// TODO: Use io.LimitReader.
var err error
body, err = ioutil.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("could not read response body: %v", err)
}
res.Body = ioutil.NopCloser(bytes.NewBuffer(body))
}
reqLogStore.AddResponse(clone, body)
return nil
}
})
var adminHandler http.Handler var adminHandler http.Handler
@ -116,7 +77,7 @@ func main() {
// GraphQL server. // GraphQL server.
adminRouter.Path("/api/playground").Handler(playground.Handler("GraphQL Playground", "/api/graphql")) adminRouter.Path("/api/playground").Handler(playground.Handler("GraphQL Playground", "/api/graphql"))
adminRouter.Path("/api/graphql").Handler(handler.NewDefaultServer(api.NewExecutableSchema(api.Config{Resolvers: &api.Resolver{ adminRouter.Path("/api/graphql").Handler(handler.NewDefaultServer(api.NewExecutableSchema(api.Config{Resolvers: &api.Resolver{
RequestLogStore: &reqLogStore, RequestLogService: &reqLogService,
}}))) }})))
// Admin interface. // Admin interface.
@ -131,6 +92,7 @@ func main() {
TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){}, // Disable HTTP/2 TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){}, // Disable HTTP/2
} }
log.Println("[INFO] Running server on :8080 ...")
err = s.ListenAndServe() err = s.ListenAndServe()
if err != nil && err != http.ErrServerClosed { if err != nil && err != http.ErrServerClosed {
log.Fatalf("[FATAL] HTTP server closed: %v", err) log.Fatalf("[FATAL] HTTP server closed: %v", err)

6
go.mod
View File

@ -3,8 +3,8 @@ module github.com/dstotijn/gurp
go 1.13 go 1.13
require ( require (
github.com/99designs/gqlgen v0.11.1 github.com/99designs/gqlgen v0.11.3
github.com/gorilla/mux v1.7.3 github.com/google/uuid v1.1.2
github.com/vektah/gqlparser v1.2.0 github.com/gorilla/mux v1.7.4
github.com/vektah/gqlparser/v2 v2.0.1 github.com/vektah/gqlparser/v2 v2.0.1
) )

35
go.sum
View File

@ -1,24 +1,29 @@
github.com/99designs/gqlgen v0.10.2 h1:FfjCqIWejHDJeLpQTI0neoZo5vDO3sdo5oNCucet3A0= github.com/99designs/gqlgen v0.11.3 h1:oFSxl1DFS9X///uHV3y6CEfpcXWrDUxVblR4Xib2bs4=
github.com/99designs/gqlgen v0.10.2/go.mod h1:aDB7oabSAyZ4kUHLEySsLxnWrBy3lA0A2gWKU+qoHwI= github.com/99designs/gqlgen v0.11.3/go.mod h1:RgX5GRRdDWNkh4pBrdzNpNPFVsdoUFY2+adM6nb1N+4=
github.com/99designs/gqlgen v0.11.1 h1:QoSL8/AAJ2T3UOeQbdnBR32JcG4pO08+P/g5jdbFkUg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/99designs/gqlgen v0.11.1/go.mod h1:vjFOyBZ7NwDl+GdSD4PFn7BQn5Fy7ohJwXn7Vk8zz+c=
github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ= github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0= github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0=
github.com/agnivade/levenshtein v1.0.3/go.mod h1:4SFRZbbXWLF4MU1T9Qg0pGgH3Pjs+t6ie5efyrwRJXs= github.com/agnivade/levenshtein v1.0.3/go.mod h1:4SFRZbbXWLF4MU1T9Qg0pGgH3Pjs+t6ie5efyrwRJXs=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c h1:TUuUh0Xgj97tLMNtWtNvI9mIV6isjEb9lBMNv+77IGM=
github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ= github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
@ -43,23 +48,22 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e h1:+w0Zm/9gaWpEAyDlU1eKOuk5twTjAjuevXqcJJw8hrg= github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e h1:+w0Zm/9gaWpEAyDlU1eKOuk5twTjAjuevXqcJJw8hrg=
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U= github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
github.com/vektah/gqlparser v1.2.0 h1:ntkSCX7F5ZJKl+HIVnmLaO269MruasVpNiMOjX9kgo0=
github.com/vektah/gqlparser v1.2.0/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74=
github.com/vektah/gqlparser/v2 v2.0.1 h1:xgl5abVnsd4hkN9rk65OJID9bfcLSMuTaTcZj777q1o= github.com/vektah/gqlparser/v2 v2.0.1 h1:xgl5abVnsd4hkN9rk65OJID9bfcLSMuTaTcZj777q1o=
github.com/vektah/gqlparser/v2 v2.0.1/go.mod h1:SyUiHgLATUR8BiYURfTirrTcGpcE+4XkV2se04Px1Ms= github.com/vektah/gqlparser/v2 v2.0.1/go.mod h1:SyUiHgLATUR8BiYURfTirrTcGpcE+4XkV2se04Px1Ms=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@ -81,8 +85,7 @@ golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589 h1:rjUrONFu4kLchcZTfp3/96b
golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -1,6 +1,6 @@
@cert = $HOME/.ssh/gurp_cert.pem @cert = $HOME/.ssh/gurp_cert.pem
@key = $HOME/.ssh/gurp_key.pem @key = $HOME/.ssh/gurp_key.pem
@dev = false @dev = true
@adminPath = $PWD/admin/build @adminPath = $PWD/admin/build
**/*.go { **/*.go {

View File

@ -42,19 +42,26 @@ type DirectiveRoot struct {
} }
type ComplexityRoot struct { type ComplexityRoot struct {
Query struct { HTTPRequest struct {
GetRequests func(childComplexity int) int Body func(childComplexity int) int
}
Request struct {
Method func(childComplexity int) int Method func(childComplexity int) int
Response func(childComplexity int) int
Timestamp func(childComplexity int) int Timestamp func(childComplexity int) int
URL func(childComplexity int) int URL func(childComplexity int) int
} }
HTTPResponse struct {
Body func(childComplexity int) int
StatusCode func(childComplexity int) int
}
Query struct {
GetHTTPRequests func(childComplexity int) int
}
} }
type QueryResolver interface { type QueryResolver interface {
GetRequests(ctx context.Context) ([]Request, error) GetHTTPRequests(ctx context.Context) ([]HTTPRequest, error)
} }
type executableSchema struct { type executableSchema struct {
@ -72,33 +79,61 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
_ = ec _ = ec
switch typeName + "." + field { switch typeName + "." + field {
case "Query.getRequests": case "HttpRequest.body":
if e.complexity.Query.GetRequests == nil { if e.complexity.HTTPRequest.Body == nil {
break break
} }
return e.complexity.Query.GetRequests(childComplexity), true return e.complexity.HTTPRequest.Body(childComplexity), true
case "Request.method": case "HttpRequest.method":
if e.complexity.Request.Method == nil { if e.complexity.HTTPRequest.Method == nil {
break break
} }
return e.complexity.Request.Method(childComplexity), true return e.complexity.HTTPRequest.Method(childComplexity), true
case "Request.timestamp": case "HttpRequest.response":
if e.complexity.Request.Timestamp == nil { if e.complexity.HTTPRequest.Response == nil {
break break
} }
return e.complexity.Request.Timestamp(childComplexity), true return e.complexity.HTTPRequest.Response(childComplexity), true
case "Request.url": case "HttpRequest.timestamp":
if e.complexity.Request.URL == nil { if e.complexity.HTTPRequest.Timestamp == nil {
break break
} }
return e.complexity.Request.URL(childComplexity), true return e.complexity.HTTPRequest.Timestamp(childComplexity), true
case "HttpRequest.url":
if e.complexity.HTTPRequest.URL == nil {
break
}
return e.complexity.HTTPRequest.URL(childComplexity), true
case "HttpResponse.body":
if e.complexity.HTTPResponse.Body == nil {
break
}
return e.complexity.HTTPResponse.Body(childComplexity), true
case "HttpResponse.statusCode":
if e.complexity.HTTPResponse.StatusCode == nil {
break
}
return e.complexity.HTTPResponse.StatusCode(childComplexity), true
case "Query.getHttpRequests":
if e.complexity.Query.GetHTTPRequests == nil {
break
}
return e.complexity.Query.GetHTTPRequests(childComplexity), true
} }
return 0, false return 0, false
@ -150,23 +185,37 @@ func (ec *executionContext) introspectType(name string) (*introspection.Type, er
} }
var sources = []*ast.Source{ var sources = []*ast.Source{
&ast.Source{Name: "pkg/api/schema.graphql", Input: `type Request { &ast.Source{Name: "pkg/api/schema.graphql", Input: `type HttpRequest {
url: String! url: String!
method: HttpMethod! method: HttpMethod!
body: String
timestamp: Time! timestamp: Time!
response: HttpResponse
} }
type HttpResponse {
statusCode: Int!
body: String
}
type Query { type Query {
getRequests: [Request!]! getHttpRequests: [HttpRequest!]!
} }
enum HttpMethod { enum HttpMethod {
GET GET
HEAD
POST POST
PUT
DELETE
CONNECT
OPTIONS
TRACE
PATCH
} }
scalar Time`, BuiltIn: false}, scalar Time
`, BuiltIn: false},
} }
var parsedSchema = gqlparser.MustLoadSchema(sources...) var parsedSchema = gqlparser.MustLoadSchema(sources...)
@ -224,7 +273,236 @@ func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArg
// region **************************** field.gotpl ***************************** // region **************************** field.gotpl *****************************
func (ec *executionContext) _Query_getRequests(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { func (ec *executionContext) _HttpRequest_url(ctx context.Context, field graphql.CollectedField, obj *HTTPRequest) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "HttpRequest",
Field: field,
Args: nil,
IsMethod: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.URL, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(string)
fc.Result = res
return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _HttpRequest_method(ctx context.Context, field graphql.CollectedField, obj *HTTPRequest) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "HttpRequest",
Field: field,
Args: nil,
IsMethod: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Method, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(HTTPMethod)
fc.Result = res
return ec.marshalNHttpMethod2githubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPMethod(ctx, field.Selections, res)
}
func (ec *executionContext) _HttpRequest_body(ctx context.Context, field graphql.CollectedField, obj *HTTPRequest) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "HttpRequest",
Field: field,
Args: nil,
IsMethod: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Body, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*string)
fc.Result = res
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
}
func (ec *executionContext) _HttpRequest_timestamp(ctx context.Context, field graphql.CollectedField, obj *HTTPRequest) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "HttpRequest",
Field: field,
Args: nil,
IsMethod: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Timestamp, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(time.Time)
fc.Result = res
return ec.marshalNTime2timeᚐTime(ctx, field.Selections, res)
}
func (ec *executionContext) _HttpRequest_response(ctx context.Context, field graphql.CollectedField, obj *HTTPRequest) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "HttpRequest",
Field: field,
Args: nil,
IsMethod: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Response, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*HTTPResponse)
fc.Result = res
return ec.marshalOHttpResponse2ᚖgithubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPResponse(ctx, field.Selections, res)
}
func (ec *executionContext) _HttpResponse_statusCode(ctx context.Context, field graphql.CollectedField, obj *HTTPResponse) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "HttpResponse",
Field: field,
Args: nil,
IsMethod: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.StatusCode, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(int)
fc.Result = res
return ec.marshalNInt2int(ctx, field.Selections, res)
}
func (ec *executionContext) _HttpResponse_body(ctx context.Context, field graphql.CollectedField, obj *HTTPResponse) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "HttpResponse",
Field: field,
Args: nil,
IsMethod: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Body, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*string)
fc.Result = res
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
}
func (ec *executionContext) _Query_getHttpRequests(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -241,7 +519,7 @@ func (ec *executionContext) _Query_getRequests(ctx context.Context, field graphq
ctx = graphql.WithFieldContext(ctx, fc) ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().GetRequests(rctx) return ec.resolvers.Query().GetHTTPRequests(rctx)
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -253,9 +531,9 @@ func (ec *executionContext) _Query_getRequests(ctx context.Context, field graphq
} }
return graphql.Null return graphql.Null
} }
res := resTmp.([]Request) res := resTmp.([]HTTPRequest)
fc.Result = res fc.Result = res
return ec.marshalNRequest2ᚕgithubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐRequestᚄ(ctx, field.Selections, res) return ec.marshalNHttpRequest2ᚕgithubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPRequestᚄ(ctx, field.Selections, res)
} }
func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
@ -327,108 +605,6 @@ func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.C
return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res) return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res)
} }
func (ec *executionContext) _Request_url(ctx context.Context, field graphql.CollectedField, obj *Request) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Request",
Field: field,
Args: nil,
IsMethod: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.URL, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(string)
fc.Result = res
return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _Request_method(ctx context.Context, field graphql.CollectedField, obj *Request) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Request",
Field: field,
Args: nil,
IsMethod: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Method, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(HTTPMethod)
fc.Result = res
return ec.marshalNHttpMethod2githubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPMethod(ctx, field.Selections, res)
}
func (ec *executionContext) _Request_timestamp(ctx context.Context, field graphql.CollectedField, obj *Request) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Request",
Field: field,
Args: nil,
IsMethod: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Timestamp, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(time.Time)
fc.Result = res
return ec.marshalNTime2timeᚐTime(ctx, field.Selections, res)
}
func (ec *executionContext) ___Directive_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { func (ec *executionContext) ___Directive_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -1492,6 +1668,76 @@ func (ec *executionContext) ___Type_ofType(ctx context.Context, field graphql.Co
// region **************************** object.gotpl **************************** // region **************************** object.gotpl ****************************
var httpRequestImplementors = []string{"HttpRequest"}
func (ec *executionContext) _HttpRequest(ctx context.Context, sel ast.SelectionSet, obj *HTTPRequest) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, httpRequestImplementors)
out := graphql.NewFieldSet(fields)
var invalids uint32
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("HttpRequest")
case "url":
out.Values[i] = ec._HttpRequest_url(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "method":
out.Values[i] = ec._HttpRequest_method(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "body":
out.Values[i] = ec._HttpRequest_body(ctx, field, obj)
case "timestamp":
out.Values[i] = ec._HttpRequest_timestamp(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "response":
out.Values[i] = ec._HttpRequest_response(ctx, field, obj)
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch()
if invalids > 0 {
return graphql.Null
}
return out
}
var httpResponseImplementors = []string{"HttpResponse"}
func (ec *executionContext) _HttpResponse(ctx context.Context, sel ast.SelectionSet, obj *HTTPResponse) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, httpResponseImplementors)
out := graphql.NewFieldSet(fields)
var invalids uint32
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("HttpResponse")
case "statusCode":
out.Values[i] = ec._HttpResponse_statusCode(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "body":
out.Values[i] = ec._HttpResponse_body(ctx, field, obj)
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch()
if invalids > 0 {
return graphql.Null
}
return out
}
var queryImplementors = []string{"Query"} var queryImplementors = []string{"Query"}
func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler { func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler {
@ -1507,7 +1753,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
switch field.Name { switch field.Name {
case "__typename": case "__typename":
out.Values[i] = graphql.MarshalString("Query") out.Values[i] = graphql.MarshalString("Query")
case "getRequests": case "getHttpRequests":
field := field field := field
out.Concurrently(i, func() (res graphql.Marshaler) { out.Concurrently(i, func() (res graphql.Marshaler) {
defer func() { defer func() {
@ -1515,7 +1761,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
} }
}() }()
res = ec._Query_getRequests(ctx, field) res = ec._Query_getHttpRequests(ctx, field)
if res == graphql.Null { if res == graphql.Null {
atomic.AddUint32(&invalids, 1) atomic.AddUint32(&invalids, 1)
} }
@ -1536,43 +1782,6 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
return out return out
} }
var requestImplementors = []string{"Request"}
func (ec *executionContext) _Request(ctx context.Context, sel ast.SelectionSet, obj *Request) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, requestImplementors)
out := graphql.NewFieldSet(fields)
var invalids uint32
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("Request")
case "url":
out.Values[i] = ec._Request_url(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "method":
out.Values[i] = ec._Request_method(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "timestamp":
out.Values[i] = ec._Request_timestamp(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch()
if invalids > 0 {
return graphql.Null
}
return out
}
var __DirectiveImplementors = []string{"__Directive"} var __DirectiveImplementors = []string{"__Directive"}
func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionSet, obj *introspection.Directive) graphql.Marshaler { func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionSet, obj *introspection.Directive) graphql.Marshaler {
@ -1841,11 +2050,11 @@ func (ec *executionContext) marshalNHttpMethod2githubᚗcomᚋdstotijnᚋgurpᚋ
return v return v
} }
func (ec *executionContext) marshalNRequest2githubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐRequest(ctx context.Context, sel ast.SelectionSet, v Request) graphql.Marshaler { func (ec *executionContext) marshalNHttpRequest2githubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPRequest(ctx context.Context, sel ast.SelectionSet, v HTTPRequest) graphql.Marshaler {
return ec._Request(ctx, sel, &v) return ec._HttpRequest(ctx, sel, &v)
} }
func (ec *executionContext) marshalNRequest2ᚕgithubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐRequestᚄ(ctx context.Context, sel ast.SelectionSet, v []Request) graphql.Marshaler { func (ec *executionContext) marshalNHttpRequest2ᚕgithubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPRequestᚄ(ctx context.Context, sel ast.SelectionSet, v []HTTPRequest) graphql.Marshaler {
ret := make(graphql.Array, len(v)) ret := make(graphql.Array, len(v))
var wg sync.WaitGroup var wg sync.WaitGroup
isLen1 := len(v) == 1 isLen1 := len(v) == 1
@ -1869,7 +2078,7 @@ func (ec *executionContext) marshalNRequest2ᚕgithubᚗcomᚋdstotijnᚋgurpᚋ
if !isLen1 { if !isLen1 {
defer wg.Done() defer wg.Done()
} }
ret[i] = ec.marshalNRequest2githubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐRequest(ctx, sel, v[i]) ret[i] = ec.marshalNHttpRequest2githubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPRequest(ctx, sel, v[i])
} }
if isLen1 { if isLen1 {
f(i) f(i)
@ -1882,6 +2091,20 @@ func (ec *executionContext) marshalNRequest2ᚕgithubᚗcomᚋdstotijnᚋgurpᚋ
return ret return ret
} }
func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v interface{}) (int, error) {
return graphql.UnmarshalInt(v)
}
func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.SelectionSet, v int) graphql.Marshaler {
res := graphql.MarshalInt(v)
if res == graphql.Null {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "must not be null")
}
}
return res
}
func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) { func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) {
return graphql.UnmarshalString(v) return graphql.UnmarshalString(v)
} }
@ -2159,6 +2382,17 @@ func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast
return ec.marshalOBoolean2bool(ctx, sel, *v) return ec.marshalOBoolean2bool(ctx, sel, *v)
} }
func (ec *executionContext) marshalOHttpResponse2githubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPResponse(ctx context.Context, sel ast.SelectionSet, v HTTPResponse) graphql.Marshaler {
return ec._HttpResponse(ctx, sel, &v)
}
func (ec *executionContext) marshalOHttpResponse2ᚖgithubᚗcomᚋdstotijnᚋgurpᚋpkgᚋapiᚐHTTPResponse(ctx context.Context, sel ast.SelectionSet, v *HTTPResponse) graphql.Marshaler {
if v == nil {
return graphql.Null
}
return ec._HttpResponse(ctx, sel, v)
}
func (ec *executionContext) unmarshalOString2string(ctx context.Context, v interface{}) (string, error) { func (ec *executionContext) unmarshalOString2string(ctx context.Context, v interface{}) (string, error) {
return graphql.UnmarshalString(v) return graphql.UnmarshalString(v)
} }

View File

@ -9,27 +9,48 @@ import (
"time" "time"
) )
type Request struct { type HTTPRequest struct {
URL string `json:"url"` URL string `json:"url"`
Method HTTPMethod `json:"method"` Method HTTPMethod `json:"method"`
Body *string `json:"body"`
Timestamp time.Time `json:"timestamp"` Timestamp time.Time `json:"timestamp"`
Response *HTTPResponse `json:"response"`
}
type HTTPResponse struct {
StatusCode int `json:"statusCode"`
Body *string `json:"body"`
} }
type HTTPMethod string type HTTPMethod string
const ( const (
HTTPMethodGet HTTPMethod = "GET" HTTPMethodGet HTTPMethod = "GET"
HTTPMethodHead HTTPMethod = "HEAD"
HTTPMethodPost HTTPMethod = "POST" HTTPMethodPost HTTPMethod = "POST"
HTTPMethodPut HTTPMethod = "PUT"
HTTPMethodDelete HTTPMethod = "DELETE"
HTTPMethodConnect HTTPMethod = "CONNECT"
HTTPMethodOptions HTTPMethod = "OPTIONS"
HTTPMethodTrace HTTPMethod = "TRACE"
HTTPMethodPatch HTTPMethod = "PATCH"
) )
var AllHTTPMethod = []HTTPMethod{ var AllHTTPMethod = []HTTPMethod{
HTTPMethodGet, HTTPMethodGet,
HTTPMethodHead,
HTTPMethodPost, HTTPMethodPost,
HTTPMethodPut,
HTTPMethodDelete,
HTTPMethodConnect,
HTTPMethodOptions,
HTTPMethodTrace,
HTTPMethodPatch,
} }
func (e HTTPMethod) IsValid() bool { func (e HTTPMethod) IsValid() bool {
switch e { switch e {
case HTTPMethodGet, HTTPMethodPost: case HTTPMethodGet, HTTPMethodHead, HTTPMethodPost, HTTPMethodPut, HTTPMethodDelete, HTTPMethodConnect, HTTPMethodOptions, HTTPMethodTrace, HTTPMethodPatch:
return true return true
} }
return false return false

View File

@ -1,5 +1,7 @@
package api package api
//go:generate go run github.com/99designs/gqlgen
import ( import (
"context" "context"
"fmt" "fmt"
@ -8,27 +10,44 @@ import (
) )
type Resolver struct { type Resolver struct {
RequestLogStore *reqlog.RequestLogStore RequestLogService *reqlog.Service
} }
type queryResolver struct{ *Resolver } type queryResolver struct{ *Resolver }
func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
func (r *queryResolver) GetRequests(ctx context.Context) ([]Request, error) { func (r *queryResolver) GetHTTPRequests(ctx context.Context) ([]HTTPRequest, error) {
reqs := r.RequestLogStore.Requests() logs := r.RequestLogService.Requests()
resp := make([]Request, len(reqs)) reqs := make([]HTTPRequest, len(logs))
for i := range resp { for i, log := range logs {
method := HTTPMethod(reqs[i].Request.Method) method := HTTPMethod(log.Request.Method)
if !method.IsValid() { if !method.IsValid() {
return nil, fmt.Errorf("request has invalid method: %v", method) return nil, fmt.Errorf("request has invalid method: %v", method)
} }
resp[i] = Request{
URL: reqs[i].Request.URL.String(), reqs[i] = HTTPRequest{
URL: log.Request.URL.String(),
Method: method, Method: method,
Timestamp: log.Timestamp,
}
if len(log.Body) > 0 {
reqBody := string(log.Body)
reqs[i].Body = &reqBody
}
if log.Response != nil {
reqs[i].Response = &HTTPResponse{
StatusCode: log.Response.Response.StatusCode,
}
if len(log.Response.Body) > 0 {
resBody := string(log.Response.Body)
reqs[i].Response.Body = &resBody
}
} }
} }
return resp, nil return reqs, nil
} }

View File

@ -1,17 +1,30 @@
type Request { type HttpRequest {
url: String! url: String!
method: HttpMethod! method: HttpMethod!
body: String
timestamp: Time! timestamp: Time!
response: HttpResponse
} }
type HttpResponse {
statusCode: Int!
body: String
}
type Query { type Query {
getRequests: [Request!]! getHttpRequests: [HttpRequest!]!
} }
enum HttpMethod { enum HttpMethod {
GET GET
HEAD
POST POST
PUT
DELETE
CONNECT
OPTIONS
TRACE
PATCH
} }
scalar Time scalar Time

View File

@ -10,8 +10,14 @@ import (
"net" "net"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
"github.com/google/uuid"
) )
type contextKey int
const ReqIDKey contextKey = 0
// Proxy implements http.Handler and offers MITM behaviour for modifying // Proxy implements http.Handler and offers MITM behaviour for modifying
// HTTP requests and responses. // HTTP requests and responses.
type Proxy struct { type Proxy struct {
@ -46,6 +52,11 @@ func NewProxy(ca *x509.Certificate, key crypto.PrivateKey) (*Proxy, error) {
} }
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Add a unique request ID, to be used for correlating responses to requests.
reqID := uuid.New()
ctx := context.WithValue(r.Context(), ReqIDKey, reqID)
r = r.WithContext(ctx)
if r.Method == http.MethodConnect { if r.Method == http.MethodConnect {
p.handleConnect(w, r) p.handleConnect(w, r)
return return
@ -69,6 +80,10 @@ func (p *Proxy) modifyRequest(r *http.Request) {
r.URL.Scheme = "https" r.URL.Scheme = "https"
} }
// Setting `X-Forwarded-For` to `nil` ensures that http.ReverseProxy doesn't
// set this header.
r.Header["X-Forwarded-For"] = nil
fn := nopReqModifier fn := nopReqModifier
for i := len(p.reqModifiers) - 1; i >= 0; i-- { for i := len(p.reqModifiers) - 1; i >= 0; i-- {
@ -119,7 +134,7 @@ func (p *Proxy) handleConnect(w http.ResponseWriter, r *http.Request) {
l := &OnceAcceptListener{clientConnNotify.Conn} l := &OnceAcceptListener{clientConnNotify.Conn}
err = http.Serve(l, p.handler) err = http.Serve(l, p)
if err != nil && err != ErrAlreadyAccepted { if err != nil && err != ErrAlreadyAccepted {
log.Printf("[ERROR] Serving HTTP request failed: %v", err) log.Printf("[ERROR] Serving HTTP request failed: %v", err)
} }

View File

@ -1,51 +1,150 @@
package reqlog package reqlog
import ( import (
"bytes"
"compress/gzip"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http" "net/http"
"sync" "sync"
"time"
"github.com/dstotijn/gurp/pkg/proxy"
"github.com/google/uuid"
) )
type Request struct { type Request struct {
ID uuid.UUID
Request http.Request Request http.Request
Body []byte Body []byte
Timestamp time.Time
Response *Response
} }
type response struct { type Response struct {
res http.Response Response http.Response
body []byte Body []byte
Timestamp time.Time
} }
type RequestLogStore struct { type Service struct {
reqStore []Request store []Request
resStore []response mu sync.Mutex
reqMu sync.Mutex
resMu sync.Mutex
} }
func NewRequestLogStore() RequestLogStore { func NewService() Service {
return RequestLogStore{ return Service{
reqStore: make([]Request, 0), store: make([]Request, 0),
resStore: make([]response, 0),
} }
} }
func (store *RequestLogStore) AddRequest(req http.Request, body []byte) { func (svc *Service) Requests() []Request {
store.reqMu.Lock() // TODO(?): Is locking necessary here?
defer store.reqMu.Unlock() svc.mu.Lock()
defer svc.mu.Unlock()
store.reqStore = append(store.reqStore, Request{req, body}) return svc.store
} }
func (store *RequestLogStore) Requests() []Request { func (svc *Service) addRequest(reqID uuid.UUID, req http.Request, body []byte) Request {
store.reqMu.Lock() svc.mu.Lock()
defer store.reqMu.Unlock() defer svc.mu.Unlock()
return store.reqStore reqLog := Request{
ID: reqID,
Request: req,
Body: body,
Timestamp: time.Now(),
}
svc.store = append(svc.store, reqLog)
return reqLog
} }
func (store *RequestLogStore) AddResponse(res http.Response, body []byte) { func (svc *Service) addResponse(reqID uuid.UUID, res http.Response, body []byte) error {
store.resMu.Lock() svc.mu.Lock()
defer store.resMu.Unlock() defer svc.mu.Unlock()
store.resStore = append(store.resStore, response{res, body}) for i := range svc.store {
if svc.store[i].ID == reqID {
svc.store[i].Response = &Response{
Response: res,
Body: body,
Timestamp: time.Now(),
}
return nil
}
}
return fmt.Errorf("no request found with ID: %s", reqID)
}
func (svc *Service) RequestModifier(next proxy.RequestModifyFunc) proxy.RequestModifyFunc {
return func(req *http.Request) {
next(req)
clone := req.Clone(req.Context())
var body []byte
if req.Body != nil {
// TODO: Use io.LimitReader.
var err error
body, err = ioutil.ReadAll(req.Body)
if err != nil {
log.Printf("[ERROR] Could not read request body for logging: %v", err)
return
}
req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
}
reqID, _ := req.Context().Value(proxy.ReqIDKey).(uuid.UUID)
if reqID == uuid.Nil {
log.Println("[ERROR] Request is missing a related request ID")
return
}
_ = svc.addRequest(reqID, *clone, body)
}
}
func (svc *Service) ResponseModifier(next proxy.ResponseModifyFunc) proxy.ResponseModifyFunc {
return func(res *http.Response) error {
if err := next(res); err != nil {
return err
}
clone := *res
// TODO: Use io.LimitReader.
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("reqlog: could not read response body: %v", err)
}
res.Body = ioutil.NopCloser(bytes.NewBuffer(body))
if res.Header.Get("Content-Encoding") == "gzip" {
gzipReader, err := gzip.NewReader(bytes.NewBuffer(body))
if err != nil {
return fmt.Errorf("reqlog: could not create gzip reader: %v", err)
}
defer gzipReader.Close()
body, err = ioutil.ReadAll(gzipReader)
if err != nil {
return fmt.Errorf("reqlog: could not read gzipped response body: %v", err)
}
}
reqID, _ := res.Request.Context().Value(proxy.ReqIDKey).(uuid.UUID)
if reqID == uuid.Nil {
return errors.New("reqlog: request is missing ID")
}
if err := svc.addResponse(reqID, clone, body); err != nil {
return fmt.Errorf("reqlog: could not add response: %v", err)
}
return nil
}
} }