fix latest CI breakages by forking code from grpc-go's interop/testing

Also moves testing package to internal/testing
This commit is contained in:
Joshua Humphries
2020-08-31 14:44:59 -04:00
committed by GitHub
parent 54ffdcacda
commit ba5f667e13
48 changed files with 1258 additions and 126 deletions

View File

@@ -0,0 +1,9 @@
# bankdemo
The `bankdemo` program is an example gRPC server that was used to demo `grpcurl` at Gophercon 2018.
It demonstrates interesting concepts for building a gRPC server, including chat functionality (that relies on full-duplex bidirectional streams). This code was written specifically to provide an interesting concrete demonstration and, as such, should not be considered in any way production-worthy.
The demo app tracks user accounts, transactions, and balances completely in memory. Every few seconds, as well as on graceful shutdown (like when the server receives a SIGTERM or SIGINT signal), this state is saved to a file named `accounts.json`, so that the data can be restored if the process restarts.
In addition to bank account data, the server also tracks "chat sessions", for demonstrating bidirectional streams in the form of an application where customers can chat with support agents.

View File

@@ -0,0 +1,49 @@
package main
import (
"strings"
"golang.org/x/net/context"
"google.golang.org/grpc/metadata"
)
func getCustomer(ctx context.Context) string {
// we'll just treat the "auth token" as if it is a
// customer ID, but reject tokens that begin with "agent"
// (those are auth tokens for support agents, not customers)
cust := getAuthCode(ctx)
if strings.HasPrefix(cust, "agent") {
return ""
}
return cust
}
func getAgent(ctx context.Context) string {
// we'll just treat the "auth token" as if it is an agent's
// user ID, but reject tokens that don't begin with "agent"
// (those are auth tokens for customers, not support agents)
agent := getAuthCode(ctx)
if !strings.HasPrefix(agent, "agent") {
return ""
}
return agent
}
func getAuthCode(ctx context.Context) string {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return ""
}
vals := md.Get("authorization")
if len(vals) != 1 {
return ""
}
pieces := strings.SplitN(strings.ToLower(vals[0]), " ", 2)
if len(pieces) != 2 {
return ""
}
if pieces[0] != "token" {
return ""
}
return pieces[1]
}

View File

@@ -0,0 +1,237 @@
package main
import (
"fmt"
"time"
"github.com/golang/protobuf/ptypes"
"github.com/golang/protobuf/ptypes/empty"
"golang.org/x/net/context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// bankServer implements the Bank gRPC service.
type bankServer struct {
allAccounts *accounts
}
func (s *bankServer) OpenAccount(ctx context.Context, req *OpenAccountRequest) (*Account, error) {
cust := getCustomer(ctx)
if cust == "" {
return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())
}
switch req.Type {
case Account_CHECKING, Account_SAVING, Account_MONEY_MARKET:
if req.InitialDepositCents < 0 {
return nil, status.Errorf(codes.InvalidArgument, "initial deposit amount cannot be negative: %s", dollars(req.InitialDepositCents))
}
case Account_LINE_OF_CREDIT, Account_LOAN, Account_EQUITIES:
if req.InitialDepositCents != 0 {
return nil, status.Errorf(codes.InvalidArgument, "initial deposit amount must be zero for account type %v: %s", req.Type, dollars(req.InitialDepositCents))
}
default:
return nil, status.Errorf(codes.InvalidArgument, "invalid account type: %v", req.Type)
}
return s.allAccounts.openAccount(cust, req.Type, req.InitialDepositCents), nil
}
func (s *bankServer) CloseAccount(ctx context.Context, req *CloseAccountRequest) (*empty.Empty, error) {
cust := getCustomer(ctx)
if cust == "" {
return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())
}
if err := s.allAccounts.closeAccount(cust, req.AccountNumber); err != nil {
return nil, err
}
return &empty.Empty{}, nil
}
func (s *bankServer) GetAccounts(ctx context.Context, _ *empty.Empty) (*GetAccountsResponse, error) {
cust := getCustomer(ctx)
if cust == "" {
return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())
}
accounts := s.allAccounts.getAllAccounts(cust)
return &GetAccountsResponse{Accounts: accounts}, nil
}
func (s *bankServer) GetTransactions(req *GetTransactionsRequest, stream Bank_GetTransactionsServer) error {
cust := getCustomer(stream.Context())
if cust == "" {
return status.Error(codes.Unauthenticated, codes.Unauthenticated.String())
}
acct, err := s.allAccounts.getAccount(cust, req.AccountNumber)
if err != nil {
return err
}
var start, end time.Time
if req.Start != nil {
start, err = ptypes.Timestamp(req.Start)
if err != nil {
return err
}
}
if req.End != nil {
end, err = ptypes.Timestamp(req.End)
if err != nil {
return err
}
} else {
end = time.Date(9999, 12, 31, 23, 59, 59, 999999999, time.Local)
}
txns := acct.getTransactions()
for _, txn := range txns {
t, err := ptypes.Timestamp(txn.Date)
if err != nil {
return err
}
if (t.After(start) || t.Equal(start)) &&
(t.Before(end) || t.Equal(end)) {
if err := stream.Send(txn); err != nil {
return err
}
}
}
return nil
}
func (s *bankServer) Deposit(ctx context.Context, req *DepositRequest) (*BalanceResponse, error) {
cust := getCustomer(ctx)
if cust == "" {
return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())
}
switch req.Source {
case DepositRequest_ACH, DepositRequest_CASH, DepositRequest_CHECK, DepositRequest_WIRE:
// ok
default:
return nil, status.Errorf(codes.InvalidArgument, "unknown deposit source: %v", req.Source)
}
if req.AmountCents <= 0 {
return nil, status.Errorf(codes.InvalidArgument, "deposit amount cannot be non-positive: %s", dollars(req.AmountCents))
}
desc := fmt.Sprintf("%v deposit", req.Source)
if req.Desc != "" {
desc = fmt.Sprintf("%s: %s", desc, req.Desc)
}
acct, err := s.allAccounts.getAccount(cust, req.AccountNumber)
if err != nil {
return nil, err
}
newBalance, err := acct.newTransaction(req.AmountCents, desc)
if err != nil {
return nil, err
}
return &BalanceResponse{
AccountNumber: req.AccountNumber,
BalanceCents: newBalance,
}, nil
}
func (s *bankServer) Withdraw(ctx context.Context, req *WithdrawRequest) (*BalanceResponse, error) {
cust := getCustomer(ctx)
if cust == "" {
return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())
}
if req.AmountCents >= 0 {
return nil, status.Errorf(codes.InvalidArgument, "withdrawal amount cannot be non-negative: %s", dollars(req.AmountCents))
}
acct, err := s.allAccounts.getAccount(cust, req.AccountNumber)
if err != nil {
return nil, err
}
newBalance, err := acct.newTransaction(req.AmountCents, req.Desc)
if err != nil {
return nil, err
}
return &BalanceResponse{
AccountNumber: req.AccountNumber,
BalanceCents: newBalance,
}, nil
}
func (s *bankServer) Transfer(ctx context.Context, req *TransferRequest) (*TransferResponse, error) {
cust := getCustomer(ctx)
if cust == "" {
return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())
}
if req.AmountCents <= 0 {
return nil, status.Errorf(codes.InvalidArgument, "transfer amount cannot be non-positive: %s", dollars(req.AmountCents))
}
var srcAcct *account
var srcDesc string
switch src := req.Source.(type) {
case *TransferRequest_ExternalSource:
srcDesc = fmt.Sprintf("ACH %09d:%06d", src.ExternalSource.AchRoutingNumber, src.ExternalSource.AchAccountNumber)
if src.ExternalSource.AchAccountNumber == 0 || src.ExternalSource.AchRoutingNumber == 0 {
return nil, status.Errorf(codes.InvalidArgument, "external source routing and account numbers cannot be zero: %s", srcDesc)
}
case *TransferRequest_SourceAccountNumber:
srcDesc = fmt.Sprintf("account %06d", src.SourceAccountNumber)
var err error
if srcAcct, err = s.allAccounts.getAccount(cust, src.SourceAccountNumber); err != nil {
return nil, err
}
}
var destAcct *account
var destDesc string
switch dest := req.Dest.(type) {
case *TransferRequest_ExternalDest:
destDesc = fmt.Sprintf("ACH %09d:%06d", dest.ExternalDest.AchRoutingNumber, dest.ExternalDest.AchAccountNumber)
if dest.ExternalDest.AchAccountNumber == 0 || dest.ExternalDest.AchRoutingNumber == 0 {
return nil, status.Errorf(codes.InvalidArgument, "external source routing and account numbers cannot be zero: %s", destDesc)
}
case *TransferRequest_DestAccountNumber:
destDesc = fmt.Sprintf("account %06d", dest.DestAccountNumber)
var err error
if destAcct, err = s.allAccounts.getAccount(cust, dest.DestAccountNumber); err != nil {
return nil, err
}
}
var srcBalance int32
if srcAcct != nil {
desc := fmt.Sprintf("transfer to %s", destDesc)
if req.Desc != "" {
desc = fmt.Sprintf("%s: %s", desc, req.Desc)
}
var err error
if srcBalance, err = srcAcct.newTransaction(-req.AmountCents, desc); err != nil {
return nil, err
}
}
var destBalance int32
if destAcct != nil {
desc := fmt.Sprintf("transfer from %s", srcDesc)
if req.Desc != "" {
desc = fmt.Sprintf("%s: %s", desc, req.Desc)
}
var err error
if destBalance, err = destAcct.newTransaction(req.AmountCents, desc); err != nil {
return nil, err
}
}
return &TransferResponse{
SrcAccountNumber: req.GetSourceAccountNumber(),
SrcBalanceCents: srcBalance,
DestAccountNumber: req.GetDestAccountNumber(),
DestBalanceCents: destBalance,
}, nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,125 @@
syntax = "proto3";
option go_package = "main";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
// Bank provides operations for interacting with bank accounts. All
// operations operate for the authenticated user (identified via an
// "authorization" request header, where the type is "token" and the
// credential is the customer's ID).
service Bank {
// OpenAccount creates an account with the type and given initial deposit
// as its balance.
rpc OpenAccount(OpenAccountRequest) returns (Account);
// CloseAccount closes the indicated account. An account can only be
// closed if its balance is zero.
rpc CloseAccount(CloseAccountRequest) returns (google.protobuf.Empty);
// GetAccounts lists all accounts for the current customer.
rpc GetAccounts(google.protobuf.Empty) returns (GetAccountsResponse);
// GetTransactions streams all transactions that match the given criteria.
// If the given start date is not specified, transactions since beginning
// of time are included. Similarly, if the given end date is not specified,
// transactions all the way to the presnet are included.
rpc GetTransactions(GetTransactionsRequest) returns (stream Transaction);
// Deposit increases the balance of an account by depositing funds into it.
rpc Deposit(DepositRequest) returns (BalanceResponse);
// Withdraw decreases the balance of an account by withdrawing funds from it.
rpc Withdraw(WithdrawRequest) returns (BalanceResponse);
// Transfer moves money from one account to another. The source and destination
// accounts can be with this bank (e.g. "local" account numbers) or can be
// external accounts, identified by their ACH routing and account numbers.
rpc Transfer(TransferRequest) returns (TransferResponse);
}
message OpenAccountRequest {
int32 initial_deposit_cents = 1;
Account.Type type = 2;
}
message CloseAccountRequest {
uint64 account_number = 1;
}
message GetAccountsResponse {
repeated Account accounts = 1;
}
message Account {
uint64 account_number = 1;
enum Type {
UNKNOWN = 0;
CHECKING = 1;
SAVING = 2;
MONEY_MARKET = 3;
LINE_OF_CREDIT = 4;
LOAN = 5;
EQUITIES = 6;
}
Type type = 2;
int32 balance_cents = 3;
}
message GetTransactionsRequest {
uint64 account_number = 1;
google.protobuf.Timestamp start = 2;
google.protobuf.Timestamp end = 3;
}
message Transaction {
uint64 account_number = 1;
uint64 seq_number = 2;
google.protobuf.Timestamp date = 3;
int32 amount_cents = 4;
string desc = 5;
}
message DepositRequest {
uint64 account_number = 1;
int32 amount_cents = 2;
enum Source {
UNKNOWN = 0;
CASH = 1;
CHECK = 2;
ACH = 3;
WIRE = 4;
}
Source source = 3;
string desc = 4;
}
message BalanceResponse {
uint64 account_number = 1;
int32 balance_cents = 2;
}
message WithdrawRequest {
uint64 account_number = 1;
int32 amount_cents = 2;
string desc = 3;
}
message TransferRequest {
message ExternalAccount {
uint64 ach_routing_number = 1;
uint64 ach_account_number = 2;
}
oneof source {
uint64 source_account_number = 1;
ExternalAccount external_source = 2;
}
oneof dest {
uint64 dest_account_number = 3;
ExternalAccount external_dest = 4;
}
int32 amount_cents = 5;
string desc = 6;
}
message TransferResponse {
uint64 src_account_number = 1;
int32 src_balance_cents = 2;
uint64 dest_account_number = 3;
int32 dest_balance_cents = 4;
}

View File

@@ -0,0 +1,465 @@
package main
import (
"fmt"
"io"
"sync"
"github.com/golang/protobuf/ptypes"
"golang.org/x/net/context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// chatServer implements the Support gRPC service, for providing
// a capability to connect customers and support agents in real-time
// chat.
type chatServer struct {
chatsBySession map[string]*session
chatsAwaitingAgent []string
lastSession int32
mu sync.RWMutex
}
type session struct {
Session
active bool
cust *listener
agents map[string]*listener
mu sync.RWMutex
}
type listener struct {
ch chan<- *ChatEntry
ctx context.Context
}
func (l *listener) send(e *ChatEntry) {
select {
case l.ch <- e:
case <-l.ctx.Done():
}
}
func (s *session) copySession() *Session {
s.mu.RLock()
defer s.mu.RUnlock()
return &Session{
SessionId: s.SessionId,
CustomerName: s.Session.CustomerName,
History: s.Session.History,
}
}
func (s *chatServer) ChatCustomer(stream Support_ChatCustomerServer) error {
ctx, cancel := context.WithCancel(stream.Context())
defer cancel()
cust := getCustomer(ctx)
if cust == "" {
return status.Error(codes.Unauthenticated, codes.Unauthenticated.String())
}
var sess *session
var ch chan *ChatEntry
var chCancel context.CancelFunc
cleanup := func() {
if sess != nil {
sess.mu.Lock()
sess.cust = nil
sess.mu.Unlock()
chCancel()
close(ch)
go func() {
// drain channel to prevent deadlock
for range ch {
}
}()
}
}
defer cleanup()
for {
req, err := stream.Recv()
if err != nil {
if err == io.EOF {
return nil
}
return err
}
switch req := req.Req.(type) {
case *ChatCustomerRequest_Init:
if sess != nil {
return status.Errorf(codes.FailedPrecondition, "already called init, currently in chat session %q", sess.SessionId)
}
sessionID := req.Init.ResumeSessionId
if sessionID == "" {
sess, ch, chCancel = s.newSession(ctx, cust)
} else if sess, ch, chCancel = s.resumeSession(ctx, cust, sessionID); sess == nil {
return status.Errorf(codes.FailedPrecondition, "cannot resume session %q; it is not an open session", sessionID)
}
err := stream.Send(&ChatCustomerResponse{
Resp: &ChatCustomerResponse_Session{
Session: sess.copySession(),
},
})
if err != nil {
return err
}
// monitor the returned channel, sending incoming agent messages down the pipe
go func() {
for {
select {
case entry, ok := <-ch:
if !ok {
return
}
if e, ok := entry.Entry.(*ChatEntry_AgentMsg); ok {
stream.Send(&ChatCustomerResponse{
Resp: &ChatCustomerResponse_Msg{
Msg: e.AgentMsg,
},
})
}
case <-ctx.Done():
return
}
}
}()
case *ChatCustomerRequest_Msg:
if sess == nil {
return status.Errorf(codes.FailedPrecondition, "never called init, no chat session for message")
}
entry := &ChatEntry{
Date: ptypes.TimestampNow(),
Entry: &ChatEntry_CustomerMsg{
CustomerMsg: req.Msg,
},
}
func() {
sess.mu.Lock()
sess.Session.History = append(sess.Session.History, entry)
sess.mu.Unlock()
sess.mu.RLock()
defer sess.mu.RUnlock()
for _, l := range sess.agents {
l.send(entry)
}
}()
case *ChatCustomerRequest_HangUp:
if sess == nil {
return status.Errorf(codes.FailedPrecondition, "never called init, no chat session to hang up")
}
s.closeSession(sess)
cleanup()
sess = nil
default:
return status.Error(codes.InvalidArgument, "unknown request type")
}
}
}
func (s *chatServer) ChatAgent(stream Support_ChatAgentServer) error {
ctx, cancel := context.WithCancel(stream.Context())
defer cancel()
agent := getAgent(ctx)
if agent == "" {
return status.Error(codes.Unauthenticated, codes.Unauthenticated.String())
}
var sess *session
var ch chan *ChatEntry
var chCancel context.CancelFunc
cleanup := func() {
if sess != nil {
sess.mu.Lock()
delete(sess.agents, agent)
if len(sess.agents) == 0 {
s.mu.Lock()
s.chatsAwaitingAgent = append(s.chatsAwaitingAgent, sess.SessionId)
s.mu.Unlock()
}
sess.mu.Unlock()
chCancel()
close(ch)
go func() {
// drain channel to prevent deadlock
for range ch {
}
}()
}
}
defer cleanup()
checkSession := func() {
// see if session was concurrently closed
if sess != nil {
sess.mu.RLock()
active := sess.active
sess.mu.RUnlock()
if !active {
cleanup()
sess = nil
}
}
}
for {
req, err := stream.Recv()
if err != nil {
if err == io.EOF {
return nil
}
return err
}
checkSession()
switch req := req.Req.(type) {
case *ChatAgentRequest_Accept:
if sess != nil {
return status.Errorf(codes.FailedPrecondition, "already called accept, currently in chat session %q", sess.SessionId)
}
sess, ch, chCancel = s.acceptSession(ctx, agent, req.Accept.SessionId)
if sess == nil {
return status.Errorf(codes.FailedPrecondition, "no session to accept")
}
err := stream.Send(&ChatAgentResponse{
Resp: &ChatAgentResponse_AcceptedSession{
AcceptedSession: sess.copySession(),
},
})
if err != nil {
return err
}
// monitor the returned channel, sending incoming agent messages down the pipe
go func() {
for {
select {
case entry, ok := <-ch:
if !ok {
return
}
if entry == nil {
stream.Send(&ChatAgentResponse{
Resp: &ChatAgentResponse_SessionEnded{
SessionEnded: Void_VOID,
},
})
continue
}
if agentMsg, ok := entry.Entry.(*ChatEntry_AgentMsg); ok {
if agentMsg.AgentMsg.AgentName == agent {
continue
}
}
stream.Send(&ChatAgentResponse{
Resp: &ChatAgentResponse_Msg{
Msg: entry,
},
})
case <-ctx.Done():
return
}
}
}()
case *ChatAgentRequest_Msg:
if sess == nil {
return status.Errorf(codes.FailedPrecondition, "never called accept, no chat session for message")
}
entry := &ChatEntry{
Date: ptypes.TimestampNow(),
Entry: &ChatEntry_AgentMsg{
AgentMsg: &AgentMessage{
AgentName: agent,
Msg: req.Msg,
},
},
}
active := true
func() {
sess.mu.Lock()
active = sess.active
if active {
sess.Session.History = append(sess.Session.History, entry)
}
sess.mu.Unlock()
if !active {
return
}
sess.mu.RLock()
defer sess.mu.RUnlock()
if sess.cust != nil {
sess.cust.send(entry)
}
for otherAgent, l := range sess.agents {
if otherAgent == agent {
continue
}
l.send(entry)
}
}()
if !active {
return status.Errorf(codes.FailedPrecondition, "customer hung up on chat session %s", sess.SessionId)
}
case *ChatAgentRequest_LeaveSession:
if sess == nil {
return status.Errorf(codes.FailedPrecondition, "never called init, no chat session to hang up")
}
s.closeSession(sess)
cleanup()
sess = nil
default:
return status.Error(codes.InvalidArgument, "unknown request type")
}
}
}
func (s *chatServer) newSession(ctx context.Context, cust string) (*session, chan *ChatEntry, context.CancelFunc) {
s.mu.Lock()
defer s.mu.Unlock()
s.lastSession++
id := fmt.Sprintf("%06d", s.lastSession)
s.chatsAwaitingAgent = append(s.chatsAwaitingAgent, id)
ch := make(chan *ChatEntry, 1)
ctx, cancel := context.WithCancel(ctx)
l := &listener{
ch: ch,
ctx: ctx,
}
sess := session{
active: true,
Session: Session{
SessionId: id,
CustomerName: cust,
},
cust: l,
}
s.chatsBySession[id] = &sess
return &sess, ch, cancel
}
func (s *chatServer) resumeSession(ctx context.Context, cust, sessionID string) (*session, chan *ChatEntry, context.CancelFunc) {
s.mu.Lock()
defer s.mu.Unlock()
sess := s.chatsBySession[sessionID]
if sess.CustomerName != cust {
// customer cannot join chat that they did not start
return nil, nil, nil
}
if !sess.active {
// chat has been closed
return nil, nil, nil
}
if sess.cust != nil {
// customer is active in the chat in another stream!
return nil, nil, nil
}
ch := make(chan *ChatEntry, 1)
ctx, cancel := context.WithCancel(ctx)
l := &listener{
ch: ch,
ctx: ctx,
}
sess.cust = l
return sess, ch, cancel
}
func (s *chatServer) closeSession(sess *session) {
active := true
func() {
sess.mu.Lock()
active = sess.active
sess.active = false
sess.mu.Unlock()
if !active {
// already closed
return
}
sess.mu.RLock()
defer sess.mu.RUnlock()
for _, l := range sess.agents {
l.send(nil)
}
}()
if !active {
// already closed
return
}
s.mu.Lock()
defer s.mu.Unlock()
delete(s.chatsBySession, sess.SessionId)
for i, id := range s.chatsAwaitingAgent {
if id == sess.SessionId {
s.chatsAwaitingAgent = append(s.chatsAwaitingAgent[:i], s.chatsAwaitingAgent[i+1:]...)
break
}
}
}
func (s *chatServer) acceptSession(ctx context.Context, agent, sessionID string) (*session, chan *ChatEntry, context.CancelFunc) {
var sess *session
func() {
s.mu.Lock()
defer s.mu.Unlock()
if len(s.chatsAwaitingAgent) == 0 {
return
}
if sessionID == "" {
sessionID = s.chatsAwaitingAgent[0]
s.chatsAwaitingAgent = s.chatsAwaitingAgent[1:]
} else {
found := false
for i, id := range s.chatsAwaitingAgent {
if id == sessionID {
found = true
s.chatsAwaitingAgent = append(s.chatsAwaitingAgent[:i], s.chatsAwaitingAgent[i+1:]...)
break
}
}
if !found {
return
}
}
sess = s.chatsBySession[sessionID]
}()
if sess == nil {
return nil, nil, nil
}
ch := make(chan *ChatEntry, 1)
ctx, cancel := context.WithCancel(ctx)
l := &listener{
ch: ch,
ctx: ctx,
}
sess.mu.Lock()
if sess.agents == nil {
sess.agents = map[string]*listener{}
}
sess.agents[agent] = l
sess.mu.Unlock()
return sess, ch, cancel
}

View File

@@ -0,0 +1,190 @@
package main
import (
"bytes"
"fmt"
"sync"
"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/ptypes"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// In-memory database that is periodically saved to a JSON file.
type accounts struct {
AccountNumbersByCustomer map[string][]uint64
AccountsByNumber map[uint64]*account
AccountNumbers []uint64
Customers []string
LastAccountNum uint64
mu sync.RWMutex
}
func (a *accounts) openAccount(customer string, accountType Account_Type, initialBalanceCents int32) *Account {
a.mu.Lock()
defer a.mu.Unlock()
accountNums, ok := a.AccountNumbersByCustomer[customer]
if !ok {
// no accounts for this customer? it's a new customer
a.Customers = append(a.Customers, customer)
}
num := a.LastAccountNum + 1
a.LastAccountNum = num
a.AccountNumbers = append(a.AccountNumbers, num)
accountNums = append(accountNums, num)
a.AccountNumbersByCustomer[customer] = accountNums
var acct account
acct.AccountNumber = num
acct.Type = accountType
acct.BalanceCents = initialBalanceCents
acct.Transactions = append(acct.Transactions, &Transaction{
AccountNumber: num,
SeqNumber: 1,
Date: ptypes.TimestampNow(),
AmountCents: initialBalanceCents,
Desc: "initial deposit",
})
a.AccountsByNumber[num] = &acct
return &acct.Account
}
func (a *accounts) closeAccount(customer string, accountNumber uint64) error {
a.mu.Lock()
defer a.mu.Unlock()
acctNums := a.AccountNumbersByCustomer[customer]
found := -1
for i, num := range acctNums {
if num == accountNumber {
found = i
break
}
}
if found == -1 {
return status.Errorf(codes.NotFound, "you have no account numbered %d", accountNumber)
}
acct := a.AccountsByNumber[accountNumber]
if acct.BalanceCents != 0 {
return status.Errorf(codes.FailedPrecondition, "account %d cannot be closed because it has a non-zero balance: %s", accountNumber, dollars(acct.BalanceCents))
}
for i, num := range a.AccountNumbers {
if num == accountNumber {
a.AccountNumbers = append(a.AccountNumbers[:i], a.AccountNumbers[i+1:]...)
break
}
}
a.AccountNumbersByCustomer[customer] = append(acctNums[:found], acctNums[found+1:]...)
delete(a.AccountsByNumber, accountNumber)
return nil
}
func (a *accounts) getAccount(customer string, accountNumber uint64) (*account, error) {
a.mu.RLock()
defer a.mu.RUnlock()
acctNums := a.AccountNumbersByCustomer[customer]
for _, num := range acctNums {
if num == accountNumber {
return a.AccountsByNumber[num], nil
}
}
return nil, status.Errorf(codes.NotFound, "you have no account numbered %d", accountNumber)
}
func (a *accounts) getAllAccounts(customer string) []*Account {
a.mu.RLock()
defer a.mu.RUnlock()
accountNums := a.AccountNumbersByCustomer[customer]
var accounts []*Account
for _, num := range accountNums {
accounts = append(accounts, &a.AccountsByNumber[num].Account)
}
return accounts
}
type account struct {
Account
Transactions []*Transaction
mu sync.RWMutex
}
func (a *account) getTransactions() []*Transaction {
a.mu.RLock()
defer a.mu.RUnlock()
return a.Transactions
}
func (a *account) newTransaction(amountCents int32, desc string) (newBalance int32, err error) {
a.mu.Lock()
defer a.mu.Unlock()
bal := a.BalanceCents + amountCents
if bal < 0 {
return 0, status.Errorf(codes.FailedPrecondition, "insufficient funds: cannot withdraw %s when balance is %s", dollars(amountCents), dollars(a.BalanceCents))
}
a.BalanceCents = bal
a.Transactions = append(a.Transactions, &Transaction{
AccountNumber: a.AccountNumber,
Date: ptypes.TimestampNow(),
AmountCents: amountCents,
SeqNumber: uint64(len(a.Transactions) + 1),
Desc: desc,
})
return bal, nil
}
func (t *Transaction) MarshalJSON() ([]byte, error) {
var jsm jsonpb.Marshaler
var buf bytes.Buffer
if err := jsm.Marshal(&buf, t); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func (t *Transaction) UnmarshalJSON(b []byte) error {
return jsonpb.Unmarshal(bytes.NewReader(b), t)
}
func (a *accounts) clone() *accounts {
var clone accounts
clone.AccountNumbersByCustomer = map[string][]uint64{}
clone.AccountsByNumber = map[uint64]*account{}
a.mu.RLock()
clone.Customers = a.Customers
a.mu.RUnlock()
for _, cust := range clone.Customers {
var acctNums []uint64
a.mu.RLock()
acctNums = a.AccountNumbersByCustomer[cust]
a.mu.RUnlock()
clone.AccountNumbersByCustomer[cust] = acctNums
clone.AccountNumbers = append(clone.AccountNumbers, acctNums...)
for _, acctNum := range acctNums {
a.mu.RLock()
acct := a.AccountsByNumber[acctNum]
a.mu.RUnlock()
acct.mu.RLock()
txns := acct.Transactions
acct.mu.RUnlock()
clone.AccountsByNumber[acctNum] = &account{Transactions: txns}
}
}
return &clone
}
func dollars(amountCents int32) string {
return fmt.Sprintf("$%02f", float64(amountCents)/100)
}

View File

@@ -0,0 +1,168 @@
package main
//go:generate protoc --go_out=plugins=grpc:./ bank.proto support.proto
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"net"
"os"
"os/signal"
"sync/atomic"
"syscall"
"time"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/peer"
"google.golang.org/grpc/reflection"
"google.golang.org/grpc/status"
)
func main() {
grpclog.SetLoggerV2(grpclog.NewLoggerV2(os.Stdout, os.Stdout, os.Stderr))
port := flag.Int("port", 12345, "The port on which bankdemo gRPC server will listen.")
datafile := flag.String("datafile", "accounts.json", "The path and filename to which bank account data is saved and from which data will be loaded.")
flag.Parse()
// create the server and load initial dataset
ctx, cancel := context.WithCancel(context.Background())
s := &svr{
datafile: *datafile,
ctx: ctx,
cancel: cancel,
}
if err := s.load(); err != nil {
panic(err)
}
l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", *port))
if err != nil {
panic(err)
}
grpcSvr := gRPCServer()
// Register gRPC service implementations
bankSvc := bankServer{
allAccounts: &s.allAccounts,
}
RegisterBankServer(grpcSvr, &bankSvc)
chatSvc := chatServer{
chatsBySession: map[string]*session{},
}
RegisterSupportServer(grpcSvr, &chatSvc)
go s.bgSaver()
// don't forget to include server reflection support!
reflection.Register(grpcSvr)
defer func() {
cancel()
s.flush()
}()
// trap SIGINT / SIGTERM to exit cleanly
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT)
signal.Notify(c, syscall.SIGTERM)
go func() {
<-c
fmt.Println("Shutting down...")
grpcSvr.GracefulStop()
}()
grpclog.Infof("server starting, listening on %v", l.Addr())
if err := grpcSvr.Serve(l); err != nil {
panic(err)
}
}
func gRPCServer() *grpc.Server {
var reqCounter uint64
return grpc.NewServer(
grpc.UnaryInterceptor(func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
reqID := atomic.AddUint64(&reqCounter, 1)
var client string
if p, ok := peer.FromContext(ctx); ok {
client = p.Addr.String()
} else {
client = "?"
}
grpclog.Infof("request %d started for %s from %s", reqID, info.FullMethod, client)
rsp, err := handler(ctx, req)
stat, _ := status.FromError(err)
grpclog.Infof("request %d completed for %s from %s: %v %s", reqID, info.FullMethod, client, stat.Code(), stat.Message())
return rsp, err
}),
grpc.StreamInterceptor(func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
reqID := atomic.AddUint64(&reqCounter, 1)
var client string
if p, ok := peer.FromContext(ss.Context()); ok {
client = p.Addr.String()
} else {
client = "?"
}
grpclog.Infof("request %d started for %s from %s", reqID, info.FullMethod, client)
err := handler(srv, ss)
stat, _ := status.FromError(err)
grpclog.Infof("request %d completed for %s from %s: %v %s", reqID, info.FullMethod, client, stat.Code(), stat.Message())
return err
}))
}
type svr struct {
datafile string
ctx context.Context
cancel context.CancelFunc
allAccounts accounts
}
func (s *svr) load() error {
accts, err := ioutil.ReadFile(s.datafile)
if err != nil && !os.IsNotExist(err) {
return err
}
if len(accts) == 0 {
s.allAccounts.AccountNumbersByCustomer = map[string][]uint64{}
s.allAccounts.AccountsByNumber = map[uint64]*account{}
} else if err := json.Unmarshal(accts, &s.allAccounts); err != nil {
return err
}
return nil
}
func (s *svr) bgSaver() {
ticker := time.NewTicker(5 * time.Second)
for {
select {
case <-ticker.C:
s.flush()
case <-s.ctx.Done():
ticker.Stop()
return
}
}
}
func (s *svr) flush() {
accounts := s.allAccounts.clone()
if b, err := json.Marshal(accounts); err != nil {
grpclog.Errorf("failed to save data to %q", s.datafile)
} else if err := ioutil.WriteFile(s.datafile, b, 0666); err != nil {
grpclog.Errorf("failed to save data to %q", s.datafile)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,118 @@
syntax = "proto3";
option go_package = "main";
import "google/protobuf/timestamp.proto";
// Support provides an interactive chat service, for customers to interact with
// the bank's support agents. A single stream, for either of the two methods, is
// a stateful connection to a single "chat session". Streams are initially disconnected
// (not part of any session). A stream must be disconnected from a session (via customer
// hang up or via agent leaving a session) before it can be connected to a new one.
service Support {
// ChatCustomer is used by a customer-facing app to send the customer's messages
// to a chat session. The customer is how initiates and terminates (via "hangup")
// a chat session. Only customers may invoke this method (e.g. requests must
// include customer auth credentials).
rpc ChatCustomer(stream ChatCustomerRequest) returns (stream ChatCustomerResponse);
// ChatAgent is used by an agent-facing app to allow an agent to reply to a
// customer's messages in a chat session. The agent may accept a chat session,
// which defaults to the session awaiting an agent for the longest period of time
// (FIFO queue).
rpc ChatAgent(stream ChatAgentRequest) returns (stream ChatAgentResponse);
}
enum Void {
VOID = 0;
}
message ChatCustomerRequest {
oneof req {
// init is used when a chat stream is not part of a
// chat session. This is a stream's initial state, as well as
// the state after a "hang_up" request is sent. This creates
// a new state session or resumes an existing one.
InitiateChat init = 1;
// msg is used to send the customer's messages to support
// agents.
string msg = 2;
// hang_up is used to terminate a chat session. If a stream
// is broken, but the session was not terminated, the client
// may initiate a new stream and use init to resume that
// session. Sessions are not terminated unless done so
// explicitly via sending this kind of request on the stream.
Void hang_up = 3;
}
}
message InitiateChat {
string resume_session_id = 1;
}
message AgentMessage {
string agent_name = 1;
string msg = 2;
}
message ChatCustomerResponse {
oneof resp {
// session is sent from the server when the stream is connected
// to a chat session. This happens after an init request is sent
// and the stream is connected to either a new or resumed session.
Session session = 1;
// msg is sent from the server to convey agents' messages to the
// customer.
AgentMessage msg = 2;
}
}
message ChatAgentRequest {
oneof req {
// accept is used when an agent wants to join a customer chat
// session. It can be used to connect to a specific session (by
// ID), or to just accept the session for which the customer has
// been waiting the longest (e.g. poll a FIFO queue of sessions
// awaiting a support agent). It is possible for multiple agents
// to be connected to the same chat session.
AcceptChat accept = 1;
// msg is used to send a message to the customer. It will also be
// delivered to any other connected support agents.
string msg = 2;
// leave_session allows an agent to exit a chat session. They can
// always re-enter later by sending an accept message for that
// session ID.
Void leave_session = 3;
}
}
message AcceptChat {
string session_id = 1;
}
message ChatEntry {
google.protobuf.Timestamp date = 1;
oneof entry {
string customer_msg = 2;
AgentMessage agent_msg = 3;
}
}
message ChatAgentResponse {
oneof resp {
// accepted_session provides the detail of a chat session. The server
// sends this message after the agent has accepted a chat session.
Session accepted_session = 1;
// msg is sent by the server when the customer, or another support
// agent, sends a message in stream's current session.
ChatEntry msg = 2;
// session_ended notifies the support agent that their currently
// connected chat session has been terminated by the customer.
Void session_ended = 3;
}
}
message Session {
string session_id = 1;
string customer_name = 2;
repeated ChatEntry history = 3;
}

View File

@@ -0,0 +1,6 @@
# testserver
The `testserver` program is a simple server that can be used for testing RPC clients such
as `grpcurl`. It implements an RPC interface that is defined in `grpcurl`'s [testing package](https://github.com/fullstorydev/grpcurl/blob/master/testing/example.proto) and also exposes [the implementation](https://godoc.org/github.com/fullstorydev/grpcurl/testing#TestServer) that is defined in that same package. This is the same test interface and implementation that is used in unit tests for `grpcurl`.
For a possibly more interesting test server, take a look at `bankdemo`, which is a demo gRPC app that provides a more concrete RPC interface, including full-duplex bidirectional streaming methods, plus an example implementation.

View File

@@ -0,0 +1,176 @@
// Command testserver spins up a test GRPC server.
package main
import (
"flag"
"fmt"
"net"
"os"
"sync/atomic"
"time"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/reflection"
"google.golang.org/grpc/status"
"github.com/fullstorydev/grpcurl"
grpcurl_testing "github.com/fullstorydev/grpcurl/internal/testing"
)
var (
getUnixSocket func() string // nil when run on non-unix platforms
help = flag.Bool("help", false, "Print usage instructions and exit.")
cacert = flag.String("cacert", "",
`File containing trusted root certificates for verifying client certs. Ignored
if TLS is not in use (e.g. no -cert or -key specified).`)
cert = flag.String("cert", "",
`File containing server certificate (public key). Must also provide -key option.
Server uses plain-text if no -cert and -key options are given.`)
key = flag.String("key", "",
`File containing server private key. Must also provide -cert option. Server uses
plain-text if no -cert and -key options are given.`)
requirecert = flag.Bool("requirecert", false,
`Require clients to authenticate via client certs. Must be using TLS (e.g. must
also provide -cert and -key options).`)
port = flag.Int("p", 0, "Port on which to listen. Ephemeral port used if not specified.")
noreflect = flag.Bool("noreflect", false, "Indicates that server should not support server reflection.")
quiet = flag.Bool("q", false, "Suppresses server request and stream logging.")
)
func main() {
flag.Parse()
if *help {
flag.PrintDefaults()
os.Exit(0)
}
grpclog.SetLoggerV2(grpclog.NewLoggerV2(os.Stdout, os.Stdout, os.Stderr))
if len(flag.Args()) > 0 {
fmt.Fprintln(os.Stderr, "No arguments expected.")
os.Exit(2)
}
if (*cert == "") != (*key == "") {
fmt.Fprintln(os.Stderr, "The -cert and -key arguments must be used together and both be present.")
os.Exit(2)
}
if *requirecert && *cert == "" {
fmt.Fprintln(os.Stderr, "The -requirecert arg cannot be used without -cert and -key arguments.")
os.Exit(2)
}
var opts []grpc.ServerOption
if *cert != "" {
creds, err := grpcurl.ServerTransportCredentials(*cacert, *cert, *key, *requirecert)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to configure transport credentials: %v\n", err)
os.Exit(1)
}
opts = []grpc.ServerOption{grpc.Creds(creds)}
}
if !*quiet {
opts = append(opts, grpc.UnaryInterceptor(unaryLogger), grpc.StreamInterceptor(streamLogger))
}
var network, addr string
if getUnixSocket != nil && getUnixSocket() != "" {
network = "unix"
addr = getUnixSocket()
} else {
network = "tcp"
addr = fmt.Sprintf("127.0.0.1:%d", *port)
}
l, err := net.Listen(network, addr)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to listen on socket: %v\n", err)
os.Exit(1)
}
fmt.Printf("Listening on %v\n", l.Addr())
svr := grpc.NewServer(opts...)
grpcurl_testing.RegisterTestServiceServer(svr, grpcurl_testing.TestServer{})
if !*noreflect {
reflection.Register(svr)
}
if err := svr.Serve(l); err != nil {
fmt.Fprintf(os.Stderr, "GRPC server returned error: %v\n", err)
os.Exit(1)
}
}
var id int32
func unaryLogger(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
i := atomic.AddInt32(&id, 1) - 1
grpclog.Infof("start <%d>: %s\n", i, info.FullMethod)
start := time.Now()
rsp, err := handler(ctx, req)
var code codes.Code
if stat, ok := status.FromError(err); ok {
code = stat.Code()
} else {
code = codes.Unknown
}
grpclog.Infof("completed <%d>: %v (%d) %v\n", i, code, code, time.Since(start))
return rsp, err
}
func streamLogger(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
i := atomic.AddInt32(&id, 1) - 1
start := time.Now()
grpclog.Infof("start <%d>: %s\n", i, info.FullMethod)
err := handler(srv, loggingStream{ss: ss, id: i})
var code codes.Code
if stat, ok := status.FromError(err); ok {
code = stat.Code()
} else {
code = codes.Unknown
}
grpclog.Infof("completed <%d>: %v(%d) %v\n", i, code, code, time.Since(start))
return err
}
type loggingStream struct {
ss grpc.ServerStream
id int32
}
func (l loggingStream) SetHeader(md metadata.MD) error {
return l.ss.SetHeader(md)
}
func (l loggingStream) SendHeader(md metadata.MD) error {
return l.ss.SendHeader(md)
}
func (l loggingStream) SetTrailer(md metadata.MD) {
l.ss.SetTrailer(md)
}
func (l loggingStream) Context() context.Context {
return l.ss.Context()
}
func (l loggingStream) SendMsg(m interface{}) error {
err := l.ss.SendMsg(m)
if err == nil {
grpclog.Infof("stream <%d>: sent message\n", l.id)
}
return err
}
func (l loggingStream) RecvMsg(m interface{}) error {
err := l.ss.RecvMsg(m)
if err == nil {
grpclog.Infof("stream <%d>: received message\n", l.id)
}
return err
}

View File

@@ -0,0 +1,17 @@
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package main
import "flag"
var (
unix = flag.String("unix", "",
`Use instead of -p to indicate listening on a Unix domain socket instead of a
TCP port. If present, must be the path to a domain socket.`)
)
func init() {
getUnixSocket = func() string {
return *unix
}
}

View File

@@ -0,0 +1,21 @@
syntax = "proto3";
import "google/protobuf/descriptor.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
import "example2.proto";
message TestRequest {
repeated string file_names = 1;
repeated Extension extensions = 2;
}
message TestResponse {
map<string, google.protobuf.FileDescriptorProto> file_protos = 1;
google.protobuf.Timestamp last_update_date = 2;
}
service TestService {
rpc GetFiles (TestRequest) returns (TestResponse);
rpc Ping (google.protobuf.Empty) returns (google.protobuf.Empty);
}

Binary file not shown.

View File

@@ -0,0 +1,8 @@
syntax = "proto3";
import "google/protobuf/any.proto";
message Extension {
uint64 id = 1;
google.protobuf.Any data = 2;
}

View File

@@ -0,0 +1,215 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: testing/jsonpb_test_proto/test_objects.proto
package jsonpb
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
any "github.com/golang/protobuf/ptypes/any"
duration "github.com/golang/protobuf/ptypes/duration"
_struct "github.com/golang/protobuf/ptypes/struct"
timestamp "github.com/golang/protobuf/ptypes/timestamp"
wrappers "github.com/golang/protobuf/ptypes/wrappers"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type KnownTypes struct {
An *any.Any `protobuf:"bytes,14,opt,name=an" json:"an,omitempty"`
Dur *duration.Duration `protobuf:"bytes,1,opt,name=dur" json:"dur,omitempty"`
St *_struct.Struct `protobuf:"bytes,12,opt,name=st" json:"st,omitempty"`
Ts *timestamp.Timestamp `protobuf:"bytes,2,opt,name=ts" json:"ts,omitempty"`
Lv *_struct.ListValue `protobuf:"bytes,15,opt,name=lv" json:"lv,omitempty"`
Val *_struct.Value `protobuf:"bytes,16,opt,name=val" json:"val,omitempty"`
Dbl *wrappers.DoubleValue `protobuf:"bytes,3,opt,name=dbl" json:"dbl,omitempty"`
Flt *wrappers.FloatValue `protobuf:"bytes,4,opt,name=flt" json:"flt,omitempty"`
I64 *wrappers.Int64Value `protobuf:"bytes,5,opt,name=i64" json:"i64,omitempty"`
U64 *wrappers.UInt64Value `protobuf:"bytes,6,opt,name=u64" json:"u64,omitempty"`
I32 *wrappers.Int32Value `protobuf:"bytes,7,opt,name=i32" json:"i32,omitempty"`
U32 *wrappers.UInt32Value `protobuf:"bytes,8,opt,name=u32" json:"u32,omitempty"`
Bool *wrappers.BoolValue `protobuf:"bytes,9,opt,name=bool" json:"bool,omitempty"`
Str *wrappers.StringValue `protobuf:"bytes,10,opt,name=str" json:"str,omitempty"`
Bytes *wrappers.BytesValue `protobuf:"bytes,11,opt,name=bytes" json:"bytes,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *KnownTypes) Reset() { *m = KnownTypes{} }
func (m *KnownTypes) String() string { return proto.CompactTextString(m) }
func (*KnownTypes) ProtoMessage() {}
func (*KnownTypes) Descriptor() ([]byte, []int) {
return fileDescriptor_ab4422ec10550c41, []int{0}
}
func (m *KnownTypes) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_KnownTypes.Unmarshal(m, b)
}
func (m *KnownTypes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_KnownTypes.Marshal(b, m, deterministic)
}
func (m *KnownTypes) XXX_Merge(src proto.Message) {
xxx_messageInfo_KnownTypes.Merge(m, src)
}
func (m *KnownTypes) XXX_Size() int {
return xxx_messageInfo_KnownTypes.Size(m)
}
func (m *KnownTypes) XXX_DiscardUnknown() {
xxx_messageInfo_KnownTypes.DiscardUnknown(m)
}
var xxx_messageInfo_KnownTypes proto.InternalMessageInfo
func (m *KnownTypes) GetAn() *any.Any {
if m != nil {
return m.An
}
return nil
}
func (m *KnownTypes) GetDur() *duration.Duration {
if m != nil {
return m.Dur
}
return nil
}
func (m *KnownTypes) GetSt() *_struct.Struct {
if m != nil {
return m.St
}
return nil
}
func (m *KnownTypes) GetTs() *timestamp.Timestamp {
if m != nil {
return m.Ts
}
return nil
}
func (m *KnownTypes) GetLv() *_struct.ListValue {
if m != nil {
return m.Lv
}
return nil
}
func (m *KnownTypes) GetVal() *_struct.Value {
if m != nil {
return m.Val
}
return nil
}
func (m *KnownTypes) GetDbl() *wrappers.DoubleValue {
if m != nil {
return m.Dbl
}
return nil
}
func (m *KnownTypes) GetFlt() *wrappers.FloatValue {
if m != nil {
return m.Flt
}
return nil
}
func (m *KnownTypes) GetI64() *wrappers.Int64Value {
if m != nil {
return m.I64
}
return nil
}
func (m *KnownTypes) GetU64() *wrappers.UInt64Value {
if m != nil {
return m.U64
}
return nil
}
func (m *KnownTypes) GetI32() *wrappers.Int32Value {
if m != nil {
return m.I32
}
return nil
}
func (m *KnownTypes) GetU32() *wrappers.UInt32Value {
if m != nil {
return m.U32
}
return nil
}
func (m *KnownTypes) GetBool() *wrappers.BoolValue {
if m != nil {
return m.Bool
}
return nil
}
func (m *KnownTypes) GetStr() *wrappers.StringValue {
if m != nil {
return m.Str
}
return nil
}
func (m *KnownTypes) GetBytes() *wrappers.BytesValue {
if m != nil {
return m.Bytes
}
return nil
}
func init() {
proto.RegisterType((*KnownTypes)(nil), "jsonpb.KnownTypes")
}
func init() {
proto.RegisterFile("testing/jsonpb_test_proto/test_objects.proto", fileDescriptor_ab4422ec10550c41)
}
var fileDescriptor_ab4422ec10550c41 = []byte{
// 402 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0xd0, 0xdf, 0x6b, 0xd5, 0x30,
0x14, 0x07, 0x70, 0x96, 0xee, 0x4e, 0xcd, 0x44, 0x25, 0x88, 0x66, 0xd7, 0xa1, 0x22, 0x82, 0xc3,
0x1f, 0x2d, 0xb6, 0xa5, 0xef, 0x0e, 0x11, 0x44, 0x9f, 0xba, 0xe9, 0xeb, 0x48, 0xd6, 0xac, 0x74,
0x64, 0x49, 0x49, 0x4e, 0xee, 0xe8, 0xbf, 0xe6, 0x5f, 0x27, 0x49, 0x73, 0x45, 0x6e, 0xc9, 0xde,
0xee, 0xcd, 0xf7, 0x73, 0xbe, 0x9c, 0x53, 0xfc, 0x11, 0x84, 0x85, 0x41, 0xf5, 0xc5, 0xb5, 0xd5,
0x6a, 0xe4, 0x17, 0xfe, 0xef, 0xc5, 0x68, 0x34, 0xe8, 0x22, 0xfc, 0xd4, 0xfc, 0x5a, 0x5c, 0x82,
0xcd, 0xc3, 0x13, 0x39, 0x98, 0xd5, 0xfa, 0xa8, 0xd7, 0xba, 0x97, 0xa2, 0x08, 0xaf, 0xdc, 0x5d,
0x15, 0x4c, 0x4d, 0x33, 0x59, 0xbf, 0xdc, 0x8d, 0x3a, 0x67, 0x18, 0x0c, 0x5a, 0xc5, 0xfc, 0x78,
0x37, 0xb7, 0x60, 0xdc, 0x25, 0xc4, 0xf4, 0xd5, 0x6e, 0x0a, 0xc3, 0x8d, 0xb0, 0xc0, 0x6e, 0xc6,
0x54, 0xfd, 0xad, 0x61, 0xe3, 0x28, 0x4c, 0xdc, 0xf0, 0xcd, 0x9f, 0x15, 0xc6, 0x3f, 0x94, 0xbe,
0x55, 0xe7, 0xd3, 0x28, 0x2c, 0x79, 0x8b, 0x11, 0x53, 0xf4, 0xd1, 0xeb, 0xbd, 0x93, 0xc3, 0xf2,
0x69, 0x3e, 0xcf, 0xe6, 0xdb, 0xd9, 0xfc, 0x8b, 0x9a, 0x5a, 0xc4, 0x14, 0xf9, 0x80, 0xb3, 0xce,
0x19, 0xba, 0x17, 0xd8, 0xd1, 0x82, 0x7d, 0x8d, 0x17, 0xb4, 0x5e, 0x91, 0x77, 0x18, 0x59, 0xa0,
0x0f, 0x83, 0x7d, 0xbe, 0xb0, 0x67, 0xe1, 0x9a, 0x16, 0x59, 0x20, 0xef, 0x31, 0x02, 0x4b, 0x51,
0x80, 0xeb, 0x05, 0x3c, 0xdf, 0x1e, 0xd6, 0x22, 0xb0, 0xde, 0xca, 0x0d, 0x7d, 0x9c, 0xb0, 0x3f,
0x07, 0x0b, 0xbf, 0x99, 0x74, 0xa2, 0x45, 0x72, 0x43, 0x4e, 0x70, 0xb6, 0x61, 0x92, 0x3e, 0x09,
0xf8, 0xd9, 0x02, 0xcf, 0xd0, 0x13, 0x92, 0xe3, 0xac, 0xe3, 0x92, 0x66, 0x41, 0x1e, 0x2f, 0xef,
0xd2, 0x8e, 0x4b, 0x11, 0x7d, 0xc7, 0x25, 0xf9, 0x84, 0xb3, 0x2b, 0x09, 0x74, 0x3f, 0xf8, 0x17,
0x0b, 0xff, 0x4d, 0x6a, 0x16, 0xf7, 0xf0, 0xce, 0xf3, 0xa1, 0xa9, 0xe9, 0x2a, 0xc1, 0xbf, 0x2b,
0x68, 0xea, 0xc8, 0x87, 0xa6, 0xf6, 0xdb, 0xb8, 0xa6, 0xa6, 0x07, 0x89, 0x6d, 0x7e, 0xfd, 0xef,
0x5d, 0x53, 0x87, 0xfa, 0xaa, 0xa4, 0xf7, 0xd2, 0xf5, 0x55, 0xb9, 0xad, 0xaf, 0xca, 0x50, 0x5f,
0x95, 0xf4, 0xfe, 0x1d, 0xf5, 0xff, 0xbc, 0x0b, 0x7e, 0x9f, 0x6b, 0x2d, 0xe9, 0x83, 0xc4, 0x47,
0x3f, 0xd5, 0x5a, 0xce, 0x3c, 0x38, 0xdf, 0x6f, 0xc1, 0x50, 0x9c, 0xe8, 0x3f, 0x03, 0x33, 0xa8,
0x3e, 0xf6, 0x5b, 0x30, 0xe4, 0x33, 0x5e, 0xf1, 0x09, 0x84, 0xa5, 0x87, 0x89, 0x03, 0x4e, 0x7d,
0x3a, 0x0f, 0xcc, 0xf2, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xab, 0x65, 0x99, 0x5a, 0x8d, 0x03,
0x00, 0x00,
}

View File

@@ -0,0 +1,59 @@
// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2015 The Go Authors. All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto2";
import "google/protobuf/any.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";
package jsonpb;
message KnownTypes {
optional google.protobuf.Any an = 14;
optional google.protobuf.Duration dur = 1;
optional google.protobuf.Struct st = 12;
optional google.protobuf.Timestamp ts = 2;
optional google.protobuf.ListValue lv = 15;
optional google.protobuf.Value val = 16;
optional google.protobuf.DoubleValue dbl = 3;
optional google.protobuf.FloatValue flt = 4;
optional google.protobuf.Int64Value i64 = 5;
optional google.protobuf.UInt64Value u64 = 6;
optional google.protobuf.Int32Value i32 = 7;
optional google.protobuf.UInt32Value u32 = 8;
optional google.protobuf.BoolValue bool = 9;
optional google.protobuf.StringValue str = 10;
optional google.protobuf.BytesValue bytes = 11;
}

1132
internal/testing/test.pb.go Normal file

File diff suppressed because it is too large Load Diff

176
internal/testing/test.proto Normal file
View File

@@ -0,0 +1,176 @@
// NB: Copied from the gRPC Go repo: google.golang.org/grpc/interop/grpc_testing/test.proto
// Copyright 2017 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// An integration test service that covers all the method signature permutations
// of unary/streaming requests/responses.
syntax = "proto3";
package testing;
message Empty {}
// The type of payload that should be returned.
enum PayloadType {
// Compressable text format.
COMPRESSABLE = 0;
// Uncompressable binary format.
UNCOMPRESSABLE = 1;
// Randomly chosen from all other formats defined in this enum.
RANDOM = 2;
}
// A block of data, to simply increase gRPC message size.
message Payload {
// The type of data in body.
PayloadType type = 1;
// Primary contents of payload.
bytes body = 2;
}
// A protobuf representation for grpc status. This is used by test
// clients to specify a status that the server should attempt to return.
message EchoStatus {
int32 code = 1;
string message = 2;
}
// Unary request.
message SimpleRequest {
// Desired payload type in the response from the server.
// If response_type is RANDOM, server randomly chooses one from other formats.
PayloadType response_type = 1;
// Desired payload size in the response from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
int32 response_size = 2;
// Optional input payload sent along with the request.
Payload payload = 3;
// Whether SimpleResponse should include username.
bool fill_username = 4;
// Whether SimpleResponse should include OAuth scope.
bool fill_oauth_scope = 5;
// Whether server should return a given status
EchoStatus response_status = 7;
}
// Unary response, as configured by the request.
message SimpleResponse {
// Payload to increase message size.
Payload payload = 1;
// The user the request came from, for verifying authentication was
// successful when the client expected it.
string username = 2;
// OAuth scope.
string oauth_scope = 3;
}
// Client-streaming request.
message StreamingInputCallRequest {
// Optional input payload sent along with the request.
Payload payload = 1;
// Not expecting any payload from the response.
}
// Client-streaming response.
message StreamingInputCallResponse {
// Aggregated size of payloads received from the client.
int32 aggregated_payload_size = 1;
}
// Configuration for a particular response.
message ResponseParameters {
// Desired payload sizes in responses from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
int32 size = 1;
// Desired interval between consecutive responses in the response stream in
// microseconds.
int32 interval_us = 2;
}
// Server-streaming request.
message StreamingOutputCallRequest {
// Desired payload type in the response from the server.
// If response_type is RANDOM, the payload from each response in the stream
// might be of different types. This is to simulate a mixed type of payload
// stream.
PayloadType response_type = 1;
// Configuration for each expected response message.
repeated ResponseParameters response_parameters = 2;
// Optional input payload sent along with the request.
Payload payload = 3;
// Whether server should return a given status
EchoStatus response_status = 7;
}
// Server-streaming response, as configured by the request and parameters.
message StreamingOutputCallResponse {
// Payload to increase response size.
Payload payload = 1;
}
// A simple service to test the various types of RPCs and experiment with
// performance with various types of payload.
service TestService {
// One empty request followed by one empty response.
rpc EmptyCall(Empty) returns (Empty);
// One request followed by one response.
// The server returns the client payload as-is.
rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
// One request followed by a sequence of responses (streamed download).
// The server returns the payload with client desired type and sizes.
rpc StreamingOutputCall(StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
// A sequence of requests followed by one response (streamed upload).
// The server returns the aggregated size of client payload as the result.
rpc StreamingInputCall(stream StreamingInputCallRequest)
returns (StreamingInputCallResponse);
// A sequence of requests with each request served by the server immediately.
// As one request could lead to multiple responses, this interface
// demonstrates the idea of full duplexing.
rpc FullDuplexCall(stream StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
// A sequence of requests followed by a sequence of responses.
// The server buffers all the client requests and then serves them in order. A
// stream of responses are returned to the client when the server starts with
// first request.
rpc HalfDuplexCall(stream StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
}
// A simple service NOT implemented at servers so clients can test for
// that case.
service UnimplementedService {
// A call that no server should implement
rpc UnimplementedCall(Empty) returns (Empty);
}

Binary file not shown.

View File

@@ -0,0 +1,257 @@
package testing
//go:generate protoc --go_out=plugins=grpc:./ test.proto
//go:generate protoc --descriptor_set_out=./test.protoset test.proto
//go:generate protoc --descriptor_set_out=./example.protoset --include_imports example.proto
import (
"io"
"strconv"
"time"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"github.com/fullstorydev/grpcurl"
)
// TestServer implements the TestService interface defined in example.proto.
type TestServer struct{}
// EmptyCall accepts one empty request and issues one empty response.
func (TestServer) EmptyCall(ctx context.Context, req *Empty) (*Empty, error) {
headers, trailers, failEarly, failLate := processMetadata(ctx)
grpc.SetHeader(ctx, headers)
grpc.SetTrailer(ctx, trailers)
if failEarly != codes.OK {
return nil, status.Error(failEarly, "fail")
}
if failLate != codes.OK {
return nil, status.Error(failLate, "fail")
}
return req, nil
}
// UnaryCall accepts one request and issues one response. The response includes
// the client's payload as-is.
func (TestServer) UnaryCall(ctx context.Context, req *SimpleRequest) (*SimpleResponse, error) {
headers, trailers, failEarly, failLate := processMetadata(ctx)
grpc.SetHeader(ctx, headers)
grpc.SetTrailer(ctx, trailers)
if failEarly != codes.OK {
return nil, status.Error(failEarly, "fail")
}
if failLate != codes.OK {
return nil, status.Error(failLate, "fail")
}
return &SimpleResponse{
Payload: req.Payload,
}, nil
}
// StreamingOutputCall accepts one request and issues a sequence of responses
// (streamed download). The server returns the payload with client desired type
// and sizes as specified in the request's ResponseParameters.
func (TestServer) StreamingOutputCall(req *StreamingOutputCallRequest, str TestService_StreamingOutputCallServer) error {
headers, trailers, failEarly, failLate := processMetadata(str.Context())
str.SetHeader(headers)
str.SetTrailer(trailers)
if failEarly != codes.OK {
return status.Error(failEarly, "fail")
}
rsp := &StreamingOutputCallResponse{Payload: &Payload{}}
for _, param := range req.ResponseParameters {
if str.Context().Err() != nil {
return str.Context().Err()
}
delayMicros := int64(param.GetIntervalUs()) * int64(time.Microsecond)
if delayMicros > 0 {
time.Sleep(time.Duration(delayMicros))
}
sz := int(param.GetSize())
buf := make([]byte, sz)
for i := 0; i < sz; i++ {
buf[i] = byte(i)
}
rsp.Payload.Type = req.ResponseType
rsp.Payload.Body = buf
if err := str.Send(rsp); err != nil {
return err
}
}
if failLate != codes.OK {
return status.Error(failLate, "fail")
}
return nil
}
// StreamingInputCall accepts a sequence of requests and issues one response
// (streamed upload). The server returns the aggregated size of client payloads
// as the result.
func (TestServer) StreamingInputCall(str TestService_StreamingInputCallServer) error {
headers, trailers, failEarly, failLate := processMetadata(str.Context())
str.SetHeader(headers)
str.SetTrailer(trailers)
if failEarly != codes.OK {
return status.Error(failEarly, "fail")
}
sz := 0
for {
if str.Context().Err() != nil {
return str.Context().Err()
}
if req, err := str.Recv(); err != nil {
if err == io.EOF {
break
}
return err
} else {
sz += len(req.Payload.Body)
}
}
if err := str.SendAndClose(&StreamingInputCallResponse{AggregatedPayloadSize: int32(sz)}); err != nil {
return err
}
if failLate != codes.OK {
return status.Error(failLate, "fail")
}
return nil
}
// FullDuplexCall accepts a sequence of requests with each request served by the
// server immediately. As one request could lead to multiple responses, this
// interface demonstrates the idea of full duplexing.
func (TestServer) FullDuplexCall(str TestService_FullDuplexCallServer) error {
headers, trailers, failEarly, failLate := processMetadata(str.Context())
str.SetHeader(headers)
str.SetTrailer(trailers)
if failEarly != codes.OK {
return status.Error(failEarly, "fail")
}
rsp := &StreamingOutputCallResponse{Payload: &Payload{}}
for {
if str.Context().Err() != nil {
return str.Context().Err()
}
req, err := str.Recv()
if err == io.EOF {
break
} else if err != nil {
return err
}
for _, param := range req.ResponseParameters {
sz := int(param.GetSize())
buf := make([]byte, sz)
for i := 0; i < sz; i++ {
buf[i] = byte(i)
}
rsp.Payload.Type = req.ResponseType
rsp.Payload.Body = buf
if err := str.Send(rsp); err != nil {
return err
}
}
}
if failLate != codes.OK {
return status.Error(failLate, "fail")
}
return nil
}
// HalfDuplexCall accepts a sequence of requests and issues a sequence of
// responses. The server buffers all the client requests and then serves them
// in order. A stream of responses is returned to the client once the client
// half-closes the stream.
func (TestServer) HalfDuplexCall(str TestService_HalfDuplexCallServer) error {
headers, trailers, failEarly, failLate := processMetadata(str.Context())
str.SetHeader(headers)
str.SetTrailer(trailers)
if failEarly != codes.OK {
return status.Error(failEarly, "fail")
}
var reqs []*StreamingOutputCallRequest
for {
if str.Context().Err() != nil {
return str.Context().Err()
}
if req, err := str.Recv(); err != nil {
if err == io.EOF {
break
}
return err
} else {
reqs = append(reqs, req)
}
}
rsp := &StreamingOutputCallResponse{}
for _, req := range reqs {
rsp.Payload = req.Payload
if err := str.Send(rsp); err != nil {
return err
}
}
if failLate != codes.OK {
return status.Error(failLate, "fail")
}
return nil
}
const (
// MetadataReplyHeaders is a request header that contains values that will
// be echoed back to the client as response headers. The format of the value
// is "key: val". To have the server reply with more than one response
// header, supply multiple values in request metadata.
MetadataReplyHeaders = "reply-with-headers"
// MetadataReplyTrailers is a request header that contains values that will
// be echoed back to the client as response trailers. Its format its the
// same as MetadataReplyHeaders.
MetadataReplyTrailers = "reply-with-trailers"
// MetadataFailEarly is a request header that, if present and not zero,
// indicates that the RPC should fail immediately with that code.
MetadataFailEarly = "fail-early"
// MetadataFailLate is a request header that, if present and not zero,
// indicates that the RPC should fail at the end with that code. This is
// different from MetadataFailEarly only for streaming calls. An early
// failure means the call to fail before any request stream is read or any
// response stream is generated. A late failure means the entire request and
// response streams will be consumed/processed and only then will the error
// code be sent.
MetadataFailLate = "fail-late"
)
func processMetadata(ctx context.Context) (metadata.MD, metadata.MD, codes.Code, codes.Code) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, nil, codes.OK, codes.OK
}
return grpcurl.MetadataFromHeaders(md[MetadataReplyHeaders]),
grpcurl.MetadataFromHeaders(md[MetadataReplyTrailers]),
toCode(md[MetadataFailEarly]),
toCode(md[MetadataFailLate])
}
func toCode(vals []string) codes.Code {
if len(vals) == 0 {
return codes.OK
}
i, err := strconv.Atoi(vals[len(vals)-1])
if err != nil {
return codes.Code(i)
}
return codes.Code(i)
}
var _ TestServiceServer = TestServer{}

View File

@@ -0,0 +1,16 @@
-----BEGIN X509 CRL-----
MIICfDBmAgEBMA0GCSqGSIb3DQEBCwUAMA0xCzAJBgNVBAMTAmNhFw0xNzA4MjUx
NTQ1NTNaFw0yNzA4MjUxNTQ1NTNaMACgIzAhMB8GA1UdIwQYMBaAFM0FLuuYBwuA
J+tocRlu+xUuOw6FMA0GCSqGSIb3DQEBCwUAA4ICAQCcN8WJKbvGrunXgRBjSnsM
j/sejaX3CCZPmrXeditekSNMatO0JDXOjyoEvv7s9aZrAf3eFOU3Vr5N7PlbLRdj
tovuKTeVp3ungqMoT70cFEf/7eMlpWMB2GkfpV9LtF5Tb8dOYT3kllqtMKv4TeZo
2adu+GXdeQsqlz9fDEi0ZV4RBruuO0QyLWXpNrUB6fznUDfE4KVBsAIadjsg+Aew
6jeTkYuUILWMwBM6MzOG/InTKqXpe4ghMufI9fO+phxY10gz4QQ44ZNOa18OuiJw
IH8MoKzhrgUAPLs135hpdGbDePVw5SIKMHUAU2UEKtozAMVfCW45MZHREDdMV3NA
w5QWDoBYl4jol08Orbccmhu4fbauXmB5Id4IPVgGEGFPpiH/QVyJgZIv1AD2dlRg
Td26iz9I25hyrpEfF1gJMtOsDOklDsUiMo8ncQ3CL+pkKnMjhm54k6OFe0qlGsdO
KSavNlEmW/F9h/gs5kaLeFv0v4JxLh12TY28pCE60yoB/UkuB1a+VTHcP3Fa6uUC
uyv0T0f5yHujaM1isGjI3XGgVgLyJiFxtKMPsMEwRrsrEafqp7JCeLpnIWt1J0C/
Zz3roGcCGj86Oq5zUjdguHS6Ra+uaX+IMJGohWq1cndzVzNfUNkRIyl8IdkCv9o3
J8fVwBzN6sAu6zWd3BqT4A==
-----END X509 CRL-----

View File

@@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIE2jCCAsKgAwIBAgIBATANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDEwJjYTAe
Fw0xNzA4MjUxNTQ1NTJaFw0yNzA4MjUxNTQ1NTNaMA0xCzAJBgNVBAMTAmNhMIIC
IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnjw7iZyn9EtjtK7zT+M59OxS
J43a3kMm11Vnh1Fw8oQ7tH0kQW6COyAwlBhAzWGtfDC6jG7A2n+8mPWoinsxoLA5
viSZrEkWZ4tGxWWZ5y/xWh5NBrHa3Jsg1rZ5dstAl08gJPwl0v32aYkMdtk16K0k
jtdPpKtO98v1N6ea7fvQKdyrAaUHY/BY+onoqmSBGPX6vVV7FGybWDf72J5vcwyA
07wdaZ7TrUYbD28nhXuc4Dt2KbZbWvkT4OYZ+4c+eiehRFVGtuXXi0cKG0xF516b
DnrSO1/2ZbIB2xmcgKvcay0jLzWqhnsSc+qTqODVLODQAMvrMlY0RUXQPn/WTfAw
aQ+u/j7qIR1KFZcLn7uVq8bCM9g+VeyLjq+6XUvgGL9KhvrR/FA4R4DUka+ZVQqh
s262Qs7pNFdoIIrTsJyPd7/UYWCcQbkCKw0aRoUfBeZgkg4bylcABygZqY9+apRF
NBEhpycAEvWFarr6rosqII9kLm1LpnPNEgSvQ/CIRIHq5z5iKeSvHFYOVxLS44HH
M16Mry5UF/jW7Vg8JY5Jrg5YwyOhdGoOSJ3+c/pbLq1TRkwK9bwEJwGr7FdkrjPZ
uNJ7HQiFn2IaeLlbtzwJ+q3vGSFEjlujOigwUJVzXz16Q93vYIuK3FcyDuOMzpwW
HHgSLH/+rdtd+7hoLwMCAwEAAaNFMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB
/wQIMAYBAf8CAQAwHQYDVR0OBBYEFM0FLuuYBwuAJ+tocRlu+xUuOw6FMA0GCSqG
SIb3DQEBCwUAA4ICAQBLNuP8ovTgFcYf7Ydgc+aDB2v+qMYVIqJMrnp8DuYy20yv
64jYcIxh35IbQWOZxZshJsebRKM9vr6huEo2c/SuHLQ5HZGPxSt++aG+iY4Y1zL5
KHtG558lK4S5VsXymMkUjGZtm+ZuJida9ZcV+jz/kePMHpErWPeMvH2jDmD4mWgA
YdjipD4cxEn+9O3lBSCkeSjaAd5rQeD9XomV4a2/uL4Y7RDbn9BNt+jdLvfu2pmo
O1zcp0f578oFlUIg0H9fb6YzL3MKOXiuh7KE1/W9el5zsN/kLlyWFbopN34A6PlO
ZHEvZZcQW06bmy2FRWgqkqWMqBwzWk7JKGp+ozv8IBvimhgjNun068FQAZV9nfKU
6U728P6T1USDhgwtpX7/2IaukXcmO2FE9XzKZyYAbmAcOhPLzFO4pdwapU2lPbFE
l2HLkYaHLXzMxB30kQQHW2l8+8xr+MAa+bBcD9Jaxaz/t3ZpLt62/1nxT7SWNwH4
Sa83BaG3EHBotlBc18hqrFWEKR4KYenqY8xa7kblDI0rXqlXBblUXp0TwIctOmzR
coqR8q6/R4VXhD9FZBIW1/uX2KKEPfTM46aQdaTtdzjd3UzwTP0SRwkvZ4oFftW6
s1GljfCGsrOpi6O/Uy/IVTE7Xn/oVnlJvGbaP+AHexLytBiBVUBukLBwvpJ8bg==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
MIIJKAIBAAKCAgEAnjw7iZyn9EtjtK7zT+M59OxSJ43a3kMm11Vnh1Fw8oQ7tH0k
QW6COyAwlBhAzWGtfDC6jG7A2n+8mPWoinsxoLA5viSZrEkWZ4tGxWWZ5y/xWh5N
BrHa3Jsg1rZ5dstAl08gJPwl0v32aYkMdtk16K0kjtdPpKtO98v1N6ea7fvQKdyr
AaUHY/BY+onoqmSBGPX6vVV7FGybWDf72J5vcwyA07wdaZ7TrUYbD28nhXuc4Dt2
KbZbWvkT4OYZ+4c+eiehRFVGtuXXi0cKG0xF516bDnrSO1/2ZbIB2xmcgKvcay0j
LzWqhnsSc+qTqODVLODQAMvrMlY0RUXQPn/WTfAwaQ+u/j7qIR1KFZcLn7uVq8bC
M9g+VeyLjq+6XUvgGL9KhvrR/FA4R4DUka+ZVQqhs262Qs7pNFdoIIrTsJyPd7/U
YWCcQbkCKw0aRoUfBeZgkg4bylcABygZqY9+apRFNBEhpycAEvWFarr6rosqII9k
Lm1LpnPNEgSvQ/CIRIHq5z5iKeSvHFYOVxLS44HHM16Mry5UF/jW7Vg8JY5Jrg5Y
wyOhdGoOSJ3+c/pbLq1TRkwK9bwEJwGr7FdkrjPZuNJ7HQiFn2IaeLlbtzwJ+q3v
GSFEjlujOigwUJVzXz16Q93vYIuK3FcyDuOMzpwWHHgSLH/+rdtd+7hoLwMCAwEA
AQKCAgBvitoVWX7zsKkqVyFhMTZLtsL66v5cK04YATYnp3tNGXXU91o1Xacj8r8L
xkT4AmD+6IK4N+JupBjYYmNaqxkCwvcRWE+TqTnH5+ANil+BHsSt2CpIC9vSIvB1
KtBYs1Jm1vo72Br5rtii8F7+8IMV7+eTYafc1n2mI/pKLzYBiL7mo41QbXrWMjkm
80w1wP9YDx2flcBbV2vyNhSsUJMTsL6ngzXgnHtu67prmNltOQQO9RuIr+maKXaf
1NSAAIhEJ+eAefSNPVxB6+Pt9khYntIC1QWZoT3Z1i+EuXsfIQcR7hGdV+FLRzps
x/Eq3MKpDhjSVu0G4MmcA2iWhhsUXbOihpXKWnAdLv0cUP0tbbxcUsEa4igAruBW
n6+hYrVkbD5sZuGoMdzKvnGkqRTf/ragdcFlfty7KaAUBFr5V0fzsaW53+/5zG5o
eSRoyCNLQSbBh2TVVpnNIbXELuYmwoDvnPNup4G7ITFsUQZEW2Tm3LHQt7EAi/wn
hJU/21rI46ubB3r5wJZ+6JOK4PiqPeSIokyVfFyPk2ny4LT7ne5MtPHF+wAVAOYj
0wcLEyh2s1b0VSlko6GnPjbLi8eAtOAk2ggVK5GofnkhsPohA+yoNvcDDLbcdQ8v
9Q/nbL2dENce6HZEBSi5RlElU9+BbOJc4qFM2o9mzrWuOpHRsQKCAQEA0j+OhUag
qbu7tNcWvE8w0I9vt1CXuZrS2ypKpuaaMYknb2Lo4YtuV9+bHx1isGHnAc2RAbUB
23mLANhquRIOo7u3NTvtRsvyzrRuuFviQZ5p1b8MHfqpg+/mA+yve8J3zyMMNC5f
c7m/13J+dsQNf/WWhqbnWU3wOoRa0NQBtOw7UhVFl+1JeBfSiVmYQXH1n5VnOWs8
Vab5kGkeYpUssgMFJG07qgdX5Ux3KzAQm7Onvsn4tT5UMtHt67f/wWzlP/xcoBJW
67clhCuO2Jiojo4jSNko0PGgTmFPmF3EOW+zd+iEYP3LF+S4uTR7yz4+foUNa9e2
xf6XwMp3ymjbfwKCAQEAwKsnMt0KErZ1/iRbN6U5Rcgxcq6FxRivXyzQX/3QrWc0
r6H4WWk5+gwy/Fb1CpQyiJkXG7PpVysdWaWF5S3NRVL3Ixuyp1R10DmPYch/4Pn6
4BD9UgKUhS2nxBVcMyyM/mN0W1Img22tCaJhaI+/raYf61JxgWmUJmUq8k8Xzgfv
ndEYQGgf62jG35aopkqfwiC8+rApgbiLoN1mGiusyJUcZmYLLYp2ao/xHc7UtjMP
N5tQeE0aZgSaBBwDAMQxMdWovo5qThvpJdy8q8EVq6sCO1G1MzLzIMxd/4asVzLc
wUHSG/8c9qdgxBGGhYAbTSVegWTaqznDrlFB8RP+fQKCAQBOx+vyeqWHFEZgm9v0
EcRb0fNtgDBqJt5tqyov4ebTOu5g6XIT2XguSyZIAW3SY8z4uvtj5VxdzexNE8rh
sCd2KMeclejyB0fjNm7qe9uK9P35Ts4OibdtLb5FqDGVMShNoHdZMisoJOkCpO9I
N2xLj02pBO9ZYj/q3V9eMqK1FXOg7UGXjR1jd6G3P7Ayja4Y7xWvyUPhYGDRQOJW
1EjcJw+NN7UMoBXKYN2ifC8s+KOZdPrRhxprtIfvNJIL+27ni/t1K4oQZx8SqHOt
K361dAM6r8yAhpmn5QS7Nh9p2jYobyLzaQXp3RVuqIDehmNKaza9OyZMiHp6jiNW
3/WnAoIBAQCTeK3ZRc03A4gPDd7wGbxbyF7o6+KiOUHKtK+OOeWnRI7UPEKulVd2
KC5CbYDEJykC20MPxka9nNerTYHOKJ+tB1L5AXNelsxSpCw2aVRQbKb1KKvtQOJT
id2Wvc7DsL7+3DssxxWJlcJT1IGAmj7Z+IUIByOwLZLjTJ5xt859uh9Tib9pVQnR
k3Jdo6DVH9tmqM5dh8dNbmcZqz1CnNl08oU5b7PwmMII0MJ60VyJVU25f105p7Kk
EbOdn59Az+rjvSmbKcD+pmhvvaSARpuCubNMmj76wG3OVf9A3eE+IUVNe0cKfNu7
g+QST2PK/YJoK0lJ+1tQojdATxwNHgO1AoIBAHjpZ9Px6L3Ek6O3ZxRfuDTkmoVB
APmvm4IcvajB0BPd1mdcf4sWYmGhNqf+xajwsKB8jIkd4LYfINjfIZOdnYgq0oQY
cM7K4+b8gkLKsIV2gFI4b95TYcbmanxdTDdbERGTJPsIBajXO5XapAswAJfllSDH
pUvLb2CUgLhMhR9SFZAjyRo0HV++jMqxWJKzhlTOkoPzBY5xAleft+hzVch3WuvP
zZn/NrpzTEpslV7dZ05Wuh8E+vJMoQNCReGlmAwNlrt/vxDuyv6ibNPxBHax82On
yo6EP59d7OE0951FruUIITUgzKG2jIqeR/e5Yb0LJusXnj4RPuvfRULFD00=
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIEGjCCAgKgAwIBAgIRAPt0KCF12GYbCoUj7klj5/AwDQYJKoZIhvcNAQELBQAw
DTELMAkGA1UEAxMCY2EwHhcNMTcwODI1MTU0NTUzWhcNMjcwODI1MTU0NTUyWjAR
MQ8wDQYDVQQDEwZjbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQC1JxEPOsZyf8883tlPBEajotyECtrYMZ48FsYEmQ1XvKPoH3eb7+Ev7tRBVAup
yB87XQ5PU/oNqAtpo/6WD5JGnKSVs+EAMESXmzEF04T9hK8uSd0cVEEkd0tbVNpX
bWMbivHnx5Vp8o2mIx0sVrgGsJW3t+cYbNTp3bOTdmz7LKbiQN2Ix0wH+2/sPXYa
cZsgbI0Ydo9KnqykPm2TqBYCL1kzhGlvaAotjdDIm7OgnaGCFe4CbK4QZB4uFw3e
M+PmLG0TsaH9CT/ZRrE21iBfg0rqgpKZKMcqYQftXdLqlikuV69F+0L84xRfeVqB
1E4j0RwBGWW8EwY4WHK3VE25AgMBAAGjcTBvMA4GA1UdDwEB/wQEAwIDuDAdBgNV
HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFMs+/QF/ZJaRu8Wv
vcaMC7jGmPwxMB8GA1UdIwQYMBaAFM0FLuuYBwuAJ+tocRlu+xUuOw6FMA0GCSqG
SIb3DQEBCwUAA4ICAQB0gPDs86Rjy/O2+l8QyaYfwmmyTMPjNVqKgVP1uuikWErN
5hTAlwtDI9FuiMFBqeBdeiT8IQvzEEQPYu69kAX2XYBWBMWDa85co5fJztAzV7Yz
VL1byhxd2jgM14usyx6PbzkhYKBNesujHj7wQ0ur+85Kp66HqKCuNCvbj0zv58PH
RWkojRPgyTpbLdXXCOWJXp62XfddL1Bf7NJCW5QTyHoHoOsOeoPajb4OOmQehzqv
b9FPAHVFBPrU53Xn1CURAzTeBQ2T/OK4nx6EdQgxP9+VVurBQ9N2YBM9VEJmfQK8
Lf5/+EJHe5ctOy1Xm4A3A52zZ1kGjfvWUtGJUSnJ5ahhMm6Dx63wk7oYNCTXnPup
aVtINWygNlS/dQsWubHaWSFwB9/QwK074+H/4EpDq9HCMMl8yPMktOmv69Hyaju3
MvGshz/DLNZf9oYpO+lbU8X124Z6XifEztMiBlUPW75KYv9X4CTbKTdE45QaRMiO
ZXcH4HE1/iQ9IOGg7CplMlMcHg+lQ7CpXQjtUUjCEpkj8BAs8YLDodLnjigs56/8
75+3cVZu0+dY+9eNt/EIqzjaFwEx72hbLyhk2IeS++7QloJDhYqlq+ng54Ul957A
8e7TsSVHlLZVGXw8yqjyxxOwWaFx6mvFzGrcBtvCgK2HwEiYQ9qXJ5VPkdo42w==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICVjCCAT4CAQAwETEPMA0GA1UEAxMGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAtScRDzrGcn/PPN7ZTwRGo6LchAra2DGePBbGBJkNV7yj
6B93m+/hL+7UQVQLqcgfO10OT1P6DagLaaP+lg+SRpyklbPhADBEl5sxBdOE/YSv
LkndHFRBJHdLW1TaV21jG4rx58eVafKNpiMdLFa4BrCVt7fnGGzU6d2zk3Zs+yym
4kDdiMdMB/tv7D12GnGbIGyNGHaPSp6spD5tk6gWAi9ZM4Rpb2gKLY3QyJuzoJ2h
ghXuAmyuEGQeLhcN3jPj5ixtE7Gh/Qk/2UaxNtYgX4NK6oKSmSjHKmEH7V3S6pYp
LlevRftC/OMUX3lagdROI9EcARllvBMGOFhyt1RNuQIDAQABoAAwDQYJKoZIhvcN
AQELBQADggEBAFnxmVCuM3J2bt79JcFOqsXNsvGUUT+4kMl3BcfSWaf1pviuhiXT
fsKkk1WItvaRQvpNdQoFQDjKHGcd6+0vCz+Q6Nni2Vniz3+f3+h/rOzWGA656Xxm
lgByryixnngWZBNLZkLWCz/H1MAlQYu8PTdy0N+JBF/E5SAGfaaXtfTC6tjnnZIm
3rjxC7C3EyELpo3X3erTcHpnFvhl6ZSkViVWfhOjxU0n+TGGohczesbHZc8YC37y
JrkrnRDrNKnca1XkXWUnbV6rH8cVDnJ0Fvs54RI686Tlv+LxW2xa3D2+pV7Koduj
Ru+PguJ3BbaRpieGTxHg7hH/1T5HsZnD2E0=
-----END CERTIFICATE REQUEST-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAtScRDzrGcn/PPN7ZTwRGo6LchAra2DGePBbGBJkNV7yj6B93
m+/hL+7UQVQLqcgfO10OT1P6DagLaaP+lg+SRpyklbPhADBEl5sxBdOE/YSvLknd
HFRBJHdLW1TaV21jG4rx58eVafKNpiMdLFa4BrCVt7fnGGzU6d2zk3Zs+yym4kDd
iMdMB/tv7D12GnGbIGyNGHaPSp6spD5tk6gWAi9ZM4Rpb2gKLY3QyJuzoJ2hghXu
AmyuEGQeLhcN3jPj5ixtE7Gh/Qk/2UaxNtYgX4NK6oKSmSjHKmEH7V3S6pYpLlev
RftC/OMUX3lagdROI9EcARllvBMGOFhyt1RNuQIDAQABAoIBACE5jRNyADu33VaY
uNqZOiuBD1jYdNL6Jr92ndLyD1RsMNO+Eb3z/SVBdISW2ZzGK5RDuQArss0WaSFz
BpqXOIji6fzbBQV31NzJhfA/n0CwOUEQIxGzEk+R4axan8ExOuAuV7ffDzRjXD+A
aTVcolv3vz326Ne9/j72fp0pN0vJ0b8mk1xmDWNOHhfoWmIGrUZAjqAkA1kh5aLk
Q8MCjVyjT+KYDkFT6NscFVxKslDVhb2OFC7oy+9l/hBru12bsi9eBdYpPT9E1cpR
U9N8+9XS9d7wgVnmVh8CIrFToLsvSrwD8SG0Indot0C6dsy0PkoMUekVxvM5/wXm
YLZnZEECgYEA28JfZxFxO+bjd+zBC+yrusHCVfZK6MZZV0u/V82Bn+gftwgxagI7
q2h7m56WBtq9MeLlhicZ+em4BtA3yHwVGhqr+d5CaXjkYype1EditqGx8JYRUwlG
9z7W6jCEDJsjrzvGgua1qsyCZFePG78i4rLumK7UVvWEK/OaiZu3Pr0CgYEA0wbT
3STBc4THLXR8nx39b6RP+qH8jO9UcD/V7Hi/SWTCcGB8IIlTV2EJVndKHPregcmI
dN61uH3d+3UtI/WxEPMcfrSlEwVrjF2m5szYjLIAeFynw7pQY95qIhgKi6OH0Yn6
9OCmieL0x1ez5zOXiv+GVjmn9tDCxXvqfsW9CK0CgYEApOd0Y4kpKUQWyPT135bX
PqsKwyqwB4BfpiwHB0IE1ROASP5y5hOK5xLePmaAOeCGPBsBFOveiDQjjalNUroZ
s570EeoAd9jpuKggxLZUkqs/NUPG+EJr6DhVWSLS1ArOej4mti+dfu87oUQ69R02
dlrCw/vdBuvxJHIGMuCQXxkCgYEAinSFVygBgQCSCkHObjuoB7LgAsp7QCDa3tcT
TZafstDYPhEf/9z6AG+bR872onL6wF7xF/Tzd7ulhJGJ73kJFtzbSkrNr+AzgyID
GpU2U4GKi24HaIT6r7vDGOF7Mck2mIWWUUqAGiH9hjkFwWD5QeqLQlGL4YVw9U9r
OIgWkfUCgYAoMub8wHJe9zhq7UCBCa4zPXqWVQcN7ANjL6fESvyK+A6TfwL5j782
CNIVl8ewU9TthEY+AdbJeiAevz+pIazSqi0ln1JKO5YpCOC1Y0UxcEpghplBTlPU
yoQyTJP81iLynwOM4pC322ptJISXIndL7Ig/9AoRZtAJV4Ot6z9b1Q==
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEODCCAiCgAwIBAgIQP6vqM4GEKFdwrZvr/s/5KTANBgkqhkiG9w0BAQsFADAN
MQswCQYDVQQDEwJjYTAeFw0xNzA4MjUxNTQ1NThaFw0xNzA4MjUxNTQ1NThaMBIx
EDAOBgNVBAMTB2V4cGlyZWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQDDlGu08Ye7IGMnh6zyiVbW64btT2BUfj4fZlyTraLNzVPfULOXezgJRAwAXEnY
eYA5j6e7iAO/yGXq+Pxh9rbGlXTVC2BsPIfKzTOcCTxgO4tfrdfSWFRDGgcFtpAA
/4b8fYQ8BMw7khVpOqj9n8TyqsN8DGyZTyNhQuc23zmmY1eVYtDIwVvVBp8/YzDT
3RzO+FyoRpKgdngX6kdqqZ+vaPxP078JM5zAMnysjSkpQCdAdu4EupvA5xRtOrDt
jJoAi4iUf4sG20RiF9XFpiDkyUiOa0ysFC4wrWbXkaT1xHZJJPwwJZ8VyGDRFlFa
POEaM9dtLPKtEV2af3G9Q9s/AgMBAAGjgY4wgYswDgYDVR0PAQH/BAQDAgO4MB0G
A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUNX8nKbJ+G0bv
k88bTHkULUX1U5AwHwYDVR0jBBgwFoAUzQUu65gHC4An62hxGW77FS47DoUwGgYD
VR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEBCwUAA4ICAQBHZsxC
GGOUhNi1b0xUYmKwFaRP641R69NydzVMma+2rpLZtL8MQq7lWAgWGE8EiKfTewk8
2Gm+rNwReBRkw56zka8WtLomO4GKKJHvfRsbSempsp9MShc3vcsEtzXGjDQi9c4Y
BJjWbh666jCKPRJ09RewcTyTYPtZ96lVeYRj9HhXF1EGnURG5sM/zCantlZXxInk
1FIOKDuawMbgf3GR6n/bM2oybYCs4Skv3yOp8x3lyhlJ/zmSPGVVkPa7Vki5+sPh
/eIZJ9mzEXsu7IXfg1isZ01iB28+6UgpZt/3017PvgopiYrL0gMKO3EL8UqJDweN
GzJh5VYOqsjTbsrYYsWGqM66vJmfPvqDyYA1jj/EH+q6TBz0s99rzF44bBKKie6T
0KrZT7ohXQ18Vhl2UpqahMqxMeAW/QP5asGuzS5EalUir5mNcOljtq1NnfmYvqok
aDC7rURoANwZ2L1oKN0oB6jn36g791pFdKycw+HdsrGgQGPOMak9P32z5kmDsODH
6aVrfio2WwSGg+1CIBH0QsclHAUgLsAXjyGbRWxPsvMcsLB6OOymuTP2UfriT05X
duabvbEP5IIRehVUfrP5uvoo29xnoPL4UB0C8gwr21IDn7Zew5/ALekN+s6IgsfL
9yKTGSD+6Ir3NqBgL8T+uhOAekyLE5S4CcwCHw==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIChDCCAWwCAQAwEjEQMA4GA1UEAxMHZXhwaXJlZDCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAMOUa7Txh7sgYyeHrPKJVtbrhu1PYFR+Ph9mXJOtos3N
U99Qs5d7OAlEDABcSdh5gDmPp7uIA7/IZer4/GH2tsaVdNULYGw8h8rNM5wJPGA7
i1+t19JYVEMaBwW2kAD/hvx9hDwEzDuSFWk6qP2fxPKqw3wMbJlPI2FC5zbfOaZj
V5Vi0MjBW9UGnz9jMNPdHM74XKhGkqB2eBfqR2qpn69o/E/TvwkznMAyfKyNKSlA
J0B27gS6m8DnFG06sO2MmgCLiJR/iwbbRGIX1cWmIOTJSI5rTKwULjCtZteRpPXE
dkkk/DAlnxXIYNEWUVo84Roz120s8q0RXZp/cb1D2z8CAwEAAaAtMCsGCSqGSIb3
DQEJDjEeMBwwGgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEB
CwUAA4IBAQA0GTIB6PxgmHBa234rSYqIew4qRfY9MeUkVQEFRDwodqxa+LWvZx2T
5JmTZYyXBfQwnSye18fDjQuHv1KaI7bnJuMRv9KU8L6ynLkAqrFWRSBjt3eCum01
IWZFyWu+dUN2c12C79zUQh8uZc15oDNFrD8ivBbGRpWvR1CSG/DH52kJ8nckgEsh
SwxbzSPOXBgLH6ke5z9QGHJMK2rhRFutFOecAId7VBiWqfZJv15+P2ZcyJNQGThs
V2sT974YGFkc0Y1MWlCgi3XhyQzIzqV1tEILGSTDE8biuKlm1nX3H0K8oI3hoGcM
CBjE3HQ1/rs10IY/WvkpIfAU71D/ExMc
-----END CERTIFICATE REQUEST-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAw5RrtPGHuyBjJ4es8olW1uuG7U9gVH4+H2Zck62izc1T31Cz
l3s4CUQMAFxJ2HmAOY+nu4gDv8hl6vj8Yfa2xpV01QtgbDyHys0znAk8YDuLX63X
0lhUQxoHBbaQAP+G/H2EPATMO5IVaTqo/Z/E8qrDfAxsmU8jYULnNt85pmNXlWLQ
yMFb1QafP2Mw090czvhcqEaSoHZ4F+pHaqmfr2j8T9O/CTOcwDJ8rI0pKUAnQHbu
BLqbwOcUbTqw7YyaAIuIlH+LBttEYhfVxaYg5MlIjmtMrBQuMK1m15Gk9cR2SST8
MCWfFchg0RZRWjzhGjPXbSzyrRFdmn9xvUPbPwIDAQABAoIBADa1IZuvpCP330SD
cyE0wZHEuC1RcsSvu3jVDThR7aRbtwZUcKgC053j5ueC6TUgZ3mycVzHoyTWTYv4
scBFXsMVs2SUlhgwpltYIwOWocjZXxcYbbJs+sT6VtSGSKm+0Gd4RLD1NpvDNTIG
MpcfRdwLYDsmzonj1SWzrTFwJ5Qe33cHSR8Oi9OarrxzIHWLDNgp8x+5j2l0T3AG
RPQMXj7jaK6qEdHGD6geg+ButeyjYyxu64l8Ooax11/jfdYHcK+BmmtmP3Bd/6FQ
DCtrXTBv5Txl/T6D/6OsyabSlwGWFDNEAaS47hKCFiYJBR+Az6r7d1QFBIzuqF0+
T7EwaKECgYEA/tUMcTXnBRquAtQHW4cSvHH2qoZAhGjXCmj/pk4UCOZDT1DP/K5u
m7tOZB4RJBmzg0a1QS89aJ6lhY0Oy1Y9MZTM6ofkBKd8+P0Rgo04UDVeQ8sA9c8x
4bFOrOEqe7NoK7Mwxyn5NOmNi/wxy+tpiMH4Jt3y4TLDEZPg0Tsfu90CgYEAxHnc
fN3gKeY3SV6nHi+johBirSNazhr4n0fx/TCsy02e5Rrn0rksGVfPij5Y2g8HQLel
hdbG7tVyA7UtGgVjwiT+4j0VXmWpCIqfPsibRoy29oPqO4p5pULsz+ueb4biRW2Q
tdLqS4OldM8YUFwkS7k3Pp13SAY+Ir9rHL6wv8sCgYEA/AZaYtCrZMnpFNT7XdLt
fb+78xQJVKqXGi2TwLbxa4fHQ/cpa75bl9scAToXO7vLZOaWNhxxQDm+e6Fw4zqs
FJAURVMV+GBo4ZrvKU1fRzwwuR1ZGsHKlGoV5DZgHKznNmjmseJaG7FsEujdms58
tgsXz+Cr53qbn5O/wU4W6WUCgYBA16sB9sPtcBIc/8UNvFE3wkqes4VbciFNiBQA
KJlOe26OVCPgMsawEn/nMw5l4QHWxQU2t5xt5Dm9qYSaCt9Sip0oE1rDDbAMpptJ
wDEmxnf3wa+DOP9OoFjBghSG4DA7E57nsxUqGOd5NoPiuZYs+5KU8qkUNyM4mo4C
LZjtowKBgCWuDPr8MBL1S3ym55VTNUWcuMKUHZkMg0HjRpi8ABXeARoM5FAWykbk
hFnI/Waj6EHNGoVVpKttJldP+uRRA+S3wZYKH4gRQcsWI/BVdiBkZgoTG7cwnixs
CqMQ4p6In1Q/6EafPqkVQOY1abNKQ7ZGbhksB/fVbf37XfOnDj3H
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEODCCAiCgAwIBAgIRAMcIOg3oRf7n7mR1BUqNnlcwDQYJKoZIhvcNAQELBQAw
DTELMAkGA1UEAxMCY2EwHhcNMTcwODI1MTU0NTUzWhcNMjcwODI1MTU0NTUyWjAQ
MQ4wDAYDVQQDEwVvdGhlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AM4lPXHK6YV7GFMeSsHKVq33LDn8Zt0IkY5pGNCuuksPa3vYBFRkwTrrobKQDnmw
jLeaQISyxyelhT4aIU34OQpGjOmCzceYPG0uOcecq7noVvbgw0UeQHkEL0p4NOlb
9LMNVhKTgQUhZC5mYSIpXNxlE/LlGmqrFX/8peSaJ1oAkkB5FYN0gAbYhd8kpJX3
9Nr2A+f88oicSX5K0L73LUFUrxoTcrFP1pWnFgg28vLvvzrW/VZtq4qJPl3GTLbM
xHOu000LaHK8TgIJMCQUalh2q2nz+Htmtv+g/b27YEMGcDBW1qBX9oMKgoM/Z1Hv
zjVhAm9I649heDUguFXCaoMCAwEAAaOBjzCBjDAOBgNVHQ8BAf8EBAMCA7gwHQYD
VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBSUR1DE/STLJwf2
hW4qXPrOSLqlmDAfBgNVHSMEGDAWgBTNBS7rmAcLgCfraHEZbvsVLjsOhTAbBgNV
HREEFDASggpmb29iYXIuY29thwQBAgMEMA0GCSqGSIb3DQEBCwUAA4ICAQBG87BU
UuDuUCnvcwUNbga8fhe1PR6z2jueKQiI10SxkYG/g6PMQGGYDNO9DZFKu9l/TMTf
LnuEozN+Csig+wC+sc32/MdR35XTmkKtNhL0cVgvKP0Q6zNk97/QJErLtDpYb9VR
Gm2Ky6FGDp+/EEUvQUKRpGBmWIqOtjxqQu8lLoJlt/TPhxJ0lGDd3c8WwaVFYTbS
isBKdHpS2hkn/O1Yd4QtNk06pCpUDQuPumUOBoa+dK3y0jZ+e34h1NoR5EZvfRJy
p3n7CLD2eZSNc4oWKb67X0RDao5LD0b51crjgsFYHhCTS+Mgh0YkgukQZ8uBKpUJ
IBhz2Nr1QXykrJUhal9MrKukjczEikGxzK1VsDgxYY1kLBURhM9/TfvICmcAaQqv
MF9B78lnoJiPZZxD+a5N9MawzN6QBqX8GpvhZoAnj6iAuNwKJVyENpZqravxeq2o
buNjgQ+SmfqxQDfMD3lu95yidqD7bcDipJsXEPQzdBjZ1JOJCGi2eiAm50e6bq94
CMKmmRjtIbF1hJnHeEFPvXqdPpqcyEvcaDebph/f+54wubTgwFI3VMnhhlv2EPIe
rwcbZV3kNpUZWXAZVzYlQcbK+9US8PocOUzmqzA7ZZRO+rCWNxahHjdgrMK3fG6r
WudSHHXawj3dkPeWrQde6SILSK/myhGLdjg1fg==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICgzCCAWsCAQAwEDEOMAwGA1UEAxMFb3RoZXIwggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQDOJT1xyumFexhTHkrBylat9yw5/GbdCJGOaRjQrrpLD2t7
2ARUZME666GykA55sIy3mkCEsscnpYU+GiFN+DkKRozpgs3HmDxtLjnHnKu56Fb2
4MNFHkB5BC9KeDTpW/SzDVYSk4EFIWQuZmEiKVzcZRPy5RpqqxV//KXkmidaAJJA
eRWDdIAG2IXfJKSV9/Ta9gPn/PKInEl+StC+9y1BVK8aE3KxT9aVpxYINvLy7786
1v1WbauKiT5dxky2zMRzrtNNC2hyvE4CCTAkFGpYdqtp8/h7Zrb/oP29u2BDBnAw
VtagV/aDCoKDP2dR7841YQJvSOuPYXg1ILhVwmqDAgMBAAGgLjAsBgkqhkiG9w0B
CQ4xHzAdMBsGA1UdEQQUMBKCCmZvb2Jhci5jb22HBAECAwQwDQYJKoZIhvcNAQEL
BQADggEBABuaL2t2Zcv9R72OH93EpIzgExL37odLUiIjTIIykK2TT/gb1LtnE1WK
THdqaLpnPot9IqBofppfkXPMrG7vavJoPlAp0lU2FHYIz64PHou8lj9yiXezDKDn
Jia3TOxCu5VTRnYT7Ypt8kSull/jlyBQgTP+P0YXwoYAXJteQr9O6yD75yWOAx3A
f/oQS77xoe0jdU4RkEMQRQQUIiaNyH8Bx4CeETmPoJDzEiIvnC5xDoySks1VJK7b
w11IANF7zO5UWtYv/i3+Wh5XLMJ0GIIVTpuGkeVaZCjB8goiBJXoaHOKsO5ygJo5
N+nxwiDTwUIZM+dU88mtCh0dvJeMMfA=
-----END CERTIFICATE REQUEST-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAziU9ccrphXsYUx5KwcpWrfcsOfxm3QiRjmkY0K66Sw9re9gE
VGTBOuuhspAOebCMt5pAhLLHJ6WFPhohTfg5CkaM6YLNx5g8bS45x5yruehW9uDD
RR5AeQQvSng06Vv0sw1WEpOBBSFkLmZhIilc3GUT8uUaaqsVf/yl5JonWgCSQHkV
g3SABtiF3ySklff02vYD5/zyiJxJfkrQvvctQVSvGhNysU/WlacWCDby8u+/Otb9
Vm2riok+XcZMtszEc67TTQtocrxOAgkwJBRqWHarafP4e2a2/6D9vbtgQwZwMFbW
oFf2gwqCgz9nUe/ONWECb0jrj2F4NSC4VcJqgwIDAQABAoIBAQCmygix5gwU/KiM
r6iqrOx+6sq0y9vqIIGsaKo0RfriukIrvHacVbzl0DpPADFGEit4beyfsQpjsI9i
1L93l0uHXderIzMdt7XEXK9RKxjiXPLn4qj7ZmOhxloA9ctRuB3/NN4cP44XOZIV
3K3gdvj0NS/zyZwbC/tkR2Vt1a/bJ8DFfaFrSdk/btpVY2BH/uWjMls1BYIs3tEk
nroJYb+fyliC+n/QXrLQAPTVLfI3jyVjRYpW5b6mZHQk4SGDde1XbtqRCp/f6PLu
H8FQumd+SfrjgTwefphSWCW2H/aMNsZpL9NK/hmglKg3OcGKhwQswdmL9rDVFQmy
AqxXwxk5AoGBAPPmijvwg6bKwqeSCdhoYjQVNXdTbdda3arjC/G/vxhsVntXluOX
wGF2jInAu+sAShBFdhG4JlL+itlpA/aDDIMUxsF+YnhTOX5pofL8Nr4sstY6wjBf
4jVHQKLnaOm/mVpHqWABVv/M085XK7HHZR9e9y32ry1geRSiPF/E5I03AoGBANhf
OPL5WWO7TOchWci6qRcv0kZ47iSE1JxSGnXr+KIIBPIAgrwhYkqgrl6noSe3x1qB
tP7ZvmFWewxmo3mN2OwPTsxAhnjQK4D1PGJcsvf2A3f1uiwG+emqWpcMshTQnjda
Hi2krfMaHwErE+dEbZiilLDRYAMAWlQodXnhllMVAoGAKdDIumYN7DavENO05Glh
DNTmCcM//cASaQ3sKlJZjPJmEVd/Ax4tWYhdp/BnR28RQ6DlETylNW12mLesekMV
jhOtz9a/QynhnY62uVYMfKZlMt14FZsayU+iAUvzbL/wps3KeC9CnzCaz7GaSCyL
Zcl+T18PwZPcrnDyMOks1hkCgYEArrQUE3tpxbER4v12tTCiHuqp6eTyw+HMmXth
ih1B3/KBq7Tl2mlKJ9+daygGYz9sY5OfRLcjlQxyxgyJqjfyEog5o4nmCd5rgfCB
FRqsFrI5Er8B11K6rwSxqIzDrTLUzPSisU/qdAN/TT4vD+icZUXAsRQdZc7/IDya
vhJ7ghECgYEAiZ6v380l2jcEk+tm4UYZ+nXq3c2wq8mZkQcEDVpqvDGL8k/dPEq9
xOy7PVooQHQJyC2bgTbEKs6JYvzAYQmohiq7L3y9WxDQFJeImtzuNi0s/dwP+mBh
R6htM5JwAO3JnE9V863M6kvtLEIk7XptI5gC0kN3Thi70yT3lnU+emk=
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIENzCCAh+gAwIBAgIQSCdvhIY3KZ9w9YRcDH1oRzANBgkqhkiG9w0BAQsFADAN
MQswCQYDVQQDEwJjYTAeFw0xNzA4MjUxNTQ1NTNaFw0yNzA4MjUxNTQ1NTJaMBEx
DzANBgNVBAMTBnNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
ANlVMhHZHjRWiGqmpOse1KZTmdUaiSgl3T88+Mie17UbiLmsOfnkd3PuEnKlXRXM
sqOg15k9xfnV6SQebpBfkcwqJVH/USjWY4C1e2vDrva+j95L+uzDMZDF9nxpnjHE
uHT9+hnrmB3Xted0tzxRC/77Cht8Kn4gaoljbBoZsRnv0vRRUYKA2OJHJRRCHhzZ
AN6A+lWbWyvyvd3UeOLl3oFhk4lS5fwYY6RY8W9ZTJVxetVycvro42kK2jtpqUeF
NercfjOg8VBWYjqB3Ey1wHDpjS407TW7RWEGA+3mP8ZFsuoYr7Rs+z8LprbYEPpF
ojgvss+vMvjGkrcU8v0MR/sCAwEAAaOBjjCBizAOBgNVHQ8BAf8EBAMCA7gwHQYD
VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBRS8x8oOcBJnrrW
jiS0t3qjKee63TAfBgNVHSMEGDAWgBTNBS7rmAcLgCfraHEZbvsVLjsOhTAaBgNV
HREEEzARgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcNAQELBQADggIBAI9t4Pvn
o0cW0SrC4EFNJqUaffbqUNd3i+dBn5/EQc8EGYVY3k8E8iUMHzRDyH+/VRp3RUOH
oJDDS2uyeJ2InkC193MOpNJDQV5qf3yOmQCeVmmjcXkg1Nc+3oe8ttWiVW0ArFXS
oud6V7/6qzIz3850ypi0Zz4KLVhSGzaI8dnzPopqNEG+ZbgIwvQLqWhv2bLpqycY
5ANECpjH6QIEp8JNjga9Sp/LspNCqMDmVswBGarySNWZ1+uflg9X30hsdCzVPgX3
KMy0wVelT/4y893BCTo2KendGh70jaGxm8nBH7OXkeki2TI6boAvsn2Iash0HSZ4
hPacZ4QWFEYW4jZeu3ZNTJ9tc2u2jgpAGueOcWRY75CraLid1V5t1kpGxAtX4NvX
X56e/IlmEI6qPsaoPouQ1riAWMdRQUT1FLNPanv4vElDYXBNcFl7knuS14JDCC0M
K6ttSb2MxJYfC6J+OJpHQd2GWU5aO2uZcgi9jRMslNwR+R94bxI+q/bjrI31JsDz
1pVRnGRWH7cDejA2f+q7X8/uRuA8bfnqBcu0uI9YR64W0VMMLe41+iR0wt5N3yWr
/DalWIvmUvE8LoaGDwxV9T3xq1I1dWnASX+Xmb1SQ0CnhEEooZfQYfb3ffciG6mU
UvVC0YW1cjOgb193W/N+Dgju7/a/e+XgbsgT
-----END CERTIFICATE-----

View File

@@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICgzCCAWsCAQAwETEPMA0GA1UEAxMGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEA2VUyEdkeNFaIaqak6x7UplOZ1RqJKCXdPzz4yJ7XtRuI
uaw5+eR3c+4ScqVdFcyyo6DXmT3F+dXpJB5ukF+RzColUf9RKNZjgLV7a8Ou9r6P
3kv67MMxkMX2fGmeMcS4dP36GeuYHde153S3PFEL/vsKG3wqfiBqiWNsGhmxGe/S
9FFRgoDY4kclFEIeHNkA3oD6VZtbK/K93dR44uXegWGTiVLl/BhjpFjxb1lMlXF6
1XJy+ujjaQraO2mpR4U16tx+M6DxUFZiOoHcTLXAcOmNLjTtNbtFYQYD7eY/xkWy
6hivtGz7PwumttgQ+kWiOC+yz68y+MaStxTy/QxH+wIDAQABoC0wKwYJKoZIhvcN
AQkOMR4wHDAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcNAQEL
BQADggEBAA76r3SIQUER3XyGp4MOrfKGwBE7RnALcxW1XkrhID2bng2hzovrZZNO
xutL1zqPFCxClIKUxYAXpMeY8lnS6H8I6FoM6ALCZbK7q9rmMK198LPMo3zC6TsO
rEP8HOF9wxaYubg8xaq8iDlaL4e418M0UPOlE75PtkDAjhY++7ZTsjPVr/9WsJpZ
MmEZ5kSS59PZbMbyqXn5MxE0iSD0LfM+lmkIBwSvD8rjq3SQ5NKCg6CJkRRq7BVe
bujA2pPb6ivS5pujjIxkdUoz6S0G+ewZG16kbBoygWuRVFD8xqR9Pa41KSPhpx85
1qSrqR4zHvS3r+RS9UVIXnbh9ejW6Vg=
-----END CERTIFICATE REQUEST-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA2VUyEdkeNFaIaqak6x7UplOZ1RqJKCXdPzz4yJ7XtRuIuaw5
+eR3c+4ScqVdFcyyo6DXmT3F+dXpJB5ukF+RzColUf9RKNZjgLV7a8Ou9r6P3kv6
7MMxkMX2fGmeMcS4dP36GeuYHde153S3PFEL/vsKG3wqfiBqiWNsGhmxGe/S9FFR
goDY4kclFEIeHNkA3oD6VZtbK/K93dR44uXegWGTiVLl/BhjpFjxb1lMlXF61XJy
+ujjaQraO2mpR4U16tx+M6DxUFZiOoHcTLXAcOmNLjTtNbtFYQYD7eY/xkWy6hiv
tGz7PwumttgQ+kWiOC+yz68y+MaStxTy/QxH+wIDAQABAoIBAEzhDVAw/LVI8wK/
JlGh21lm82Dl/SS9mDE5kUvunKGNNuVvXibewb65tb7mbjI68epeCEZGCtVg7RMA
zN23YOzW79K8vWnzxMkP6bPqSecw69WYDRBZ0BvFW3cRKYuzagjAmws2Qt4zoz5Y
FEV66gJtrVqhpqptLyKgj+n/sp1YiHMjkcJF5PGnAPudYxpiDHiPFked7vZtigyq
eE2GCVbaOmRGPMe28JzRmOuFqEN9GccRjlq+AuzYe14lWKa6fZLVke78luByF//M
RA+gGoKfHq859wACOEGbqShMWxC++y1HJ2Mu4adUlikzVq8rboinaAxzv56kc1n6
EDc/qPECgYEA2x+qxHKSJnQuItZp0CpsDk51yr1fJcWtdEbcgmomiwMwl/Nk0OAB
rplrW5gezyVRfMDsoqAnmBS301JEL/7QuEWvedTpptKC503Z+mCINPTZAaJfa7Jb
KUCSQHO3hThfOXFMkb9mcJEVVNqtobnK61tC+JNOSd00Z9TdLQAbcokCgYEA/ehf
Vq3md8bkQFlnMtFib8SIr8IP5j4JecFSeqa/OnBFfHJr0trUhoMO04uaQOl6lTT7
9Ca36WfJsv3EGkMHEFeceOsPswf65bI/qgxgUS7Qmu+NZTjvXL0gCpeHLPzEaLTV
CDXoo+YAJzzv9yWwVEvIIru5SJPVud6Gap5L1WMCgYAjfO91PXD6FVrbfYpJknVJ
o99j5GOihG9hI5DW9kYjwXJ/SYYMZhsfoe1HOk3TEqIt6Djq5bFD6icTbIFqnIRF
M9QFkTv+Lp3QxEUHTdcBbJ4wq5F0qcAl4DVPhu4z/zs83GKgQDVhCb5AreHtDWAV
2gPwqjrFr7OrFUh0302SsQKBgQCOHKZn9HNfLOIKJj/9kHYhCoZaoSqW+rgA/rQ0
U+oKQlaR/dTdsn9rPiVpP+S5WjSzGHHAyH79U4rv9Nryu/tTKUY5447o7JmAQJEj
k0PBjItTfKrOMdy/MlehtggBpQQlerkVnF62hYAmdhP1Z5HWzIea8SkWNzBTlPn0
6N6W8wKBgQCDBPXqGii/ur5IBQ+RASIL78RgIGH49XYcq7IrYFOuztGBbqf4qorR
BTl/mWFFHr+fAcLHlC/qTSBBflGClElqcg+j+92RAI6HbCFemdSbnsv5FCmciL6e
09x+oprKq3/WAVARUBif3RtDq92SFwSBfrx8JFhGIa9kUcsDFSBaDQ==
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,16 @@
-----BEGIN X509 CRL-----
MIICgjBsAgEBMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMTCHdyb25nLWNhFw0x
NzA4MjUxNTQ1NTdaFw0yNzA4MjUxNTQ1NTdaMACgIzAhMB8GA1UdIwQYMBaAFGM6
559SRpX6VJlKbIObtrLXvc1rMA0GCSqGSIb3DQEBCwUAA4ICAQA+6zsHCq9YRZ+a
fwsZmbGQqDUBVp5TWtDsy+qvKf/084CgTn0sR28HKEONQvX+R1CyzAaCGrkm081k
yDUizdyGrVR8zmCc7O3ztPobfZBmQXbR0pcxwweFiELBO1exEQ5IpM4J0KOPc+CB
AwVA227Q4oKrKyUtNQ9d3qr0/2E2HE8W0aV7Ax38MvXsUfafWk0SNPDusFiYNsTt
v50Gmd2yBlaMzT9Dsze9wuoTvT42lpCby/NSSDYynG9Cra2y0VoIpVxvPqVzn77L
1otkaQRatbfktaa1WVufEK81FXEyeYdM/T4bKCbB5oBKxmwwiS8ukvunc9gdrosx
/7QIlpr3iBEu+X+GeOdGyPA+a+S566Hil38QCyMUX4fI7xjYt3ek2MI1YeNEv576
CwEihc7NvPh5MI0OQq0nIjTcIeEGQag0eAaGwmJ+AmrZ+4bop5QavuwHVh/7sNel
rNhFucmx+gEPeS/Ae0cp2BeXjEXHmdfwKDCT1n1Rdd5wfLeFuKlBG8NdZKwZ7HH7
vwi+JwIBx/WJ/f1qcPlWAyF4Y/HUJIRRNSXJOUQCWhCvfGtLQ4xs7uo4ViE8CtyO
RE/3xHrX9UwJUymw50Efj3PpOxWPvJ9B7A+8ED1kJR29HQz5gVZhEcW6nDTntwDQ
5nrvhzi93xzaTQIhEe53uz+rTGs/fw==
-----END X509 CRL-----

View File

@@ -0,0 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIE5jCCAs6gAwIBAgIBATANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDEwh3cm9u
Zy1jYTAeFw0xNzA4MjUxNTQ1NTRaFw0yNzA4MjUxNTQ1NTdaMBMxETAPBgNVBAMT
CHdyb25nLWNhMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzs91DHKf
enUfIPigN6n4CsTSHGMkRpWQw9T5vIqUd3ZgMJLCoEnj0OpVlFVcb9+P2/f5RYg8
H0dmM1iHGi5uCoDnJk7K5zaLMgxGtRy8dwZ1nHri3sZwM6Z9AyvktuSVbuElzp08
utwB+fstR2METN5Dovp738rdx2q3zYYYcfnEXcYvxBdxVeitFgrXauRDxuqMio+5
5bHVUpIWlpu8Fd3CqnMUS4N6McijIn6T2wiyALJDf9xE7edAYl4ExYVKaOyR3Ff3
+BhiS+IEPd9AoctU9JYFpDavfaiZz77AukwwfU+W93NTTFQ+rf/ev8XzsgkFyZPw
CCfKuet+o2/8MIxv4nwKxv6GGMFbQz1gNw3RqG5m19zppqVzp1vgMcNXSeRPFlQI
fXYFN9BY9bvx2L2ZpTn3gsdgzDGNzYU5fGro3YzmtelNBCY3sAz/riG6+wMDHCId
5K5NxrJBW3tTvEZQyKVZA1W22/F/Wz2LxA+4ZLhUoUuXTkJxLS75EWLkK2xK+IXv
h4s8n8CwfhFV/De7u18Pho0XKTm2IPir1nL0WNhjy4jvBYDN/Jy4fE4QALt4oH9t
+GITkDo0Zd/USZSkAOXgEb+Ks5F/fztI9yVp7/nhhj1KnSIrkObBaltlfEOe4vgz
3dNFAG5kH5lyG90uffKR7h1Vo7UkYcpJ3IMCAwEAAaNFMEMwDgYDVR0PAQH/BAQD
AgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFGM6559SRpX6VJlKbIOb
trLXvc1rMA0GCSqGSIb3DQEBCwUAA4ICAQCIm4pRbel8V/W1OKSzJXg3F76ORIjz
zN4mAtCX8YtFQwawBlES25Ju2IQ9XfvqM0CPO5LEe1v8ZTXeke9Vjf/XGReBCCqy
/STzLSBHflQqvybMYH87K5h5e91Ow9T2HjyPtzS3RdyaahU/Y8/EnOTG89uJlpN3
0k0/KXfwVKAyjrOaoTeGPM9BjDssNq2S07h5C8sCby3MpR26CIMGbFnotwTjmSww
qkDSVd63/ZIB5/dOcOlBd1+rE3LOzYxDiZtKWu0NM+o7N0m4Y+gD4siyxRWuKslz
cTwiwnLmzZG5BUvRT2FmzCwejp45+LjrXmUZ8hCznk68hnkilx9XLdkBL/1qyk40
I1IUFQtkkcyznwUyKpC0z4VJZAVL8xi6KO60TOYtidxFTJxkPrWcAHvgzItao3XZ
C4hLlNk7RD6BJ8oyMtpXFq7MHAAb8MWSLu/rSAhQHoKqlCEK4Iks9nWLmRP0OdAw
BcXGMuTIn1jFRM0CQvg68GPFOH3FKv+cyUbjPoXvCBYiXKmxA/WX3rYvDo2paZKU
/mDMu+EdAR3Zk/wYXl4738ujqzO88Nw2LBHLKhXytHMaSbfmWf085r0L+fqHLuVM
jlpPEi6vQum25j9tvGnp6GyO8lUDAUqk5gtYIp+D67+NG+9eBocA1ADVpeKZHBQV
xGgCdjnoP+nDVw==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEJTCCAg2gAwIBAgIQINVcdjlrWhsKxwcXL4vbUDANBgkqhkiG9w0BAQsFADAT
MREwDwYDVQQDEwh3cm9uZy1jYTAeFw0xNzA4MjUxNTQ1NThaFw0yNzA4MjUxNTQ1
NTZaMBcxFTATBgNVBAMTDHdyb25nLWNsaWVudDCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBANHAXfyT8vuNKc3FeedZH3865bF5PLbRs2R8CaUOdQj/HTM5
xSsb5Tr3x6IkWK+5SwtnGdaLY7GktSktXyUNf2uZflXHCLAqiqcBpLNO9mcFAACz
pRb7C18ZZ6d9b7UtPA5oK1Vt45iUzI+mdCC0BtRTWeyKdtTF4muD2TtF7RMQwnjf
RGV1EkfQ3sKpX3P7daiA/W116NlESpX3J/VAzoQu+3BrDeXrqgEbNVl+/NN/7uA0
RY0HxE75RNhL9yuz1VFP4/NaFOdWN3pSMKwcmiIeNC5n0eyW/fodQpOT/dcscfbP
3a0XeoZkfB8nuVcGkmtUkuw0jPy+R64vnQvlugsCAwEAAaNxMG8wDgYDVR0PAQH/
BAQDAgO4MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQU
0pX6qc7YwiehN5AYh+p4JNkFEXwwHwYDVR0jBBgwFoAUYzrnn1JGlfpUmUpsg5u2
ste9zWswDQYJKoZIhvcNAQELBQADggIBALH5pFNhbnro2vFE+8RqbRqOZZNoyKqL
INY/e0MNPmhp4CE2BQcrxgFcRgJmyh4lOP8gmIHT8q/9kOvYqMmfU1vbVF/XLFsa
NJxkQSX9uilV1LDykyRbwlI4McjdTW7rLEkW8YrZueMXnDYQHGx9L2qYWgXzA5yA
Mfsgq3pr39sDVDfYg1H+0daA3nIw+OWDsWjORXvzo5TQzjUXLhREp6WuuRKBT1+p
VHGAnUcwDEb6L1bWEloG9ogXJfsXuCUxF+/II1RYSKiAmjge1nDOM2USfIKfD5nz
tLJn0pn0B5dyceJTgOK6dwCXwn0Gc99qVzBSSHtPe+abSuY5dNoIwtL4R4rDE4U+
+y2vQwzum+GhHn/ZEDuYT/0+IDqkVxeWBiZ5IFEkRpBEFpmEJOdKWaSrIQWMpIjf
FIlxY3VzUD8H5M65kMSRKXbRJ1zSHMcIFKK2R98SPuYnYmgc4kOh49WkEr6dj2B1
0QNZxPg70HP3qWgCxf8F5Mxg5YOtz7gN6N3AutrlYV0KB/OT0h4lhtvW3inxRgID
iAHw0A4X/1qbFUeSUpINZaVQFtBh6fT/JfYDDTFFBcoqrLOZpFaS7r6FbGP3FDS4
v9MqsYOSA6LrOHMdRop+eDV718iGDcUVtIItRZZV0s4UTo5q0JpGBtVPo/R8ui19
eaGxvLJT1Gd+
-----END CERTIFICATE-----

View File

@@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICXDCCAUQCAQAwFzEVMBMGA1UEAxMMd3JvbmctY2xpZW50MIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0cBd/JPy+40pzcV551kffzrlsXk8ttGzZHwJ
pQ51CP8dMznFKxvlOvfHoiRYr7lLC2cZ1otjsaS1KS1fJQ1/a5l+VccIsCqKpwGk
s072ZwUAALOlFvsLXxlnp31vtS08DmgrVW3jmJTMj6Z0ILQG1FNZ7Ip21MXia4PZ
O0XtExDCeN9EZXUSR9Dewqlfc/t1qID9bXXo2URKlfcn9UDOhC77cGsN5euqARs1
WX7803/u4DRFjQfETvlE2Ev3K7PVUU/j81oU51Y3elIwrByaIh40LmfR7Jb9+h1C
k5P91yxx9s/drRd6hmR8Hye5VwaSa1SS7DSM/L5Hri+dC+W6CwIDAQABoAAwDQYJ
KoZIhvcNAQELBQADggEBAMxscjfVRQ0/0c6f0MWtJJe+vy5Gj26XHVy5EsbH1ofq
eWF00CFlVw5CdznGV0NL6LOE+sz5sBKsN2sZU7xPeV5XRHVXpAuECcOcgWK6FkqA
wSmwVWZ93o+kJXrUZTyZBMkvQMUUr30JIpXIXJmLWKPBq5KRBJLirHZYw4FmbARv
iZdNlQ1rLOZKZl7yUVkAfyfw+ueb0OPFp/fzuCerNB0ySSmYdHzqDFsMLm4Bq/2z
FJOgasAU2RvxFVoRp7P/ZUSvtOKACMUHaYBnKZAvkKv3MIS/qLCSkzxNwsclT8uF
aKRFZnOkZZHvEaXVAwCfJB7tI3TELb+L2KyqU36Q1Oc=
-----END CERTIFICATE REQUEST-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA0cBd/JPy+40pzcV551kffzrlsXk8ttGzZHwJpQ51CP8dMznF
KxvlOvfHoiRYr7lLC2cZ1otjsaS1KS1fJQ1/a5l+VccIsCqKpwGks072ZwUAALOl
FvsLXxlnp31vtS08DmgrVW3jmJTMj6Z0ILQG1FNZ7Ip21MXia4PZO0XtExDCeN9E
ZXUSR9Dewqlfc/t1qID9bXXo2URKlfcn9UDOhC77cGsN5euqARs1WX7803/u4DRF
jQfETvlE2Ev3K7PVUU/j81oU51Y3elIwrByaIh40LmfR7Jb9+h1Ck5P91yxx9s/d
rRd6hmR8Hye5VwaSa1SS7DSM/L5Hri+dC+W6CwIDAQABAoIBAHml4JyRTdX4q+sM
gcPcG3lVtkt0rfK1sh4wFgPlW5kpJE1GTwTOe+b0N5LhE5Jum4h0djbIxrwLc4n7
J3g82M6VygCDm5VYRuvO9y+LNzrOWo8NoUyvsouoF0a7aCMipfcRETjNr7cZbX5O
ooEpB+Dyqm+Wao7CaavDXySSTInGHG4AD9HM5nQsVIebS1HOkhI7SmNkZTOd3gzp
bR/iZgaYI5eC7Zj7hHNr4gWdRBuefU8wLZZGoqByHRTSrKwICRLIkGyoMRAD9p9r
S48lyUmd3BGHRPLmNl4u0kfsVlcCYBNKVZV6kkSVv4Wht/KVr3tl0+2wrNUe17w7
vlCsCPECgYEA9AdHq3UjswD6PYITCdrDOGVLMpgL0obA6X2Fi6GsMiRXQgsvxMVY
a4D2vtfZvLa8TSA+b8bK6uSMyD3mOHLxBkUPMQZxiHzq/ldK9vSIvzky0woF6suT
J8fa2F0QfjlKWhFMwf6JVGyZl+vmYqqF55lxRJSWGZHSSxaxYPU7dbkCgYEA3Aqb
YInpCp7zSlWYfXorwnyvsHsTdFbsoGGgMrc5l2B7PcP+Na7lm0dsNCrp7n7TSrE0
8iIoqhYq5u7RlGf5QXzXcGgZQMHcxLcrqBPviEktuUifXZEwfw9NXrlt4iF/oVTc
++7jqUZ+iH9NIMoxrQPXVdXJSJN9iwc7/yG6j+MCgYA+ehqsWCpSqx5mXwYW0M6I
gs6U3n6wYNXFMeDeFf9rOwioHQsW2tu/cl46EDNr8HEXYfj6TzAmoWs13TszGqKA
02+HQroQksLraVgFEChupOtRQtCvA33ignWSTYlqd6qEksdPJ6brWX6decUbX8M2
v39TaqNfWok3tlClnUOi6QKBgQCIhfQ9g5OZyWE937nLMH/yHZaMMvCxIDWUlL3m
eZQ7/dq5Sd9xw2AmZbwW6gFWvk2ubCBjkxoT3ckkm0xhfdlC7ohk79GrQh0N2HA3
ypa1wmGiMhLe5PRoAUCJ4xbwVMRxfsvVbDTIlDpxyjo6e/kyVc3HLevDIe+k0QpC
k9TC7QKBgDyghEPAM5euQHk2o7cMr0YK52vzoM1FGhrRAF5MhTYJEYBNu0Z+0McB
G8kwy4WH5zMuvKj1zZckAzkbpD/iL3XQuzs9pZdNnXzdf25/us0pZ2a/6v5+fpmF
JEuwQ1AztPEv4tLd3+xrmE+j+qd3xDqYt8eaWFswcuxchB6nUdqq
-----END RSA PRIVATE KEY-----