2023-06-25 20:20:59 -06:00
package graphvent
import (
2023-07-19 14:45:05 -06:00
"time"
"net"
2023-06-25 20:20:59 -06:00
"net/http"
"github.com/graphql-go/graphql"
"github.com/graphql-go/graphql/language/parser"
"github.com/graphql-go/graphql/language/source"
"github.com/graphql-go/graphql/language/ast"
"context"
"encoding/json"
2023-08-11 13:01:32 -06:00
"encoding/base64"
2023-06-25 20:20:59 -06:00
"io"
"reflect"
"fmt"
"sync"
"github.com/gobwas/ws"
"github.com/gobwas/ws/wsutil"
2023-07-13 18:21:33 -06:00
"strings"
2023-08-07 20:26:02 -06:00
"crypto/ecdsa"
"crypto/elliptic"
2023-07-19 14:45:05 -06:00
"crypto/ecdh"
2023-08-06 12:47:47 -06:00
"crypto/ed25519"
2023-07-19 14:45:05 -06:00
"crypto/rand"
"crypto/x509"
2023-07-21 01:05:24 -06:00
"crypto/tls"
2023-07-21 01:21:53 -06:00
"crypto/x509/pkix"
"math/big"
"encoding/pem"
2023-07-29 16:00:01 -06:00
"github.com/google/uuid"
2023-06-25 20:20:59 -06:00
)
func GraphiQLHandler ( ) func ( http . ResponseWriter , * http . Request ) {
return func ( w http . ResponseWriter , r * http . Request ) {
graphiql_string := fmt . Sprintf ( `
< ! --
* Copyright ( c ) 2021 GraphQL Contributors
* All rights reserved .
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree .
-- >
< ! DOCTYPE html >
< html lang = "en" >
< head >
< title > GraphiQL < / title >
< style >
body {
height : 100 % % ;
margin : 0 ;
width : 100 % % ;
overflow : hidden ;
}
# graphiql {
height : 100 vh ;
}
< / style >
< ! --
This GraphiQL example depends on Promise and fetch , which are available in
modern browsers , but can be "polyfilled" for older browsers .
GraphiQL itself depends on React DOM .
If you do not want to rely on a CDN , you can host these files locally or
include them directly in your favored resource bundler .
-- >
< script
crossorigin
src = "https://unpkg.com/react@18/umd/react.development.js"
> < / script >
< script
crossorigin
src = "https://unpkg.com/react-dom@18/umd/react-dom.development.js"
> < / script >
< ! --
These two files can be found in the npm module , however you may wish to
copy them directly into your environment , or perhaps include them in your
favored resource bundler .
-- >
< link rel = "stylesheet" href = "https://unpkg.com/graphiql/graphiql.min.css" / >
< / head >
< body >
< div id = "graphiql" > Loading ... < / div >
< script
src = "https://unpkg.com/graphiql/graphiql.min.js"
type = "application/javascript"
> < / script >
< script >
const root = ReactDOM . createRoot ( document . getElementById ( ' graphiql ' ) ) ;
root . render (
React . createElement ( GraphiQL , {
fetcher : GraphiQL . createFetcher ( {
url : ' / gql ' ,
} ) ,
defaultEditorToolsVisibility : true ,
} ) ,
) ;
< / script >
< / body >
< / html >
` )
w . Header ( ) . Set ( "Content-Type" , "text/html; charset=utf-8" )
w . WriteHeader ( http . StatusOK )
io . WriteString ( w , graphiql_string )
}
}
2023-07-20 00:24:22 -06:00
type GQLPayload struct {
2023-06-25 20:20:59 -06:00
OperationName string ` json:"operationName,omitempty" `
Query string ` json:"query,omitempty" `
Variables map [ string ] interface { } ` json:"variables,omitempty" `
Extensions map [ string ] interface { } ` json:"extensions,omitempty" `
Data string ` json:"data,omitempty" `
}
type GQLWSMsg struct {
ID string ` json:"id,omitempty" `
Type string ` json:"type" `
2023-07-20 00:24:22 -06:00
Payload GQLPayload ` json:"payload,omitempty" `
2023-06-25 20:20:59 -06:00
}
func enableCORS ( w * http . ResponseWriter ) {
( * w ) . Header ( ) . Set ( "Access-Control-Allow-Origin" , "*" )
( * w ) . Header ( ) . Set ( "Access-Control-Allow-Credentials" , "true" )
( * w ) . Header ( ) . Set ( "Access-Control-Allow-Headers" , "*" )
( * w ) . Header ( ) . Set ( "Access-Control-Allow-Methods" , "*" )
}
2023-07-13 18:21:33 -06:00
type GQLUnauthorized string
func ( e GQLUnauthorized ) Is ( target error ) bool {
error_type := reflect . TypeOf ( GQLUnauthorized ( "" ) )
target_type := reflect . TypeOf ( target )
return error_type == target_type
}
func ( e GQLUnauthorized ) Error ( ) string {
return fmt . Sprintf ( "GQL_UNAUTHORIZED_ERROR: %s" , string ( e ) )
}
2023-07-13 18:28:02 -06:00
func ( e GQLUnauthorized ) MarshalJSON ( ) ( [ ] byte , error ) {
return json . MarshalIndent ( & struct {
Error string ` json:"error" `
} {
Error : string ( e ) ,
} , "" , " " )
}
2023-07-13 18:21:33 -06:00
func checkForAuthHeader ( header http . Header ) ( string , bool ) {
auths , ok := header [ "Authorization" ]
if ok == false {
return "" , false
}
for _ , auth := range ( auths ) {
parts := strings . SplitN ( auth , " " , 2 )
if len ( parts ) != 2 {
continue
}
if parts [ 0 ] == "TM" {
return parts [ 1 ] , true
}
}
return "" , false
}
2023-07-29 16:00:01 -06:00
// Context passed to each resolve execution
2023-07-21 18:51:42 -06:00
type ResolveContext struct {
2023-07-31 20:53:56 -06:00
// Channels for the gql extension to route data to this context
Chans map [ uuid . UUID ] chan Signal
2023-07-29 16:00:01 -06:00
// Graph Context this resolver is running under
2023-07-21 18:51:42 -06:00
Context * Context
2023-07-29 16:00:01 -06:00
// GQL Extension context this resolver is running under
2023-07-26 00:18:11 -06:00
GQLContext * GQLExtContext
2023-07-29 16:00:01 -06:00
// Pointer to the node that's currently processing this request
2023-07-26 00:18:11 -06:00
Server * Node
2023-07-29 16:00:01 -06:00
// The state data for the node processing this request
2023-07-26 00:18:11 -06:00
Ext * GQLExt
2023-07-29 16:00:01 -06:00
// ID of the user that made this request
2023-07-29 00:28:44 -06:00
User NodeID
2023-08-01 14:09:29 -06:00
// Key for the user that made this request, to sign resolver requests
// TODO: figure out some way to use a generated key so that the server can't impersonate the user afterwards
2023-08-06 12:47:47 -06:00
Key ed25519 . PrivateKey
2023-07-21 18:51:42 -06:00
}
2023-07-26 00:18:11 -06:00
func NewResolveContext ( ctx * Context , server * Node , gql_ext * GQLExt , r * http . Request ) ( * ResolveContext , error ) {
2023-08-11 13:01:32 -06:00
id_b64 , key_b64 , ok := r . BasicAuth ( )
2023-07-20 00:24:22 -06:00
if ok == false {
return nil , fmt . Errorf ( "GQL_REQUEST_ERR: no auth header included in request header" )
}
2023-08-11 13:01:32 -06:00
id_bytes , err := base64 . StdEncoding . DecodeString ( id_b64 )
if err != nil {
return nil , fmt . Errorf ( "GQL_REQUEST_ERR: failed to parse ID bytes from auth username: %+v" , id_b64 )
}
2023-08-07 20:26:02 -06:00
auth_uuid , err := uuid . FromBytes ( [ ] byte ( id_bytes ) )
2023-07-20 00:24:22 -06:00
if err != nil {
2023-08-11 13:01:32 -06:00
return nil , fmt . Errorf ( "GQL_REQUEST_ERR: failed to parse ID from id_bytes %+v" , id_bytes )
2023-07-20 00:24:22 -06:00
}
2023-08-31 19:50:32 -06:00
auth_id := NodeID ( auth_uuid )
2023-07-20 00:24:22 -06:00
2023-08-11 13:01:32 -06:00
key_bytes , err := base64 . StdEncoding . DecodeString ( key_b64 )
if err != nil {
return nil , fmt . Errorf ( "GQL_REQUEST_ERR: failed to parse key bytes from auth password: %+v" , key_b64 )
}
2023-08-06 12:47:47 -06:00
key_raw , err := x509 . ParsePKCS8PrivateKey ( [ ] byte ( key_bytes ) )
2023-08-01 14:09:29 -06:00
if err != nil {
return nil , fmt . Errorf ( "GQL_REQUEST_ERR: failed to parse ecdsa key from auth password: %s" , key_bytes )
}
2023-08-06 12:47:47 -06:00
var key ed25519 . PrivateKey
switch k := key_raw . ( type ) {
case ed25519 . PrivateKey :
key = k
default :
return nil , fmt . Errorf ( "GQL_REQUEST_ERR: wrong type for key: %s" , reflect . TypeOf ( key_raw ) )
}
key_id := KeyID ( key . Public ( ) . ( ed25519 . PublicKey ) )
2023-08-01 14:09:29 -06:00
if auth_id != key_id {
return nil , fmt . Errorf ( "GQL_REQUEST_ERR: key_id(%s) != auth_id(%s)" , auth_id , key_id )
}
2023-07-21 18:51:42 -06:00
return & ResolveContext {
2023-07-29 16:00:01 -06:00
Ext : gql_ext ,
2023-07-31 20:53:56 -06:00
Chans : map [ uuid . UUID ] chan Signal { } ,
2023-07-21 18:51:42 -06:00
Context : ctx ,
2023-08-31 19:50:32 -06:00
GQLContext : ctx . Extensions [ GQLExtType ] . Data . ( * GQLExtContext ) ,
2023-07-21 18:51:42 -06:00
Server : server ,
2023-08-01 14:09:29 -06:00
User : key_id ,
Key : key ,
2023-07-21 18:51:42 -06:00
} , nil
2023-07-20 00:24:22 -06:00
}
2023-07-26 00:18:11 -06:00
func GQLHandler ( ctx * Context , server * Node , gql_ext * GQLExt ) func ( http . ResponseWriter , * http . Request ) {
2023-06-25 20:20:59 -06:00
return func ( w http . ResponseWriter , r * http . Request ) {
ctx . Log . Logf ( "gql" , "GQL REQUEST: %s" , r . RemoteAddr )
enableCORS ( & w )
header_map := map [ string ] interface { } { }
for header , value := range ( r . Header ) {
header_map [ header ] = value
}
ctx . Log . Logm ( "gql" , header_map , "REQUEST_HEADERS" )
2023-07-19 21:28:48 -06:00
2023-07-26 00:18:11 -06:00
resolve_context , err := NewResolveContext ( ctx , server , gql_ext , r )
2023-07-19 21:28:48 -06:00
if err != nil {
2023-07-20 00:24:22 -06:00
ctx . Log . Logf ( "gql" , "GQL_AUTH_ERR: %s" , err )
json . NewEncoder ( w ) . Encode ( GQLUnauthorized ( fmt . Sprintf ( "%s" , err ) ) )
2023-07-19 21:28:48 -06:00
return
}
2023-07-21 18:51:42 -06:00
req_ctx := context . Background ( )
2023-07-26 00:18:11 -06:00
req_ctx = context . WithValue ( req_ctx , "resolve" , resolve_context )
2023-06-25 20:20:59 -06:00
str , err := io . ReadAll ( r . Body )
if err != nil {
2023-07-20 00:24:22 -06:00
ctx . Log . Logf ( "gql" , "GQL_READ_ERR: %s" , err )
json . NewEncoder ( w ) . Encode ( fmt . Sprintf ( "%e" , err ) )
2023-06-25 20:20:59 -06:00
return
2023-07-13 18:21:33 -06:00
}
2023-07-20 00:24:22 -06:00
query := GQLPayload { }
2023-06-25 20:20:59 -06:00
json . Unmarshal ( str , & query )
2023-08-31 19:50:32 -06:00
gql_context := ctx . Extensions [ GQLExtType ] . Data . ( * GQLExtContext )
2023-07-26 00:18:11 -06:00
2023-06-25 20:20:59 -06:00
params := graphql . Params {
2023-07-26 00:18:11 -06:00
Schema : gql_context . Schema ,
2023-07-13 18:21:33 -06:00
Context : req_ctx ,
2023-06-25 20:20:59 -06:00
RequestString : query . Query ,
}
if query . OperationName != "" {
params . OperationName = query . OperationName
}
if len ( query . Variables ) > 0 {
params . VariableValues = query . Variables
}
2023-07-29 16:00:01 -06:00
2023-06-25 20:20:59 -06:00
result := graphql . Do ( params )
if len ( result . Errors ) > 0 {
extra_fields := map [ string ] interface { } { }
extra_fields [ "body" ] = string ( str )
extra_fields [ "headers" ] = r . Header
ctx . Log . Logm ( "gql" , extra_fields , "wrong result, unexpected errors: %v" , result . Errors )
}
json . NewEncoder ( w ) . Encode ( result )
}
}
func sendOneResultAndClose ( res * graphql . Result ) chan * graphql . Result {
resultChannel := make ( chan * graphql . Result )
go func ( ) {
resultChannel <- res
close ( resultChannel )
} ( )
return resultChannel
}
func getOperationTypeOfReq ( p graphql . Params ) string {
source := source . NewSource ( & source . Source {
Body : [ ] byte ( p . RequestString ) ,
Name : "GraphQL request" ,
} )
AST , err := parser . Parse ( parser . ParseParams { Source : source } )
if err != nil {
return ""
}
for _ , node := range AST . Definitions {
if operationDef , ok := node . ( * ast . OperationDefinition ) ; ok {
name := ""
if operationDef . Name != nil {
name = operationDef . Name . Value
}
if name == p . OperationName || p . OperationName == "" {
return operationDef . Operation
}
}
}
return ""
}
2023-07-09 19:33:18 -06:00
func GQLWSDo ( ctx * Context , p graphql . Params ) chan * graphql . Result {
2023-06-25 20:20:59 -06:00
operation := getOperationTypeOfReq ( p )
ctx . Log . Logf ( "gqlws" , "GQLWSDO_OPERATION: %s %+v" , operation , p . RequestString )
if operation == ast . OperationTypeSubscription {
return graphql . Subscribe ( p )
}
res := graphql . Do ( p )
return sendOneResultAndClose ( res )
}
2023-07-26 00:18:11 -06:00
func GQLWSHandler ( ctx * Context , server * Node , gql_ext * GQLExt ) func ( http . ResponseWriter , * http . Request ) {
2023-06-25 20:20:59 -06:00
return func ( w http . ResponseWriter , r * http . Request ) {
ctx . Log . Logf ( "gqlws_new" , "HANDLING %s" , r . RemoteAddr )
2023-06-25 21:00:00 -06:00
enableCORS ( & w )
2023-06-25 20:20:59 -06:00
header_map := map [ string ] interface { } { }
for header , value := range ( r . Header ) {
header_map [ header ] = value
}
2023-07-13 18:23:57 -06:00
2023-06-25 20:20:59 -06:00
ctx . Log . Logm ( "gql" , header_map , "REQUEST_HEADERS" )
2023-07-26 00:18:11 -06:00
resolve_context , err := NewResolveContext ( ctx , server , gql_ext , r )
2023-07-19 21:28:48 -06:00
if err != nil {
2023-07-20 00:24:22 -06:00
ctx . Log . Logf ( "gql" , "GQL_AUTH_ERR: %s" , err )
2023-07-19 21:28:48 -06:00
return
}
2023-07-21 18:51:42 -06:00
req_ctx := context . Background ( )
req_ctx = context . WithValue ( req_ctx , "resolve" , resolve_context )
2023-07-13 18:23:57 -06:00
2023-06-25 20:20:59 -06:00
u := ws . HTTPUpgrader {
Protocol : func ( protocol string ) bool {
ctx . Log . Logf ( "gqlws" , "UPGRADE_PROTOCOL: %s" , string ( protocol ) )
2023-06-25 21:00:00 -06:00
if string ( protocol ) == "graphql-transport-ws" || string ( protocol ) == "graphql-ws" {
return true
}
return false
2023-06-25 20:20:59 -06:00
} ,
}
conn , _ , _ , err := u . Upgrade ( r , w )
if err == nil {
defer conn . Close ( )
conn_state := "init"
for {
msg_raw , op , err := wsutil . ReadClientData ( conn )
ctx . Log . Logf ( "gqlws_hb" , "MSG: %s\nOP: 0x%02x\nERR: %+v\n" , string ( msg_raw ) , op , err )
msg := GQLWSMsg { }
json . Unmarshal ( msg_raw , & msg )
if err != nil {
ctx . Log . Logf ( "gqlws" , "WS_CLIENT_ERROR" )
break
}
if msg . Type == "connection_init" {
if conn_state != "init" {
ctx . Log . Logf ( "gqlws" , "WS_CLIENT_ERROR: INIT WHILE IN %s" , conn_state )
break
}
conn_state = "ready"
err = wsutil . WriteServerMessage ( conn , 1 , [ ] byte ( "{\"type\": \"connection_ack\"}" ) )
if err != nil {
ctx . Log . Logf ( "gqlws" , "WS_SERVER_ERROR: FAILED TO SEND connection_ack" )
break
}
} else if msg . Type == "ping" {
ctx . Log . Logf ( "gqlws_hb" , "PING FROM %s" , r . RemoteAddr )
err = wsutil . WriteServerMessage ( conn , 1 , [ ] byte ( "{\"type\": \"pong\"}" ) )
if err != nil {
ctx . Log . Logf ( "gqlws" , "WS_SERVER_ERROR: FAILED TO SEND PONG" )
}
} else if msg . Type == "subscribe" {
ctx . Log . Logf ( "gqlws" , "SUBSCRIBE: %+v" , msg . Payload )
2023-08-31 19:50:32 -06:00
gql_context := ctx . Extensions [ GQLExtType ] . Data . ( * GQLExtContext )
2023-06-25 20:20:59 -06:00
params := graphql . Params {
2023-07-26 00:18:11 -06:00
Schema : gql_context . Schema ,
2023-07-19 21:28:48 -06:00
Context : req_ctx ,
2023-06-25 20:20:59 -06:00
RequestString : msg . Payload . Query ,
}
if msg . Payload . OperationName != "" {
params . OperationName = msg . Payload . OperationName
}
if len ( msg . Payload . Variables ) > 0 {
params . VariableValues = msg . Payload . Variables
}
res_chan := GQLWSDo ( ctx , params )
2023-07-03 19:13:29 -06:00
if res_chan == nil {
ctx . Log . Logf ( "gqlws" , "res_chan is nil" )
} else {
ctx . Log . Logf ( "gqlws" , "res_chan: %+v" , res_chan )
}
2023-06-25 20:20:59 -06:00
go func ( res_chan chan * graphql . Result ) {
for {
next , ok := <- res_chan
if ok == false {
ctx . Log . Logf ( "gqlws" , "response channel was closed" )
return
}
if next == nil {
ctx . Log . Logf ( "gqlws" , "NIL_ON_CHANNEL" )
return
}
if len ( next . Errors ) > 0 {
extra_fields := map [ string ] interface { } { }
extra_fields [ "query" ] = string ( msg . Payload . Query )
ctx . Log . Logm ( "gqlws" , extra_fields , "ERROR: wrong result, unexpected errors: %+v" , next . Errors )
continue
}
ctx . Log . Logf ( "gqlws" , "DATA: %+v" , next . Data )
data , err := json . Marshal ( next . Data )
if err != nil {
ctx . Log . Logf ( "gqlws" , "ERROR: %+v" , err )
continue
}
msg , err := json . Marshal ( GQLWSMsg {
ID : msg . ID ,
Type : "next" ,
2023-07-20 00:24:22 -06:00
Payload : GQLPayload {
2023-06-25 20:20:59 -06:00
Data : string ( data ) ,
} ,
} )
if err != nil {
ctx . Log . Logf ( "gqlws" , "ERROR: %+v" , err )
continue
}
err = wsutil . WriteServerMessage ( conn , 1 , msg )
if err != nil {
ctx . Log . Logf ( "gqlws" , "ERROR: %+v" , err )
continue
}
}
} ( res_chan )
} else {
}
}
return
} else {
panic ( "Failed to upgrade websocket" )
}
}
}
2023-07-29 11:03:41 -06:00
type Interface struct {
2023-07-26 20:26:41 -06:00
Interface * graphql . Interface
Default * graphql . Object
List * graphql . List
Extensions [ ] ExtType
}
2023-07-29 11:03:41 -06:00
type Type struct {
2023-07-26 23:57:50 -06:00
Type * graphql . Object
List * graphql . List
}
2023-07-29 16:00:01 -06:00
type Field struct {
Ext ExtType
Name string
2023-07-29 17:23:25 -06:00
Field * graphql . Field
2023-07-29 00:28:44 -06:00
}
2023-07-26 00:18:11 -06:00
// GQL Specific Context information
type GQLExtContext struct {
// Generated GQL schema
Schema graphql . Schema
2023-07-26 20:26:41 -06:00
// Custom graphql types, mapped to NodeTypes
NodeTypes map [ NodeType ] * graphql . Object
2023-07-29 17:23:25 -06:00
Interfaces map [ string ] * Interface
2023-07-29 16:00:01 -06:00
Fields map [ string ] Field
2023-07-26 00:18:11 -06:00
2023-07-26 20:26:41 -06:00
// Schema parameters
Types [ ] graphql . Type
2023-07-26 00:18:11 -06:00
Query * graphql . Object
Mutation * graphql . Object
Subscription * graphql . Object
}
2023-07-29 16:00:01 -06:00
func ( ctx * GQLExtContext ) GetACLFields ( obj_name string , names [ ] string ) ( map [ ExtType ] [ ] string , error ) {
ext_fields := map [ ExtType ] [ ] string { }
for _ , name := range ( names ) {
switch name {
case "ID" :
case "TypeHash" :
default :
field , exists := ctx . Fields [ name ]
if exists == false {
return nil , fmt . Errorf ( "%s is not a know field in GQLContext, cannot resolve" , name )
}
ext , exists := ext_fields [ field . Ext ]
if exists == false {
ext = [ ] string { }
}
ext = append ( ext , field . Name )
ext_fields [ field . Ext ] = ext
}
}
return ext_fields , nil
}
2023-07-26 20:26:41 -06:00
func BuildSchema ( ctx * GQLExtContext ) ( graphql . Schema , error ) {
schemaConfig := graphql . SchemaConfig {
Types : ctx . Types ,
Query : ctx . Query ,
Mutation : ctx . Mutation ,
Subscription : ctx . Subscription ,
}
return graphql . NewSchema ( schemaConfig )
}
2023-09-13 16:27:55 -06:00
func ( ctx * GQLExtContext ) RegisterField ( gql_type graphql . Type , gql_name string , ext_type ExtType , gv_tag string , resolve_fn func ( graphql . ResolveParams , * ResolveContext , reflect . Value ) ( interface { } , error ) ) error {
2023-07-29 17:23:25 -06:00
if ctx == nil {
return fmt . Errorf ( "ctx is nil" )
}
if resolve_fn == nil {
return fmt . Errorf ( "resolve_fn cannot be nil" )
}
2023-07-29 16:00:01 -06:00
_ , exists := ctx . Fields [ gql_name ]
if exists == true {
return fmt . Errorf ( "%s is already a field in the context, cannot add again" , gql_name )
}
2023-09-13 16:27:55 -06:00
// Resolver has p.Source.(NodeResult) = read result of current node
2023-07-29 22:16:54 -06:00
resolver := func ( p graphql . ResolveParams ) ( interface { } , error ) {
2023-09-13 16:27:55 -06:00
ctx , err := PrepResolve ( p )
if err != nil {
return nil , err
}
2023-07-29 17:23:25 -06:00
2023-09-13 16:27:55 -06:00
node , ok := p . Source . ( NodeResult )
if ok == false {
return nil , fmt . Errorf ( "p.Value is not NodeResult" )
}
2023-07-29 17:23:25 -06:00
2023-09-13 16:27:55 -06:00
ext , ext_exists := node . Result . Extensions [ ext_type ]
if ext_exists == false {
return nil , fmt . Errorf ( "%+v is not in the extensions of the result" , ext_type )
}
2023-07-29 17:23:25 -06:00
2023-09-13 16:27:55 -06:00
val_ser , field_exists := ext [ gv_tag ]
if field_exists == false {
return nil , fmt . Errorf ( "%s is not in the fields of %+v in the result" , gv_tag , ext_type )
}
if val_ser . TypeStack [ 0 ] == ErrorType {
return nil , fmt . Errorf ( string ( val_ser . Data ) )
}
field_type , field_value , _ , err := DeserializeValue ( ctx . Context , val_ser )
if err != nil {
return nil , err
}
if field_value == nil {
return nil , fmt . Errorf ( "%s returned a nil value of %+v type" , gv_tag , field_type )
}
return resolve_fn ( p , ctx , * field_value )
2023-07-29 22:16:54 -06:00
}
2023-09-13 16:27:55 -06:00
ctx . Fields [ gql_name ] = Field { ext_type , gv_tag , & graphql . Field {
2023-07-29 22:16:54 -06:00
Type : gql_type ,
Resolve : resolver ,
2023-07-29 17:23:25 -06:00
} }
2023-07-29 16:00:01 -06:00
return nil
}
2023-07-29 18:27:52 -06:00
func GQLInterfaces ( ctx * GQLExtContext , interface_names [ ] string ) ( [ ] * graphql . Interface , error ) {
ret := make ( [ ] * graphql . Interface , len ( interface_names ) )
for i , in := range ( interface_names ) {
ctx_interface , exists := ctx . Interfaces [ in ]
if exists == false {
return nil , fmt . Errorf ( "%s is not in GQLExtContext.Interfaces" , in )
}
ret [ i ] = ctx_interface . Interface
}
return ret , nil
}
func GQLFields ( ctx * GQLExtContext , field_names [ ] string ) ( graphql . Fields , [ ] ExtType , error ) {
fields := graphql . Fields {
"ID" : & graphql . Field {
Type : graphql . String ,
Resolve : ResolveNodeID ,
} ,
"TypeHash" : & graphql . Field {
Type : graphql . String ,
Resolve : ResolveNodeTypeHash ,
} ,
2023-07-26 20:26:41 -06:00
}
2023-07-29 18:27:52 -06:00
exts := map [ ExtType ] ExtType { }
ext_list := [ ] ExtType { }
for _ , name := range ( field_names ) {
field , exists := ctx . Fields [ name ]
if exists == false {
return nil , nil , fmt . Errorf ( "%s is not in GQLExtContext.Fields" , name )
}
fields [ name ] = field . Field
_ , exists = exts [ field . Ext ]
if exists == false {
ext_list = append ( ext_list , field . Ext )
exts [ field . Ext ] = field . Ext
}
}
return fields , ext_list , nil
}
type NodeResult struct {
ID NodeID
2023-08-01 20:55:15 -06:00
Result * ReadResultSignal
2023-07-29 18:27:52 -06:00
}
type ListField struct {
ACLName string
Extension ExtType
2023-09-13 16:27:55 -06:00
ResolveFn func ( graphql . ResolveParams , * ResolveContext , reflect . Value ) ( [ ] NodeID , error )
2023-07-29 18:27:52 -06:00
}
type SelfField struct {
ACLName string
Extension ExtType
2023-09-13 16:27:55 -06:00
ResolveFn func ( graphql . ResolveParams , * ResolveContext , reflect . Value ) ( * NodeID , error )
2023-07-29 18:27:52 -06:00
}
func ( ctx * GQLExtContext ) RegisterInterface ( name string , default_name string , interfaces [ ] string , fields [ ] string , self_fields map [ string ] SelfField , list_fields map [ string ] ListField ) error {
if interfaces == nil {
return fmt . Errorf ( "interfaces is nil" )
}
if fields == nil {
return fmt . Errorf ( "fields is nil" )
2023-07-26 20:26:41 -06:00
}
2023-07-29 17:23:25 -06:00
_ , exists := ctx . Interfaces [ name ]
if exists == true {
return fmt . Errorf ( "%s is already an interface in ctx" , name )
}
2023-07-29 18:27:52 -06:00
node_interfaces , err := GQLInterfaces ( ctx , interfaces )
if err != nil {
return err
}
node_fields , node_exts , err := GQLFields ( ctx , fields )
if err != nil {
return err
}
ctx_interface := Interface { }
ctx_interface . Interface = graphql . NewInterface ( graphql . InterfaceConfig {
Name : name ,
ResolveType : NodeInterfaceResolveType ( node_exts , & ctx_interface . Default ) ,
Fields : node_fields ,
} )
ctx_interface . List = graphql . NewList ( ctx_interface . Interface )
2023-07-29 22:16:54 -06:00
for field_name , field := range ( self_fields ) {
self_field := field
2023-08-31 19:50:32 -06:00
err := ctx . RegisterField ( ctx_interface . Interface , field_name , self_field . Extension , self_field . ACLName ,
2023-09-13 16:27:55 -06:00
func ( p graphql . ResolveParams , ctx * ResolveContext , value reflect . Value ) ( interface { } , error ) {
id , err := self_field . ResolveFn ( p , ctx , value )
2023-07-29 19:16:33 -06:00
if err != nil {
2023-08-11 13:01:32 -06:00
return nil , err
2023-07-29 19:16:33 -06:00
}
2023-08-11 13:01:32 -06:00
if id != nil {
nodes , err := ResolveNodes ( ctx , p , [ ] NodeID { * id } )
if err != nil {
return nil , err
} else if len ( nodes ) != 1 {
return nil , fmt . Errorf ( "wrong length of nodes returned" )
}
return nodes [ 0 ] , nil
} else {
return nil , nil
2023-07-29 19:16:33 -06:00
}
} )
if err != nil {
return err
}
ctx_interface . Interface . AddFieldConfig ( field_name , ctx . Fields [ field_name ] . Field )
node_fields [ field_name ] = ctx . Fields [ field_name ] . Field
}
2023-07-29 22:16:54 -06:00
for field_name , field := range ( list_fields ) {
list_field := field
2023-09-13 16:27:55 -06:00
resolve_fn := func ( p graphql . ResolveParams , ctx * ResolveContext , value reflect . Value ) ( interface { } , error ) {
2023-07-29 19:16:33 -06:00
var zero NodeID
2023-09-13 16:27:55 -06:00
ids , err := list_field . ResolveFn ( p , ctx , value )
2023-07-29 19:16:33 -06:00
if err != nil {
return zero , err
}
nodes , err := ResolveNodes ( ctx , p , ids )
if err != nil {
return nil , err
} else if len ( nodes ) != len ( ids ) {
return nil , fmt . Errorf ( "wrong length of nodes returned" )
}
return nodes , nil
2023-07-29 22:16:54 -06:00
}
2023-08-31 19:50:32 -06:00
err := ctx . RegisterField ( ctx_interface . List , field_name , list_field . Extension , list_field . ACLName , resolve_fn )
2023-07-29 18:27:52 -06:00
if err != nil {
return err
}
2023-07-29 19:16:33 -06:00
ctx_interface . Interface . AddFieldConfig ( field_name , ctx . Fields [ field_name ] . Field )
node_fields [ field_name ] = ctx . Fields [ field_name ] . Field
2023-07-29 18:27:52 -06:00
}
ctx_interface . Default = graphql . NewObject ( graphql . ObjectConfig {
Name : default_name ,
Interfaces : append ( node_interfaces , ctx_interface . Interface ) ,
IsTypeOf : NodeInterfaceDefaultIsType ( node_exts ) ,
Fields : node_fields ,
} )
ctx . Interfaces [ name ] = & ctx_interface
ctx . Types = append ( ctx . Types , ctx_interface . Default )
2023-07-26 20:26:41 -06:00
return nil
}
2023-07-29 18:27:52 -06:00
func ( ctx * GQLExtContext ) RegisterNodeType ( node_type NodeType , name string , interface_names [ ] string , field_names [ ] string ) error {
2023-07-29 17:23:25 -06:00
if field_names == nil {
return fmt . Errorf ( "fields is nil" )
2023-07-26 20:26:41 -06:00
}
2023-07-29 16:00:01 -06:00
2023-07-26 20:26:41 -06:00
_ , exists := ctx . NodeTypes [ node_type ]
if exists == true {
2023-08-31 19:50:32 -06:00
return fmt . Errorf ( "%+v already in GQLExtContext.NodeTypes" , node_type )
2023-07-26 20:26:41 -06:00
}
2023-07-29 18:27:52 -06:00
node_interfaces , err := GQLInterfaces ( ctx , interface_names )
if err != nil {
return err
2023-07-29 17:23:25 -06:00
}
2023-07-29 18:27:52 -06:00
gql_fields , _ , err := GQLFields ( ctx , field_names )
if err != nil {
return err
2023-07-29 17:23:25 -06:00
}
gql_type := graphql . NewObject ( graphql . ObjectConfig {
Name : name ,
Interfaces : node_interfaces ,
IsTypeOf : func ( p graphql . IsTypeOfParams ) bool {
node , ok := p . Value . ( NodeResult )
if ok == false {
return false
}
return node . Result . NodeType == node_type
} ,
2023-07-29 18:27:52 -06:00
Fields : gql_fields ,
2023-07-29 17:23:25 -06:00
} )
2023-07-26 20:26:41 -06:00
ctx . NodeTypes [ node_type ] = gql_type
ctx . Types = append ( ctx . Types , gql_type )
return nil
}
2023-07-26 00:18:11 -06:00
func NewGQLExtContext ( ) * GQLExtContext {
query := graphql . NewObject ( graphql . ObjectConfig {
Name : "Query" ,
Fields : graphql . Fields { } ,
} )
2023-07-31 17:27:54 -06:00
mutation := graphql . NewObject ( graphql . ObjectConfig {
Name : "Mutation" ,
Fields : graphql . Fields { } ,
} )
2023-08-15 18:23:06 -06:00
subscription := graphql . NewObject ( graphql . ObjectConfig {
Name : "Subscription" ,
Fields : graphql . Fields { } ,
} )
2023-07-26 00:18:11 -06:00
context := GQLExtContext {
Schema : graphql . Schema { } ,
2023-07-26 20:26:41 -06:00
Types : [ ] graphql . Type { } ,
2023-07-26 00:18:11 -06:00
Query : query ,
2023-07-31 17:27:54 -06:00
Mutation : mutation ,
2023-08-15 18:23:06 -06:00
Subscription : subscription ,
2023-07-26 20:26:41 -06:00
NodeTypes : map [ NodeType ] * graphql . Object { } ,
2023-07-29 17:23:25 -06:00
Interfaces : map [ string ] * Interface { } ,
2023-07-29 16:34:21 -06:00
Fields : map [ string ] Field { } ,
2023-07-26 00:18:11 -06:00
}
2023-07-26 20:26:41 -06:00
var err error
2023-07-29 18:27:52 -06:00
err = context . RegisterInterface ( "Node" , "DefaultNode" , [ ] string { } , [ ] string { } , map [ string ] SelfField { } , map [ string ] ListField { } )
2023-07-26 20:26:41 -06:00
if err != nil {
panic ( err )
}
2023-08-31 19:50:32 -06:00
err = context . RegisterField ( context . Interfaces [ "Node" ] . List , "Members" , GroupExtType , "members" ,
2023-09-13 16:27:55 -06:00
func ( p graphql . ResolveParams , ctx * ResolveContext , value reflect . Value ) ( interface { } , error ) {
node_map , ok := value . Interface ( ) . ( map [ NodeID ] string )
if ok == false {
return nil , fmt . Errorf ( "value is %+v, not map[NodeID]string" , value . Type ( ) )
2023-07-29 23:24:48 -06:00
}
2023-09-13 16:27:55 -06:00
node_list := [ ] NodeID { }
2023-07-29 23:24:48 -06:00
i := 0
2023-09-14 15:50:08 -06:00
for id := range ( node_map ) {
2023-09-13 16:27:55 -06:00
node_list = append ( node_list , id )
2023-07-29 23:24:48 -06:00
i += 1
}
2023-09-13 16:27:55 -06:00
2023-07-29 23:24:48 -06:00
nodes , err := ResolveNodes ( ctx , p , node_list )
if err != nil {
return nil , err
}
return nodes , nil
} )
if err != nil {
panic ( err )
}
err = context . RegisterInterface ( "Group" , "DefaultGroup" , [ ] string { "Node" } , [ ] string { "Members" } , map [ string ] SelfField { } , map [ string ] ListField { } )
if err != nil {
panic ( err )
}
2023-07-29 19:16:33 -06:00
err = context . RegisterInterface ( "Lockable" , "DefaultLockable" , [ ] string { "Node" } , [ ] string { } , map [ string ] SelfField {
2023-09-14 15:50:08 -06:00
"Owner" : {
2023-07-29 19:16:33 -06:00
"owner" ,
LockableExtType ,
2023-09-13 16:27:55 -06:00
func ( p graphql . ResolveParams , ctx * ResolveContext , value reflect . Value ) ( * NodeID , error ) {
id , ok := value . Interface ( ) . ( * NodeID )
2023-07-29 19:16:33 -06:00
if ok == false {
2023-09-13 16:27:55 -06:00
return nil , fmt . Errorf ( "can't parse %+v as *NodeID" , value . Type ( ) )
2023-07-29 19:16:33 -06:00
}
return id , nil
} ,
} ,
} , map [ string ] ListField {
2023-09-14 15:50:08 -06:00
"Requirements" : {
2023-07-29 22:16:54 -06:00
"requirements" ,
LockableExtType ,
2023-09-13 16:27:55 -06:00
func ( p graphql . ResolveParams , ctx * ResolveContext , value reflect . Value ) ( [ ] NodeID , error ) {
id_strs , ok := value . Interface ( ) . ( map [ NodeID ] ReqState )
2023-07-29 22:16:54 -06:00
if ok == false {
2023-09-13 16:27:55 -06:00
return nil , fmt . Errorf ( "can't parse requirements %+v as map[NodeID]ReqState" , value . Type ( ) )
2023-07-29 22:16:54 -06:00
}
2023-08-10 23:43:10 -06:00
ids := [ ] NodeID { }
2023-09-13 16:27:55 -06:00
for id := range ( id_strs ) {
2023-08-11 13:01:32 -06:00
ids = append ( ids , id )
2023-07-29 22:16:54 -06:00
}
return ids , nil
} ,
} ,
2023-07-29 19:16:33 -06:00
} )
if err != nil {
panic ( err )
}
2023-09-13 16:27:55 -06:00
err = context . RegisterField ( graphql . String , "Listen" , GQLExtType , "listen" , func ( p graphql . ResolveParams , ctx * ResolveContext , value reflect . Value ) ( interface { } , error ) {
return value . String ( ) , nil
2023-07-29 19:16:33 -06:00
} )
if err != nil {
panic ( err )
}
2023-08-11 13:01:32 -06:00
err = context . RegisterNodeType ( GQLNodeType , "GQLServer" , [ ] string { "Node" , "Lockable" , "Group" } , [ ] string { "Listen" , "Owner" , "Requirements" , "Members" } )
2023-07-29 19:16:33 -06:00
if err != nil {
panic ( err )
}
2023-08-07 20:26:02 -06:00
context . Mutation . AddFieldConfig ( "stop" , & graphql . Field {
2023-07-31 17:27:54 -06:00
Type : graphql . String ,
Resolve : func ( p graphql . ResolveParams ) ( interface { } , error ) {
2023-08-08 14:00:17 -06:00
return nil , fmt . Errorf ( "NOT_IMPLEMENTED" )
2023-07-31 17:27:54 -06:00
} ,
} )
2023-09-14 15:50:08 -06:00
// This library handles subscriptions by running the "Subscribe" function which should return a channel, and a "Resolve" function which returns the new value to send to the client
/ *
Library modifications required :
1 ) Make the last result available in the ResolveContext
2 ) When resolving , check if the current field exists in the previous result , using the previous result instead of re - resolving ( only resolve fields that don ' t exist in the previous result )
To make this work with graphvent :
1 ) In Subscribe :
- Make a channel for the gql extension to forward messages to this context
- Send the read request associated with the root of the subscription
2 ) In Resolve :
- If the incoming message is a read result , process it as the new root result
- If the incoming message is a status , delete all the nodes found from the root that have the same NodeID and re - resolve
* /
2023-08-15 18:23:06 -06:00
context . Subscription . AddFieldConfig ( "Self" , & graphql . Field {
2023-09-14 15:50:08 -06:00
Type : context . NodeTypes [ GQLNodeType ] ,
2023-08-15 18:23:06 -06:00
Subscribe : func ( p graphql . ResolveParams ) ( interface { } , error ) {
2023-09-14 15:50:08 -06:00
ctx , err := PrepResolve ( p )
if err != nil {
return nil , err
}
c := make ( chan interface { } , 1 )
nodes , err := ResolveNodes ( ctx , p , [ ] NodeID { ctx . Server . ID } )
if err != nil {
return nil , err
} else if len ( nodes ) != 1 {
return nil , fmt . Errorf ( "wrong length of nodes returned" )
2023-08-15 18:23:06 -06:00
}
2023-09-14 15:50:08 -06:00
ctx . Context . Log . Logf ( "gql" , "NODES: %+v" , nodes [ 0 ] . Result )
c <- nodes [ 0 ]
2023-08-15 18:23:06 -06:00
return c , nil
} ,
2023-09-14 15:50:08 -06:00
Resolve : func ( p graphql . ResolveParams ) ( interface { } , error ) {
ctx , err := PrepResolve ( p )
if err != nil {
return nil , err
}
var self_result NodeResult
switch source := p . Source . ( type ) {
case NodeResult :
self_result = source
ctx . Context . Log . Logf ( "gql" , "FIRST_RESULT_RECEIVED" )
case StatusSignal :
// Look for Source in tree and delete references
default :
return nil , fmt . Errorf ( "Don't know how to handle %+v" , source )
}
return self_result , nil
} ,
2023-08-15 18:23:06 -06:00
} )
2023-07-31 17:15:52 -06:00
context . Query . AddFieldConfig ( "Self" , & graphql . Field {
Type : context . Interfaces [ "Node" ] . Interface ,
Resolve : func ( p graphql . ResolveParams ) ( interface { } , error ) {
ctx , err := PrepResolve ( p )
if err != nil {
return nil , err
}
nodes , err := ResolveNodes ( ctx , p , [ ] NodeID { ctx . Server . ID } )
if err != nil {
return nil , err
} else if len ( nodes ) != 1 {
return nil , fmt . Errorf ( "wrong length of resolved nodes returned" )
}
return nodes [ 0 ] , nil
} ,
} )
2023-07-29 18:27:52 -06:00
context . Query . AddFieldConfig ( "Node" , & graphql . Field {
Type : context . Interfaces [ "Node" ] . Interface ,
Args : graphql . FieldConfigArgument {
"id" : & graphql . ArgumentConfig {
Type : graphql . String ,
} ,
} ,
Resolve : func ( p graphql . ResolveParams ) ( interface { } , error ) {
ctx , err := PrepResolve ( p )
if err != nil {
return nil , err
}
2023-07-29 19:16:33 -06:00
id , err := ExtractID ( p , "id" )
if err != nil {
return nil , err
}
nodes , err := ResolveNodes ( ctx , p , [ ] NodeID { id } )
2023-07-29 18:27:52 -06:00
if err != nil {
return nil , err
2023-07-29 19:16:33 -06:00
} else if len ( nodes ) != 1 {
return nil , fmt . Errorf ( "wrong length of resolved nodes returned" )
2023-07-29 18:27:52 -06:00
}
2023-07-29 19:16:33 -06:00
return nodes [ 0 ] , nil
2023-07-29 18:27:52 -06:00
} ,
} )
2023-07-26 20:26:41 -06:00
schema , err := BuildSchema ( & context )
if err != nil {
panic ( err )
}
context . Schema = schema
2023-07-26 00:18:11 -06:00
return & context
}
type GQLExt struct {
2023-09-13 16:27:55 -06:00
tcp_listener net . Listener
http_server * http . Server
http_done sync . WaitGroup
2023-07-29 11:03:41 -06:00
2023-07-31 20:53:56 -06:00
// map of read request IDs to response channels
2023-09-13 16:27:55 -06:00
resolver_response map [ uuid . UUID ] chan Signal
resolver_response_lock sync . RWMutex
2023-07-29 16:00:01 -06:00
2023-09-13 16:27:55 -06:00
TLSKey [ ] byte ` gv:"tls_key" `
TLSCert [ ] byte ` gv:"tls_cert" `
Listen string ` gv:"listen" `
2023-07-24 16:04:56 -06:00
}
2023-08-15 18:23:06 -06:00
func ( ext * GQLExt ) FindResponseChannel ( req_id uuid . UUID ) chan Signal {
ext . resolver_response_lock . RLock ( )
response_chan , _ := ext . resolver_response [ req_id ]
ext . resolver_response_lock . RUnlock ( )
return response_chan
}
2023-07-31 21:03:48 -06:00
func ( ext * GQLExt ) GetResponseChannel ( req_id uuid . UUID ) chan Signal {
response_chan := make ( chan Signal , 1 )
ext . resolver_response_lock . Lock ( )
ext . resolver_response [ req_id ] = response_chan
ext . resolver_response_lock . Unlock ( )
return response_chan
}
func ( ext * GQLExt ) FreeResponseChannel ( req_id uuid . UUID ) chan Signal {
2023-08-15 18:23:06 -06:00
response_chan := ext . FindResponseChannel ( req_id )
2023-07-31 21:03:48 -06:00
2023-08-15 18:23:06 -06:00
if response_chan != nil {
2023-07-31 21:03:48 -06:00
ext . resolver_response_lock . Lock ( )
delete ( ext . resolver_response , req_id )
ext . resolver_response_lock . Unlock ( )
}
2023-08-15 18:23:06 -06:00
return response_chan
2023-07-31 21:03:48 -06:00
}
2023-08-08 14:00:17 -06:00
func ( ext * GQLExt ) Process ( ctx * Context , node * Node , source NodeID , signal Signal ) Messages {
2023-07-29 16:00:01 -06:00
// Process ReadResultSignalType by forwarding it to the waiting resolver
2023-08-31 19:50:32 -06:00
switch sig := signal . ( type ) {
case * ErrorSignal :
2023-07-31 18:29:26 -06:00
// TODO: Forward to resolver if waiting for it
2023-08-31 19:50:32 -06:00
response_chan := ext . FreeResponseChannel ( sig . Header ( ) . ReqID )
2023-07-31 21:03:48 -06:00
if response_chan != nil {
2023-07-31 20:53:56 -06:00
select {
2023-07-31 21:03:48 -06:00
case response_chan <- sig :
2023-07-31 20:53:56 -06:00
ctx . Log . Logf ( "gql" , "Forwarded error to resolver, %+v" , sig )
default :
ctx . Log . Logf ( "gql" , "Resolver channel overflow %+v" , sig )
2023-07-31 18:29:26 -06:00
}
} else {
2023-08-01 14:09:29 -06:00
ctx . Log . Logf ( "gql" , "received error signal response %+v with no mapped resolver" , sig )
2023-07-31 18:29:26 -06:00
}
2023-08-31 19:50:32 -06:00
case * ReadResultSignal :
response_chan := ext . FindResponseChannel ( sig . ReqID )
2023-07-31 21:03:48 -06:00
if response_chan != nil {
2023-07-31 20:53:56 -06:00
select {
2023-07-31 21:03:48 -06:00
case response_chan <- sig :
2023-07-31 20:53:56 -06:00
ctx . Log . Logf ( "gql" , "Forwarded to resolver, %+v" , sig )
default :
ctx . Log . Logf ( "gql" , "Resolver channel overflow %+v" , sig )
2023-07-29 16:00:01 -06:00
}
} else {
ctx . Log . Logf ( "gql" , "Received read result that wasn't expected - %+v" , sig )
}
2023-08-31 19:50:32 -06:00
case * StartSignal :
2023-08-15 18:23:06 -06:00
ctx . Log . Logf ( "gql" , "starting gql server %s" , node . ID )
err := ext . StartGQLServer ( ctx , node )
if err == nil {
2023-08-31 19:50:32 -06:00
node . QueueSignal ( time . Now ( ) , NewStatusSignal ( node . ID , "server_started" ) )
2023-08-15 18:23:06 -06:00
} else {
ctx . Log . Logf ( "gql" , "GQL_RESTART_ERROR: %s" , err )
2023-07-31 16:25:18 -06:00
}
2023-07-24 16:04:56 -06:00
}
2023-08-31 19:50:32 -06:00
return nil
2023-06-26 21:20:04 -06:00
}
2023-07-26 00:18:11 -06:00
func ( ext * GQLExt ) Type ( ) ExtType {
return GQLExtType
2023-07-25 00:27:39 -06:00
}
2023-08-31 19:50:32 -06:00
func ( ext * GQLExt ) MarshalBinary ( ) ( [ ] byte , error ) {
2023-08-01 20:55:15 -06:00
return json . Marshal ( ext )
2023-07-26 00:18:11 -06:00
}
2023-07-19 14:45:05 -06:00
var ecdsa_curves = map [ uint8 ] elliptic . Curve {
0 : elliptic . P256 ( ) ,
}
var ecdsa_curve_ids = map [ elliptic . Curve ] uint8 {
elliptic . P256 ( ) : 0 ,
}
var ecdh_curves = map [ uint8 ] ecdh . Curve {
0 : ecdh . P256 ( ) ,
}
var ecdh_curve_ids = map [ ecdh . Curve ] uint8 {
ecdh . P256 ( ) : 0 ,
2023-07-01 13:03:28 -06:00
}
2023-08-01 20:55:15 -06:00
func ( ext * GQLExt ) Deserialize ( ctx * Context , data [ ] byte ) error {
2023-08-07 20:26:02 -06:00
ext . resolver_response = map [ uuid . UUID ] chan Signal { }
2023-08-01 20:55:15 -06:00
return json . Unmarshal ( data , & ext )
2023-07-26 00:18:11 -06:00
}
2023-07-03 13:14:48 -06:00
2023-08-15 18:23:06 -06:00
func NewGQLExt ( ctx * Context , listen string , tls_cert [ ] byte , tls_key [ ] byte ) ( * GQLExt , error ) {
2023-07-21 01:21:53 -06:00
if tls_cert == nil || tls_key == nil {
2023-08-07 20:26:02 -06:00
ssl_key , err := ecdsa . GenerateKey ( elliptic . P256 ( ) , rand . Reader )
2023-07-21 01:21:53 -06:00
if err != nil {
2023-07-29 00:40:18 -06:00
return nil , err
2023-07-21 01:21:53 -06:00
}
2023-08-06 12:47:47 -06:00
ssl_key_bytes , err := x509 . MarshalPKCS8PrivateKey ( ssl_key )
2023-07-21 01:21:53 -06:00
if err != nil {
2023-07-29 00:40:18 -06:00
return nil , err
2023-07-21 01:21:53 -06:00
}
ssl_key_pem := pem . EncodeToMemory ( & pem . Block { Type : "PRIVATE KEY" , Bytes : ssl_key_bytes } )
serialNumberLimit := new ( big . Int ) . Lsh ( big . NewInt ( 1 ) , 128 )
serialNumber , _ := rand . Int ( rand . Reader , serialNumberLimit )
notBefore := time . Now ( )
notAfter := notBefore . Add ( 365 * 24 * time . Hour )
template := x509 . Certificate {
SerialNumber : serialNumber ,
Subject : pkix . Name {
Organization : [ ] string { "mekkanized" } ,
} ,
NotBefore : notBefore ,
NotAfter : notAfter ,
KeyUsage : x509 . KeyUsageDigitalSignature ,
ExtKeyUsage : [ ] x509 . ExtKeyUsage { x509 . ExtKeyUsageServerAuth } ,
BasicConstraintsValid : true ,
}
2023-08-06 12:47:47 -06:00
ssl_cert , err := x509 . CreateCertificate ( rand . Reader , & template , & template , ssl_key . Public ( ) , ssl_key )
2023-07-21 01:21:53 -06:00
if err != nil {
2023-07-29 00:40:18 -06:00
return nil , err
2023-07-21 01:21:53 -06:00
}
ssl_cert_pem := pem . EncodeToMemory ( & pem . Block { Type : "CERTIFICATE" , Bytes : ssl_cert } )
tls_cert = ssl_cert_pem
tls_key = ssl_key_pem
}
2023-07-26 11:56:10 -06:00
return & GQLExt {
2023-06-25 20:20:59 -06:00
Listen : listen ,
2023-07-31 20:53:56 -06:00
resolver_response : map [ uuid . UUID ] chan Signal { } ,
2023-08-07 20:26:02 -06:00
TLSCert : tls_cert ,
TLSKey : tls_key ,
2023-07-29 00:40:18 -06:00
} , nil
2023-06-25 20:20:59 -06:00
}
2023-07-29 11:03:41 -06:00
func ( ext * GQLExt ) StartGQLServer ( ctx * Context , node * Node ) error {
if ext . tcp_listener != nil || ext . http_server != nil {
return fmt . Errorf ( "listener or server is still running, stop them first" )
}
2023-07-27 15:27:14 -06:00
mux := http . NewServeMux ( )
2023-07-29 11:03:41 -06:00
mux . HandleFunc ( "/gql" , GQLHandler ( ctx , node , ext ) )
mux . HandleFunc ( "/gqlws" , GQLWSHandler ( ctx , node , ext ) )
2023-07-01 13:03:28 -06:00
2023-07-27 15:27:14 -06:00
// Server a graphiql interface(TODO make configurable whether to start this)
mux . HandleFunc ( "/graphiql" , GraphiQLHandler ( ) )
2023-07-01 13:03:28 -06:00
2023-07-27 15:27:14 -06:00
// Server the ./site directory to /site (TODO make configurable with better defaults)
fs := http . FileServer ( http . Dir ( "./site" ) )
mux . Handle ( "/site/" , http . StripPrefix ( "/site" , fs ) )
2023-07-21 01:05:24 -06:00
2023-07-27 15:27:14 -06:00
http_server := & http . Server {
2023-07-29 11:03:41 -06:00
Addr : ext . Listen ,
2023-07-27 15:27:14 -06:00
Handler : mux ,
}
2023-07-21 01:05:24 -06:00
2023-07-27 15:27:14 -06:00
l , err := net . Listen ( "tcp" , http_server . Addr )
if err != nil {
return fmt . Errorf ( "Failed to start listener for server on %s" , http_server . Addr )
}
2023-07-19 14:45:05 -06:00
2023-08-07 20:26:02 -06:00
cert , err := tls . X509KeyPair ( ext . TLSCert , ext . TLSKey )
2023-07-27 15:27:14 -06:00
if err != nil {
return err
}
2023-06-26 21:20:04 -06:00
2023-07-27 15:27:14 -06:00
config := tls . Config {
Certificates : [ ] tls . Certificate { cert } ,
NextProtos : [ ] string { "http/1.1" } ,
}
2023-07-19 14:45:05 -06:00
2023-07-27 15:27:14 -06:00
listener := tls . NewListener ( l , & config )
2023-07-22 20:21:17 -06:00
2023-07-29 11:03:41 -06:00
ext . http_done . Add ( 1 )
2023-07-27 15:27:14 -06:00
go func ( qql_ext * GQLExt ) {
2023-07-29 11:03:41 -06:00
defer ext . http_done . Done ( )
2023-07-22 20:21:17 -06:00
2023-07-27 15:27:14 -06:00
err := http_server . Serve ( listener )
if err != http . ErrServerClosed {
panic ( fmt . Sprintf ( "Failed to start gql server: %s" , err ) )
2023-07-22 20:21:17 -06:00
}
2023-07-29 11:03:41 -06:00
} ( ext )
2023-07-22 20:21:17 -06:00
2023-07-26 00:18:11 -06:00
2023-07-29 11:03:41 -06:00
ext . tcp_listener = listener
ext . http_server = http_server
2023-07-27 15:27:14 -06:00
return nil
2023-06-25 20:20:59 -06:00
}
2023-07-29 11:03:41 -06:00
func ( ext * GQLExt ) StopGQLServer ( ) error {
if ext . tcp_listener == nil || ext . http_server == nil {
return fmt . Errorf ( "already shutdown, cannot shut down again" )
}
ext . http_server . Shutdown ( context . TODO ( ) )
ext . http_done . Wait ( )
ext . tcp_listener = nil
ext . http_server = nil
return nil
2023-06-25 20:20:59 -06:00
}