<commit_before>//Package for creating a connection to Apple's APNS gateway and facilitating
//sending push notifications via that gateway
package apns

import (
	"bytes"
	"container/list"
	"crypto/tls"
	"encoding/binary"
	"encoding/hex"
	"errors"
	"fmt"
	"net"
	"sync"
	"time"
)

//Config for creating an APNS Connection
type APNSConfig struct {
	//number of payloads to keep for error purposes, defaults to 10000
	InFlightPayloadBufferSize int
	//number of milliseconds between frame flushes, defaults to 10
	FramingTimeout int
	//max number of bytes allowed in payload, defaults to 256
	MaxPayloadSize int
	//bytes for cert.pem : required
	CertificateBytes []byte
	//bytes for key.pem : required
	KeyBytes []byte
	//apple gateway, defaults to "gateway.push.apple.com"
	GatewayHost string
	//apple gateway port, defaults to "2195"
	GatewayPort string
	//max number of bytes to frame data to, defaults to TCP_FRAME_MAX
	//generally best to NOT set this and use the default
	MaxOutboundTCPFrameSize int
	//number of seconds to wait for connection before bailing, defaults to no timeout
	SocketTimeout int
	//number of seconds to wait for Tls handshake to complete before bailing, defaults to no timeout
	TlsTimeout int
}

//Object returned on a connection close or connection error
type ConnectionClose struct {
	//Any payload objects that weren't sent after a connection close
	UnsentPayloads *list.List
	//The error details returned from Apple
	Error *AppleError
	//The payload object that caused the error
	ErrorPayload *Payload
	//True if error payload wasn't found indicating some unsent payloads were lost
	UnsentPayloadBufferOverflow bool
}

//Details from Apple regarding a connection close
type AppleError struct {
	//Internal ID of the message that caused the error
	MessageID uint32
	//Error code returned by Apple (see APPLE_PUSH_RESPONSES)
	ErrorCode uint8
	//String name of error code
	ErrorString string
}

//APNS Connection state
type APNSConnection struct {
	//Channel to send payloads on
	SendChannel chan *Payload
	//Channel that connection close is received on
	CloseChannel chan *ConnectionClose
	//raw socket connection
	socket net.Conn
	//config
	config *APNSConfig
	//Buffer to hold payloads for replay
	inFlightPayloadBuffer *list.List
	//Stateful buffer to hold framed byte data
	inFlightFrameByteBuffer *bytes.Buffer
	//Stateful buffer to hold data while generating item bytes
	inFlightItemByteBuffer *bytes.Buffer
	//Mutex to sync access to Frame byte buffer
	inFlightBufferLock *sync.Mutex
	//Stateful counter to identify payloads for replay
	payloadIdCounter uint32
}

//Wrapper for associating an ID with a Payload object
type idPayload struct {
	//The Payload object
	Payload *Payload
	//The numerical id (from payloadIdCounter) for replay identification
	ID uint32
}

const (
	//Max number of bytes in a TCP frame
	TCP_FRAME_MAX = 65535
)

// This enumerates the response codes that Apple defines
// for push notification attempts.
var APPLE_PUSH_RESPONSES = map[uint8]string{
	0:   "NO_ERRORS",
	1:   "PROCESSING_ERROR",
	2:   "MISSING_DEVICE_TOKEN",
	3:   "MISSING_TOPIC",
	4:   "MISSING_PAYLOAD",
	5:   "INVALID_TOKEN_SIZE",
	6:   "INVALID_TOPIC_SIZE",
	7:   "INVALID_PAYLOAD_SIZE",
	8:   "INVALID_TOKEN",
	10:  "SHUTDOWN",
	128: "INVALID_FRAME_ITEM_ID", //this is not documented, but ran across it in testing
	255: "UNKNOWN",
}

//Create a new apns connection with supplied config
//If invalid config an error will be returned
//See APNSConfig object for defaults
func NewAPNSConnection(config *APNSConfig) (*APNSConnection, error) {
	errorStrs := ""

	if config.CertificateBytes == nil || config.KeyBytes == nil {
		errorStrs += "Invalid Key/Certificate bytes\n"
	}
	if config.InFlightPayloadBufferSize < 0 {
		errorStrs += "Invalid InFlightPayloadBufferSize. Should be > 0 (and probably around 10000)\n"
	}
	if config.MaxOutboundTCPFrameSize < 0 || config.MaxOutboundTCPFrameSize > TCP_FRAME_MAX {
		errorStrs += "Invalid MaxOutboundTCPFrameSize. Should be between 0 and TCP_FRAME_MAX (and probably above 256)\n"
	}
	if config.MaxPayloadSize < 0 {
		errorStrs += "Invalid MaxPayloadSize. Should be greater than 0.\n"
	}

	if errorStrs != "" {
		return nil, errors.New(errorStrs)
	}

	if config.InFlightPayloadBufferSize == 0 {
		config.InFlightPayloadBufferSize = 10000
	}
	if config.MaxOutboundTCPFrameSize == 0 {
		config.MaxOutboundTCPFrameSize = TCP_FRAME_MAX
	}
	if config.FramingTimeout == 0 {
		config.FramingTimeout = 10
	}
	if config.GatewayPort == "" {
		config.GatewayPort = "2195"
	}
	if config.GatewayHost == "" {
		config.GatewayHost = "gateway.push.apple.com"
	}
	if config.MaxPayloadSize == 0 {
		config.MaxPayloadSize = 256
	}

	x509Cert, err := tls.X509KeyPair(config.CertificateBytes, config.KeyBytes)
	if err != nil {
		//failed to validate key pair
		return nil, err
	}

	tlsConf := &tls.Config{
		Certificates: []tls.Certificate{x509Cert},
		ServerName:   config.GatewayHost,
	}

	tcpSocket, err := net.DialTimeout("tcp",
		config.GatewayHost+":"+config.GatewayPort,<commit_msg>Put in default tls deadline config value<commit_after>//Package for creating a connection to Apple's APNS gateway and facilitating
//sending push notifications via that gateway
package apns

import (
	"bytes"
	"container/list"
	"crypto/tls"
	"encoding/binary"
	"encoding/hex"
	"errors"
	"fmt"
	"net"
	"sync"
	"time"
)

//Config for creating an APNS Connection
type APNSConfig struct {
	//number of payloads to keep for error purposes, defaults to 10000
	InFlightPayloadBufferSize int
	//number of milliseconds between frame flushes, defaults to 10
	FramingTimeout int
	//max number of bytes allowed in payload, defaults to 256
	MaxPayloadSize int
	//bytes for cert.pem : required
	CertificateBytes []byte
	//bytes for key.pem : required
	KeyBytes []byte
	//apple gateway, defaults to "gateway.push.apple.com"
	GatewayHost string
	//apple gateway port, defaults to "2195"
	GatewayPort string
	//max number of bytes to frame data to, defaults to TCP_FRAME_MAX
	//generally best to NOT set this and use the default
	MaxOutboundTCPFrameSize int
	//number of seconds to wait for connection before bailing, defaults to no timeout
	SocketTimeout int
	//number of seconds to wait for Tls handshake to complete before bailing, defaults to no timeout
	TlsTimeout int
}

//Object returned on a connection close or connection error
type ConnectionClose struct {
	//Any payload objects that weren't sent after a connection close
	UnsentPayloads *list.List
	//The error details returned from Apple
	Error *AppleError
	//The payload object that caused the error
	ErrorPayload *Payload
	//True if error payload wasn't found indicating some unsent payloads were lost
	UnsentPayloadBufferOverflow bool
}

//Details from Apple regarding a connection close
type AppleError struct {
	//Internal ID of the message that caused the error
	MessageID uint32
	//Error code returned by Apple (see APPLE_PUSH_RESPONSES)
	ErrorCode uint8
	//String name of error code
	ErrorString string
}

//APNS Connection state
type APNSConnection struct {
	//Channel to send payloads on
	SendChannel chan *Payload
	//Channel that connection close is received on
	CloseChannel chan *ConnectionClose
	//raw socket connection
	socket net.Conn
	//config
	config *APNSConfig
	//Buffer to hold payloads for replay
	inFlightPayloadBuffer *list.List
	//Stateful buffer to hold framed byte data
	inFlightFrameByteBuffer *bytes.Buffer
	//Stateful buffer to hold data while generating item bytes
	inFlightItemByteBuffer *bytes.Buffer
	//Mutex to sync access to Frame byte buffer
	inFlightBufferLock *sync.Mutex
	//Stateful counter to identify payloads for replay
	payloadIdCounter uint32
}

//Wrapper for associating an ID with a Payload object
type idPayload struct {
	//The Payload object
	Payload *Payload
	//The numerical id (from payloadIdCounter) for replay identification
	ID uint32
}

const (
	//Max number of bytes in a TCP frame
	TCP_FRAME_MAX = 65535
)

// This enumerates the response codes that Apple defines
// for push notification attempts.
var APPLE_PUSH_RESPONSES = map[uint8]string{
	0:   "NO_ERRORS",
	1:   "PROCESSING_ERROR",
	2:   "MISSING_DEVICE_TOKEN",
	3:   "MISSING_TOPIC",
	4:   "MISSING_PAYLOAD",
	5:   "INVALID_TOKEN_SIZE",
	6:   "INVALID_TOPIC_SIZE",
	7:   "INVALID_PAYLOAD_SIZE",
	8:   "INVALID_TOKEN",
	10:  "SHUTDOWN",
	128: "INVALID_FRAME_ITEM_ID", //this is not documented, but ran across it in testing
	255: "UNKNOWN",
}

//Create a new apns connection with supplied config
//If invalid config an error will be returned
//See APNSConfig object for defaults
func NewAPNSConnection(config *APNSConfig) (*APNSConnection, error) {
	errorStrs := ""

	if config.CertificateBytes == nil || config.KeyBytes == nil {
		errorStrs += "Invalid Key/Certificate bytes\n"
	}
	if config.InFlightPayloadBufferSize < 0 {
		errorStrs += "Invalid InFlightPayloadBufferSize. Should be > 0 (and probably around 10000)\n"
	}
	if config.MaxOutboundTCPFrameSize < 0 || config.MaxOutboundTCPFrameSize > TCP_FRAME_MAX {
		errorStrs += "Invalid MaxOutboundTCPFrameSize. Should be between 0 and TCP_FRAME_MAX (and probably above 256)\n"
	}
	if config.MaxPayloadSize < 0 {
		errorStrs += "Invalid MaxPayloadSize. Should be greater than 0.\n"
	}

	if errorStrs != "" {
		return nil, errors.New(errorStrs)
	}

	if config.InFlightPayloadBufferSize == 0 {
		config.InFlightPayloadBufferSize = 10000
	}
	if config.MaxOutboundTCPFrameSize == 0 {
		config.MaxOutboundTCPFrameSize = TCP_FRAME_MAX
	}
	if config.FramingTimeout == 0 {
		config.FramingTimeout = 10
	}
	if config.GatewayPort == "" {
		config.GatewayPort = "2195"
	}
	if config.GatewayHost == "" {
		config.GatewayHost = "gateway.push.apple.com"
	}
	if config.MaxPayloadSize == 0 {
		config.MaxPayloadSize = 256
	}
	if config.TlsTimeout == 0 {
		config.TlsTimeout = 5
	}

	x509Cert, err := tls.X509KeyPair(config.CertificateBytes, config.KeyBytes)
	if err != nil {
		//failed to validate key pair
		return nil, err
	}

	tlsConf := &tls.Config{
		Certificates: []tls.Certificate{x509Cert},
		ServerName:   config.GatewayHost,
	}

	tcpSocket, err := net.DialTimeout("tcp",
		config.GatewayHost+":"+config.GatewayPort,
{'fraction_non_alphanumeric': 0.08120878039177085, 'fraction_numerical': 0.016930800275617677, 'mean_word_length': 4.25879917184265, 'pattern_counts': {'":': 2, '<': 9, '<?xml version=': 0, '>': 7, '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': '7168262', 'n_tokens_mistral': 3298, 'n_tokens_neox': 3118, 'n_words': 1114}