<commit_before>package apns

import (
	"crypto/tls"
	"net"
	"strings"
	"appengine"
	"sync"
	"encoding/binary"
	"bytes"
	"errors"
	"time"
	"fmt"
)

// Client contains the fields necessary to communicate
// with Apple, such as the gateway to use and your
// certificate contents.
//
// You'll need to provide your own CertificateFile
// and KeyFile to send notifications. Ideally, you'll
// just set the CertificateFile and KeyFile fields to
// a location on drive where the certs can be loaded,
// but if you prefer you can use the CertificateBase64
// and KeyBase64 fields to store the actual contents.
type Client struct {
	sync.Mutex

	ctx			  appengine.Context

	Gateway           string
	CertificateFile   string
	CertificateBase64 string
	KeyFile           string
	KeyBase64         string
	DialFunction      func(address string) (net.Conn, error)
	Closed			  bool

	pushNotifCh	  chan *PushNotification
	FailCh		  chan *PushNotificationResponse

	SocketCloseCh chan struct{}

	doneCh		  chan struct{}
	apnsRespCh	  chan []byte

	certificate		  tls.Certificate
	apnsConn		*tls.Conn
}

type errResponse struct {
	Command uint8
	Status uint8
	Identifier int32
}

// BareClient can be used to set the contents of your
// certificate and key blocks manually.
func BareClient(ctx appengine.Context, gateway, certificateBase64, keyBase64 string) (c *Client) {
	c = new(Client)
	c.ctx = ctx
	c.Gateway = gateway
	c.CertificateBase64 = certificateBase64
	c.KeyBase64 = keyBase64
	c.DialFunction = func(address string) (net.Conn, error) { return net.Dial("tcp", address) }
	c.Closed = false
	return
}

// NewClient assumes you'll be passing in paths that
// point to your certificate and key.
func NewClient(ctx appengine.Context, gateway, certificateFile, keyFile string) (c *Client) {
	c = new(Client)
	c.ctx = ctx
	c.Gateway = gateway
	c.CertificateFile = certificateFile
	c.KeyFile = keyFile
	c.DialFunction = func(address string) (net.Conn, error) { return net.Dial("tcp", address) }
	c.Closed = false
	return
}

func (client *Client) Open() error {
	if client.apnsConn == nil {
		return client.openConnection()
	}
	return nil
}

func (client *Client) openConnection() error {
	err := client.getCertificate()
	if err != nil {
		client.ctx.Errorf("Error getting cert: %v", err)
		return err
	}

	gatewayParts := strings.Split(client.Gateway, ":")
	conf := &tls.Config{
		Certificates: []tls.Certificate{client.certificate},
		ServerName:   gatewayParts[0],
	}

	conn, err := client.DialFunction(client.Gateway)
	if err != nil {
		client.ctx.Errorf("Error dialing on gateway: %v, %v", client.Gateway, err)
		return err
	}

	tlsConn := tls.Client(conn, conf)
	err = tlsConn.Handshake()
	if err != nil {
		client.ctx.Errorf("Error doing handshake: %v", err)
		return err
	}

	client.apnsConn = tlsConn
	client.initChans()

	go client.loop()
	return nil
}

func (client *Client) initChans() {
	client.pushNotifCh = make(chan *PushNotification)
	client.FailCh = make(chan *PushNotificationResponse)

	client.SocketCloseCh = make(chan struct{})

	client.doneCh = make(chan struct{})

	client.apnsRespCh = make(chan []byte)
}

func (client *Client) Close() {
	client.ctx.Debugf("Closing %s", client.Gateway)

	client.Lock()
	defer client.Unlock()

	if client.apnsConn == nil {
		client.ctx.Infof("apns connection nil so not closing any channels on client close")
		return
	}
	close(client.SocketCloseCh)
	close(client.doneCh)
	client.apnsConn.Close()
	client.apnsConn = nil
	client.Closed = true
}

func (client *Client) EnqueuePushNotif(pn *PushNotification) error {
	select {
	case client.pushNotifCh <- pn:
		return nil
	case <- client.doneCh:
		return errors.New("Done channel was fired probably because client was closed.")
	case <- time.Tick(10 * time.Second):<commit_msg>Client should return whether its closed properly<commit_after>package apns

import (
	"crypto/tls"
	"net"
	"strings"
	"appengine"
	"sync"
	"encoding/binary"
	"bytes"
	"errors"
	"time"
	"fmt"
)

// Client contains the fields necessary to communicate
// with Apple, such as the gateway to use and your
// certificate contents.
//
// You'll need to provide your own CertificateFile
// and KeyFile to send notifications. Ideally, you'll
// just set the CertificateFile and KeyFile fields to
// a location on drive where the certs can be loaded,
// but if you prefer you can use the CertificateBase64
// and KeyBase64 fields to store the actual contents.
type Client struct {
	sync.Mutex

	ctx			  appengine.Context

	Gateway           string
	CertificateFile   string
	CertificateBase64 string
	KeyFile           string
	KeyBase64         string
	DialFunction      func(address string) (net.Conn, error)
	closed bool

	pushNotifCh	  chan *PushNotification
	FailCh		  chan *PushNotificationResponse

	SocketCloseCh chan struct{}

	doneCh		  chan struct{}
	apnsRespCh	  chan []byte

	certificate		  tls.Certificate
	apnsConn		*tls.Conn
}

type errResponse struct {
	Command uint8
	Status uint8
	Identifier int32
}


// BareClient can be used to set the contents of your
// certificate and key blocks manually.
func BareClient(ctx appengine.Context, gateway, certificateBase64, keyBase64 string) (c *Client) {
	c = new(Client)
	c.ctx = ctx
	c.Gateway = gateway
	c.CertificateBase64 = certificateBase64
	c.KeyBase64 = keyBase64
	c.DialFunction = func(address string) (net.Conn, error) { return net.Dial("tcp", address) }
	c.closed = false
	return
}

// NewClient assumes you'll be passing in paths that
// point to your certificate and key.
func NewClient(ctx appengine.Context, gateway, certificateFile, keyFile string) (c *Client) {
	c = new(Client)
	c.ctx = ctx
	c.Gateway = gateway
	c.CertificateFile = certificateFile
	c.KeyFile = keyFile
	c.DialFunction = func(address string) (net.Conn, error) { return net.Dial("tcp", address) }
	return
}

func (client *Client) IsOpen() bool {
	if client.closed {
		return false
	}
	return client.apnsConn != nil
}

func (client *Client) IsClosed() bool {
	return !client.IsOpen()
}

func (client *Client) Open() error {
	if client.apnsConn == nil {
		return client.openConnection()
	}
	return nil
}

func (client *Client) openConnection() error {
	err := client.getCertificate()
	if err != nil {
		client.ctx.Errorf("Error getting cert: %v", err)
		return err
	}

	gatewayParts := strings.Split(client.Gateway, ":")
	conf := &tls.Config{
		Certificates: []tls.Certificate{client.certificate},
		ServerName:   gatewayParts[0],
	}

	conn, err := client.DialFunction(client.Gateway)
	if err != nil {
		client.ctx.Errorf("Error dialing on gateway: %v, %v", client.Gateway, err)
		return err
	}

	tlsConn := tls.Client(conn, conf)
	err = tlsConn.Handshake()
	if err != nil {
		client.ctx.Errorf("Error doing handshake: %v", err)
		return err
	}

	client.apnsConn = tlsConn
	client.initChans()
	client.closed = false
	go client.loop()
	return nil
}

func (client *Client) initChans() {
	client.pushNotifCh = make(chan *PushNotification)
	client.FailCh = make(chan *PushNotificationResponse)

	client.SocketCloseCh = make(chan struct{})

	client.doneCh = make(chan struct{})

	client.apnsRespCh = make(chan []byte)
}

func (client *Client) Close() {
	client.ctx.Debugf("Closing %s", client.Gateway)

	client.Lock()
	defer client.Unlock()

	if client.apnsConn == nil {
		client.ctx.Infof("apns connection nil so not closing any channels on client close")
		return
	}
	close(client.SocketCloseCh)
	close(client.doneCh)
	client.apnsConn.Close()
	client.apnsConn = nil
	client.closed = true
}

func (client *Client) EnqueuePushNotif(pn *PushNotification) error {
	select {
	case client.pushNotifCh <- pn:
		return nil
	case <- client.doneCh:
		return errors.New("Done channel was fired probably because client was closed.")
	case <- time.Tick(10 * time.Second):
{'fraction_non_alphanumeric': 0.1072717895822589, 'fraction_numerical': 0.006962351727694688, 'mean_word_length': 3.9533844189016603, 'pattern_counts': {'":': 2, '<': 9, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4567308', 'n_tokens_mistral': 2636, 'n_tokens_neox': 2501, 'n_words': 772}