Skip to content

Commit

Permalink
Add GetMediaStats to StatsReport
Browse files Browse the repository at this point in the history
Add an API to get basic stats around certificate

Relates to #610
  • Loading branch information
obasajujoshua31 committed Sep 22, 2020
1 parent 062e55d commit f0d26f9
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 3 deletions.
27 changes: 25 additions & 2 deletions certificate.go
Expand Up @@ -9,6 +9,7 @@ import (
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/hex"
"fmt"
"math/big"
Expand All @@ -22,6 +23,7 @@ import (
type Certificate struct {
privateKey crypto.PrivateKey
x509Cert *x509.Certificate
statsID string
}

// NewCertificate generates a new x509 compliant Certificate to be used
Expand Down Expand Up @@ -55,7 +57,7 @@ func NewCertificate(key crypto.PrivateKey, tpl x509.Certificate) (*Certificate,
return nil, &rtcerr.UnknownError{Err: err}
}

return &Certificate{privateKey: key, x509Cert: cert}, nil
return &Certificate{privateKey: key, x509Cert: cert, statsID: fmt.Sprintf("certificate-%d", time.Now().UnixNano())}, nil
}

// Equals determines if two certificates are identical by comparing both the
Expand Down Expand Up @@ -155,5 +157,26 @@ func GenerateCertificate(secretKey crypto.PrivateKey) (*Certificate, error) {
//
// This can be used if you want to share a certificate across multiple PeerConnections
func CertificateFromX509(privateKey crypto.PrivateKey, certificate *x509.Certificate) Certificate {
return Certificate{privateKey, certificate}
return Certificate{privateKey, certificate, fmt.Sprintf("certificate-%d", time.Now().UnixNano())}
}

func (c Certificate) collectStats(report *statsReportCollector) {
report.Collecting()

// Unable to cause it to error
fingerPrintAlgo, _ := c.GetFingerprints()

base64Certificate := base64.RawURLEncoding.EncodeToString(c.x509Cert.Raw)

stats := CertificateStats{
Timestamp: statsTimestampFrom(time.Now()),
Type: StatsTypeCertificate,
ID: c.statsID,
Fingerprint: fingerPrintAlgo[0].Value,
FingerprintAlgorithm: fingerPrintAlgo[0].Algorithm,
Base64Certificate: base64Certificate,
IssuerCertificateID: c.x509Cert.Issuer.String(),
}

report.Collect(stats.ID, stats)
}
11 changes: 11 additions & 0 deletions certificate_test.go
Expand Up @@ -65,6 +65,9 @@ func TestGenerateCertificateEqual(t *testing.T) {
sk1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
assert.Nil(t, err)

sk3, err := rsa.GenerateKey(rand.Reader, 2048)
assert.NoError(t, err)

cert1, err := GenerateCertificate(sk1)
assert.Nil(t, err)

Expand All @@ -74,8 +77,12 @@ func TestGenerateCertificateEqual(t *testing.T) {
cert2, err := GenerateCertificate(sk2)
assert.Nil(t, err)

cert3, err := GenerateCertificate(sk3)
assert.NoError(t, err)

assert.True(t, cert1.Equals(*cert1))
assert.False(t, cert1.Equals(*cert2))
assert.True(t, cert3.Equals(*cert3))
}

func TestGenerateCertificateExpires(t *testing.T) {
Expand All @@ -87,4 +94,8 @@ func TestGenerateCertificateExpires(t *testing.T) {

now := time.Now()
assert.False(t, cert.Expires().IsZero() || now.After(cert.Expires()))

x509Cert := CertificateFromX509(sk, &x509.Certificate{})
assert.NotNil(t, x509Cert)
assert.Contains(t, x509Cert.statsID, "certificate")
}
8 changes: 7 additions & 1 deletion peerconnection.go
Expand Up @@ -1947,9 +1947,15 @@ func (pc *PeerConnection) GetStats() StatsReport {
DataChannelsOpened: dataChannelsOpened,
DataChannelsRequested: dataChannelsRequested,
}
pc.mu.Unlock()

statsCollector.Collect(stats.ID, stats)

certificates := pc.configuration.Certificates
for _, certificate := range certificates {
certificate.collectStats(statsCollector)
}
pc.mu.Unlock()

return statsCollector.Ready()
}

Expand Down
15 changes: 15 additions & 0 deletions stats_go.go
Expand Up @@ -61,3 +61,18 @@ func (r StatsReport) GetICECandidatePairStats(c *ICECandidatePair) (ICECandidate
}
return candidateStats, true
}

// GetCertificateStats is a helper method to return the associated stats for a given Certificate
func (r StatsReport) GetCertificateStats(c *Certificate) (CertificateStats, bool) {
statsID := c.statsID
stats, ok := r[statsID]
if !ok {
return CertificateStats{}, false
}

certificateStats, ok := stats.(CertificateStats)
if !ok {
return CertificateStats{}, false
}
return certificateStats, true
}
14 changes: 14 additions & 0 deletions stats_go_test.go
Expand Up @@ -107,6 +107,13 @@ func getTransportStats(t *testing.T, report StatsReport, statsID string) Transpo
return transportStats
}

func getCertificateStats(t *testing.T, report StatsReport, certificate *Certificate) CertificateStats {
certificateStats, ok := report.GetCertificateStats(certificate)
assert.True(t, ok)
assert.Equal(t, certificateStats.Type, StatsTypeCertificate)
return certificateStats
}

func findLocalCandidateStats(report StatsReport) []ICECandidateStats {
result := []ICECandidateStats{}
for _, s := range report {
Expand Down Expand Up @@ -295,6 +302,13 @@ func TestPeerConnection_GetStats(t *testing.T) {
assert.GreaterOrEqual(t, offerSCTPTransportStats.BytesSent, answerSCTPTransportStats.BytesReceived)
assert.GreaterOrEqual(t, answerSCTPTransportStats.BytesSent, offerSCTPTransportStats.BytesReceived)

certificates := offerPC.configuration.Certificates

for _, certificate := range certificates {
certificateStats := getCertificateStats(t, reportPCOffer, &certificate)
assert.NotEmpty(t, certificateStats)
}

assert.NoError(t, offerPC.Close())
assert.NoError(t, answerPC.Close())
}
Expand Down

0 comments on commit f0d26f9

Please sign in to comment.