selfcare init
This commit is contained in:
8
proxy_go/.idea/.gitignore
generated
vendored
Normal file
8
proxy_go/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
6
proxy_go/.idea/encodings.xml
generated
Normal file
6
proxy_go/.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/redis" charset="GBK" />
|
||||
</component>
|
||||
</project>
|
||||
6
proxy_go/.idea/misc.xml
generated
Normal file
6
proxy_go/.idea/misc.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</component>
|
||||
</project>
|
||||
8
proxy_go/.idea/modules.xml
generated
Normal file
8
proxy_go/.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/selfcare_proxy_go.iml" filepath="$PROJECT_DIR$/.idea/selfcare_proxy_go.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
9
proxy_go/.idea/selfcare_proxy_go.iml
generated
Normal file
9
proxy_go/.idea/selfcare_proxy_go.iml
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
7
proxy_go/.idea/vcs.xml
generated
Normal file
7
proxy_go/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/public/protobuf@v1.5.0" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/public/protobuf@v1.5.2" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
11
proxy_go/.project
Normal file
11
proxy_go/.project
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>selfcare_proxy</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
177
proxy_go/MsgDef/canal.go
Normal file
177
proxy_go/MsgDef/canal.go
Normal file
@@ -0,0 +1,177 @@
|
||||
package MsgDef
|
||||
|
||||
const (
|
||||
TbAcctInfo = "tb_prd_prd_inst_551"
|
||||
TbOfrDetail = "tb_prd_ofr_detail_inst_551"
|
||||
TbTariff = "tb_bil_tariff"
|
||||
TbOfrInfo = "tb_prd_ofr"
|
||||
TbPricingStrategy = "tb_bil_evt_pricing_strategy"
|
||||
TbConfigArea = "config_area"
|
||||
TbPricingArea = "tb_bil_pricing_area"
|
||||
TbRr = "ratable_history"
|
||||
TbHoliday = "tb_bil_holiday_rel"
|
||||
TbBilHoliday = "tb_bil_holiday"
|
||||
TbSmsInfo = "tb_sms_info"
|
||||
TbSyncMobile = "tb_sync_mobile"
|
||||
)
|
||||
|
||||
type RecordChange struct {
|
||||
TableName string
|
||||
EventType string//entry.EventType
|
||||
|
||||
ChgAcctInfo []ChgAcctInfo
|
||||
ChgOfrDetail []ChgOfrDetail
|
||||
ChgTariff []ChgTariff
|
||||
ChgOfrInfo []ChgOfrInfo
|
||||
ChgPricingStrategy []ChgPricingStrategy
|
||||
ChgConfigArea []ChgConfigArea
|
||||
ChgPricingArea []ChgPricingArea
|
||||
ChgRr []ChgRr
|
||||
ChgHoliday []ChgHoliday
|
||||
ChgBilHoliday []ChgBilHoliday
|
||||
ChgAlertSms []ChgAlertSms
|
||||
ChgSyncMobile []ChgSyncMobile
|
||||
}
|
||||
|
||||
type ChgAcctInfo struct {
|
||||
PrdInstId int//string
|
||||
ServiceNbr string
|
||||
IfPrepaid string
|
||||
OldPrdInstStasId string
|
||||
NewPrdInstStasId string
|
||||
|
||||
BUpdate bool
|
||||
}
|
||||
|
||||
type ChgOfrDetail struct {
|
||||
OfrDetailInstId int//string
|
||||
OfrDetailTypeId string
|
||||
OldEffDate string
|
||||
NewEffDate string
|
||||
OldExpDate string
|
||||
NewExpDate string
|
||||
//OldCalcPriority string
|
||||
//NewCalcPriority string
|
||||
|
||||
BUpdate bool
|
||||
}
|
||||
|
||||
type ChgTariff struct {
|
||||
TariffId int//string
|
||||
}
|
||||
|
||||
type ChgOfrInfo struct {
|
||||
OfrId int//string
|
||||
OfrTypeId string
|
||||
IfPrepaid string
|
||||
OldCalcPriority string
|
||||
NewCalcPriority string
|
||||
OldOfrState string
|
||||
NewOfrState string
|
||||
|
||||
BUpdate bool
|
||||
}
|
||||
|
||||
type ChgPricingStrategy struct {
|
||||
EventPricingStrategyId int//string
|
||||
OldEventPriority string
|
||||
NewEventPriority string
|
||||
OldCallType string
|
||||
NewCallType string
|
||||
|
||||
BUpdate bool
|
||||
}
|
||||
|
||||
type ChgConfigArea struct {
|
||||
AreaId int//string
|
||||
OldAreaCode string
|
||||
NewAreaCode string
|
||||
OldState string
|
||||
NewState string
|
||||
|
||||
BUpdate bool
|
||||
}
|
||||
|
||||
type ChgPricingArea struct {
|
||||
OldStrategyId int//string
|
||||
NewStrategyId int//string
|
||||
OldAreaId int//string
|
||||
NewAreaId int//string
|
||||
|
||||
BUpdate bool
|
||||
}
|
||||
|
||||
type ChgRr struct {
|
||||
Id int64//tring
|
||||
OldBeginTime string
|
||||
NewBeginTime string
|
||||
OldEndTime string
|
||||
NewEndTime string
|
||||
OldFreeValue int64//string
|
||||
NewFreeValue int64//string
|
||||
OldValue int64//string
|
||||
NewValue int64//string
|
||||
OldOwnId string
|
||||
NewOwnId string
|
||||
OldOfrInstId string
|
||||
NewOfrInstId string
|
||||
OldPricingSubSectionId string
|
||||
NewPricingSubSectionId string
|
||||
|
||||
BUpdate bool
|
||||
}
|
||||
|
||||
type ChgHoliday struct {
|
||||
OldState string
|
||||
NewState string
|
||||
OldTariffId int//string
|
||||
NewTariffId int//string
|
||||
OldTariffSeq int//string
|
||||
NewTariffSeq int//string
|
||||
|
||||
BUpdate bool
|
||||
}
|
||||
|
||||
type ChgBilHoliday struct {
|
||||
HolidayId int//string
|
||||
OldState string
|
||||
NewState string
|
||||
|
||||
BUpdate bool
|
||||
}
|
||||
|
||||
type ChgAlertSms struct {
|
||||
AlertId int
|
||||
ServiceNbr string
|
||||
SmsContent string
|
||||
OldState int
|
||||
NewState int
|
||||
|
||||
BUpdate bool
|
||||
}
|
||||
|
||||
type ChgSyncMobile struct {
|
||||
PreId int `redis:"preId,omitempty"`// key
|
||||
OperType int `redis:"operType"`// == 1, new; 2, replace imsi
|
||||
State int `redis:"state"`// == 1
|
||||
//Imsi string
|
||||
ServiceNbr string `redis:"serviceNbr"`
|
||||
CustId int `redis:"custId"`
|
||||
AcctId int `redis:"acctId"`
|
||||
PrdInstId int `redis:"prdInstId"`
|
||||
MobileType int `redis:"mobileType"`// == 1, mobile type
|
||||
BirthDate string `redis:"birthDate"`// date
|
||||
Balance int `redis:"balance"`
|
||||
BalanceExpDate string `redis:"balanceExpDate"`// datetime
|
||||
OfrId int `redis:"ofrId"`
|
||||
|
||||
Imsi string `redis:"imsi"`
|
||||
Ki string `redis:"ki"`
|
||||
Opc string `redis:"opc"`
|
||||
VmsFlag int `redis:"vmsFlag"`
|
||||
ExpDate string `redis:"expDate"`// datetime
|
||||
CugId int `redis:"cugId"`
|
||||
|
||||
OldState int// == 1
|
||||
//BUpdate bool
|
||||
}
|
||||
95
proxy_go/MsgDef/msg_struct.go
Normal file
95
proxy_go/MsgDef/msg_struct.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package MsgDef
|
||||
|
||||
import (
|
||||
|
||||
)
|
||||
|
||||
//&prd_inst_id, &service_nbr, &prd_inst_stas_id, &acct_id, &cust_id
|
||||
type AcctData struct {
|
||||
AcctType int `redis:"acctType"`
|
||||
PrdInstId int `redis:"prdInstId"`
|
||||
ServiceNbr string `redis:"serviceNbr"`
|
||||
PrdInstStasId int `redis:"prdInstStasId"`
|
||||
AcctId int `redis:"acctId"`
|
||||
CustId int `redis:"custId"`
|
||||
OfrId int `redis:"ofrId"`
|
||||
OfrInstId int `redis:"ofrInstId"`
|
||||
EffTime string `redis:"effTime"`
|
||||
ExpTime string `redis:"expTime"`
|
||||
}
|
||||
|
||||
//SELECT rr.ID ratableId, rr.PRICING_SUB_SECTION_ID, rr.OFR_INST_ID, rr.BEGIN_TIME, rr.END_TIME,
|
||||
//rr.FREE_VALUE AS ratableValue, rr.`VALUE` usedRatable, g.MEASURE_DOMAIN, c.OFR_ID,
|
||||
//c.OFR_NAME, g.TARIFF_ID, g.START_REF_VALUE, g.END_REF_VALUE, c.CALC_PRIORITY, h.EVENT_PRIORITY
|
||||
type RrData struct {
|
||||
RrId int64 `redis:"rrId"`
|
||||
ServiceNbr string `redis:"serviceNbr"`
|
||||
PrdInstId int `redis:"prdInstId"`
|
||||
PricingSectionId int `redis:"pricingSectionId"`
|
||||
OfrInstId int `redis:"ofrInstId"`
|
||||
OfrId int `redis:"ofrId"`
|
||||
OfrName string `redis:"ofrName"`
|
||||
BeginTime string `redis:"beginTime"`
|
||||
EndTime string `redis:"endTime"`
|
||||
FreeValue int64 `redis:"freeValue"`
|
||||
UsedValue int64 `redis:"usedValue"`
|
||||
MeasureDomain string `redis:"measureDomain"`
|
||||
TariffId int `redis:"tariffId"`
|
||||
StartRefTime int `redis:"startRefTime"`
|
||||
EndRefTime int `redis:"endRefTime"`
|
||||
CalcPriority int `redis:"calcPriority"`
|
||||
EventPriority int `redis:"eventPriority"`
|
||||
StrategyId int `redis:"strategyId"`
|
||||
UnitFee int `redis:"unitFee"`
|
||||
RateUnit int `redis:"rateUnit"`
|
||||
}
|
||||
|
||||
// g.MEASURE_DOMAIN, time_to_sec(g.START_REF_VALUE) startTime, time_to_sec(g.END_REF_VALUE) endTime, g.SCALED_RATE_VALUE_ID feeUnit,
|
||||
// g.rate_unit rateUnit, c.CALC_PRIORITY,h.EVENT_PRIORITY,h.event_pricing_strategy_id,h.EVENT_PRICING_STRATEGY_NAME,c.ofr_id,c.OFR_NAME
|
||||
type TariffData struct {
|
||||
TariffId int `redis:"tariffId"`
|
||||
TariffSeq int `redis:"tariffSeq"`
|
||||
MeasureDomain string `redis:"measureDomain"`
|
||||
StartTime int `redis:"startTime"`
|
||||
EndTime int `redis:"endTime"`
|
||||
FeeUnit int `redis:"feeUnit"`
|
||||
RateUnit int `redis:"rateUnit"`
|
||||
CalcPriority int `redis:"calcPriority"`
|
||||
EventPriority int `redis:"eventPriority"`
|
||||
OfrId int `redis:"ofrId"`
|
||||
OfrName string `redis:"ofrName"`
|
||||
StrategyId int `redis:"strategyId"`
|
||||
StrategyName string `redis:"strategyName"`
|
||||
}
|
||||
|
||||
// SELECT DISTINCT ca.area_name, ca.area_code FROM TB_BIL_PRICING_AREA ar INNER JOIN config_area ca ON ca.area_id = ar.area_id WHERE ar.STRATEGY_ID = ?;
|
||||
type PrefixData struct {
|
||||
StrategyId int `redis:"StrategyId"`
|
||||
AreaName string `redis:"areaName"`
|
||||
AreaCode string `redis:"areaCode"`
|
||||
}
|
||||
|
||||
/*
|
||||
HD table import
|
||||
SELECT hr.ofr_id, hr.tariff_id, hr.tariff_seq, hr.holiday_group, hr.state hr_state, hr.holiday_discount,
|
||||
hd.HOLIDAY_ID, hd.STATE hd_state, hd.HOLIDAY_PRIORITY, hd.HOLIDAY_NAME, hd.HOLIDAY_TYPE, hd.HOLIDAY_BEGIN_DATE, hd.HOLIDAY_END_DATE
|
||||
FROM tb_bil_holiday_rel hr
|
||||
INNER JOIN tb_bil_holiday hd ON hr.holiday_group = hd.HOLIDAY_GROUP
|
||||
WHERE hr.tariff_id=? AND hr.tariff_seq=? AND hr.state = 1 AND hd.STATE = 'L0R';
|
||||
*/
|
||||
type HolidayDiscountData struct {
|
||||
OfrId int `redis:"ofrId"`
|
||||
TariffId int `redis:"tariffId"`
|
||||
TariffSeq int `redis:"tariffSeq"`
|
||||
HdGroup int `redis:"hdGroup"`
|
||||
HrState int `redis:"hrState"`
|
||||
HrDiscount int `redis:"hrDiscount"`
|
||||
|
||||
HdId int `redis:"hdId"`
|
||||
HdName string `redis:"hdName"`
|
||||
HdType string `redis:"hdType"`
|
||||
HdState string `redis:"hdState"`
|
||||
HdPriority int `redis:"hdPriority"`
|
||||
HdBeginDate string `redis:"hdBeginDate"`
|
||||
HdEndDate string `redis:"hdEndDate"`
|
||||
}
|
||||
35
proxy_go/Nmysql/alert_sms.go
Normal file
35
proxy_go/Nmysql/alert_sms.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package Nmysql
|
||||
|
||||
import (
|
||||
// "fmt"
|
||||
l4g "proxy/logger"
|
||||
)
|
||||
|
||||
func SetAlertSmsState2Sent(alertId int) error {
|
||||
stmt, _ := MySqlDb.Prepare("update tb_sms_info set sms_state=2 where sms_alert_id=?;")
|
||||
defer stmt.Close()
|
||||
|
||||
_, err := stmt.Exec(alertId)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("SetAlertSmsState2Sent err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
l4g.MysqlLog.Debugf("SetAlertSmsState2Sent succ!")
|
||||
return nil
|
||||
}
|
||||
|
||||
func InsertExpiredNotifySms(accountId uint32, planId uint32, expiredDate int64) error {
|
||||
stmt, _ := MySqlDb.Prepare("INSERT into tb_sync_msg SET msg_type=1, acct_id=?, ofr_inst_id=?, exp_date=FROM_UNIXTIME(?), state=1;")
|
||||
defer stmt.Close()
|
||||
|
||||
_, err := stmt.Exec(accountId, planId, expiredDate)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("InsertExpiredNotifySms acctId[%d], planId[%d], expiredDate[%d] err: %v", accountId, planId, expiredDate, err)
|
||||
return err
|
||||
}
|
||||
|
||||
l4g.MysqlLog.Debugf("InsertExpiredNotifySms acctId[%d], planId[%d], expiredDate[%d] succ!", accountId, planId, expiredDate)
|
||||
return nil
|
||||
}
|
||||
|
||||
72
proxy_go/Nmysql/create_acct.go
Normal file
72
proxy_go/Nmysql/create_acct.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package Nmysql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
l4g "proxy/logger"
|
||||
)
|
||||
|
||||
func QueryOfrIdByPrdInstId(prdInstId int) (int, error) {
|
||||
sqlStr := fmt.Sprintf("select OFR_ID from tb_prd_sub_prd_inst_551 where MAIN_PRD_INST_ID=%d limit 1;", prdInstId)
|
||||
rows, err := MySqlDb.Query(sqlStr)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("Query tb_prd_sub_prd_inst_551, prdInstId[%d] fail: %v", prdInstId, err)
|
||||
return 0, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var ofrId int
|
||||
for rows.Next(){
|
||||
if err = rows.Scan(&ofrId); err != nil {
|
||||
l4g.MysqlLog.Errorf("query row of TB_RESOURCE_EXAM fail: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return ofrId, nil
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func QueryOfrLevelByOfrId(ofrId int) (int, error) {
|
||||
sqlStr := fmt.Sprintf("select OFR_LEVEL from tb_prd_ofr where OFR_ID=%d limit 1;", ofrId)
|
||||
rows, err := MySqlDb.Query(sqlStr)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("Query tb_prd_ofr, ofrId[%d] fail: %v", ofrId, err)
|
||||
return 0, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var ofrLevel int
|
||||
for rows.Next(){
|
||||
if err = rows.Scan(&ofrLevel); err != nil {
|
||||
l4g.MysqlLog.Errorf("query row of tb_prd_ofr fail: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return ofrLevel, nil
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func QueryRentByOfrId(ofrId int) (int, error) {
|
||||
sqlStr := fmt.Sprintf("SELECT f.FEE_VALUE rentValue FROM tb_prd_prd_once_fee f WHERE f.PRD_ID = %d AND f.TYPE = 2 AND f.FEE_TYPE = 5;", ofrId)
|
||||
rows, err := MySqlDb.Query(sqlStr)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("Query tb_prd_prd_once_fee, ofrId[%d] fail: %v", ofrId, err)
|
||||
return 0, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var onceFee int
|
||||
for rows.Next(){
|
||||
if err = rows.Scan(&onceFee); err != nil {
|
||||
l4g.MysqlLog.Errorf("query row of tb_prd_prd_once_fee fail: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return onceFee, nil
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
4
proxy_go/Nmysql/map.go
Normal file
4
proxy_go/Nmysql/map.go
Normal file
@@ -0,0 +1,4 @@
|
||||
package Nmysql
|
||||
|
||||
type void struct{}
|
||||
var member void
|
||||
1311
proxy_go/Nmysql/mysql_db.go
Normal file
1311
proxy_go/Nmysql/mysql_db.go
Normal file
File diff suppressed because it is too large
Load Diff
175
proxy_go/Nmysql/provision.go
Normal file
175
proxy_go/Nmysql/provision.go
Normal file
@@ -0,0 +1,175 @@
|
||||
package Nmysql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
l4g "proxy/logger"
|
||||
)
|
||||
|
||||
func QuerySyncMobileInfo() ([]SyncMobileInfo, error) {
|
||||
sqlStr := fmt.Sprintf("select PRE_ID, OPER_TYPE, IMSI, SERVICE_NBR, KI, IFNULL(OPC,'') as OPC, CUST_ID, ACCT_ID, PRD_INST_ID, MOBILE_TYPE, VMS_FLAG, IFNULL(BIRTH_DATE,'') as BIRTH_DATE, BALANCE, BALANCE_EXP_DATE, OFR_ID, EXP_DATE, IFNULL(CUG_ID,0) as CUG_ID from tb_sync_mobile where STATE=1 limit 10;")
|
||||
rows, err := MySqlDb.Query(sqlStr)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("Query tb_sync_mobile fail: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var rsp []SyncMobileInfo= nil
|
||||
for rows.Next(){
|
||||
var si SyncMobileInfo
|
||||
if err = rows.Scan(&si.PreId, &si.OperType, &si.Imsi, &si.ServiceNbr, &si.Ki, &si.Opc, &si.CustId, &si.AcctId, &si.PrdInstId,
|
||||
&si.MobileType, &si.VmsFlag, &si.BirthDate, &si.Balance, &si.BalanceExpDate, &si.OfrId, &si.ExpDate, &si.CugId); err != nil {
|
||||
l4g.MysqlLog.Errorf("query row of tb_sync_mobile fail: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
if si.BirthDate == "" {
|
||||
si.BirthDate = "2000-01-01"
|
||||
}
|
||||
|
||||
rsp = append(rsp, si)
|
||||
}
|
||||
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
type SyncMobileInfo struct {
|
||||
PreId int// auto incr
|
||||
OperType int// 1: create subs, 2: replace IMSI
|
||||
Imsi string
|
||||
ServiceNbr string
|
||||
Ki string
|
||||
Opc string
|
||||
CustId int
|
||||
AcctId int
|
||||
PrdInstId int
|
||||
MobileType int//0: postpaid, 1: prepaid
|
||||
VmsFlag int//0: not VMS service, 1: VMS service
|
||||
BirthDate string
|
||||
Balance int
|
||||
BalanceExpDate string
|
||||
OfrId int
|
||||
ExpDate string
|
||||
CugId int// 1: staff, 2: customer
|
||||
}
|
||||
|
||||
func QueryOldImsiInfo(serviceNbr string) (*OldImsiInfo, error) {
|
||||
sqlStr := fmt.Sprintf("select PRE_ID, IMSI, PRD_INST_ID from tb_sync_mobile WHERE SERVICE_NBR='%s' and STATE>1 ORDER BY PRE_ID DESC limit 1;", serviceNbr)
|
||||
rows, err := MySqlDb.Query(sqlStr)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("Query tb_sync_mobile fail: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var rsp OldImsiInfo
|
||||
for rows.Next(){
|
||||
if err = rows.Scan(&rsp.PreId, &rsp.Imsi, &rsp.PrdInstId); err != nil {
|
||||
l4g.MysqlLog.Errorf("query row of tb_sync_mobile fail: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &rsp, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type OldImsiInfo struct {
|
||||
PreId int
|
||||
Imsi string
|
||||
PrdInstId int
|
||||
}
|
||||
|
||||
func AddSmcliCmd2Mysql(cmdType int, nt int, serviceNbr string, cmdStr string) error {
|
||||
sqlStr := fmt.Sprintf("INSERT INTO tb_sync_cn_cmd SET cmd_type=%d, node_type=%d, node_name='%s', srv_num='%s', command='%s', state=1, result=0, timestamp=NOW();",
|
||||
cmdType, nt, NodeName[nt], serviceNbr, cmdStr)
|
||||
stmt, _ := MySqlDb.Prepare(sqlStr)
|
||||
defer stmt.Close()
|
||||
|
||||
_, err := stmt.Exec()
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("AddSmcliCmd2Mysql, cmdType[%d], nt[%d], prdInstId[%s], cmd[%s] error: %v", cmdType, nt, serviceNbr, cmdStr, err)
|
||||
return err
|
||||
}
|
||||
|
||||
l4g.MysqlLog.Debugf("AddSmcliCmd2Mysql, cmdType[%d], nt[%d], prdInstId[%s], cmd[%s] succ!", cmdType, nt, serviceNbr, cmdStr)
|
||||
return nil
|
||||
}
|
||||
|
||||
func AddSmcliCmdSucc2Mysql(cmdType int, nt int, serviceNbr string, cmdStr string) error {
|
||||
cause := "000:Command Successful"
|
||||
sqlStr := fmt.Sprintf("INSERT INTO tb_sync_cn_cmd SET cmd_type=%d, node_type=%d, node_name='%s', srv_num='%s', command='%s', state=2, result=1, cause='%s', timestamp=NOW();",
|
||||
cmdType, nt, NodeName[nt], serviceNbr, cmdStr, cause)
|
||||
stmt, _ := MySqlDb.Prepare(sqlStr)
|
||||
defer stmt.Close()
|
||||
|
||||
_, err := stmt.Exec()
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("AddSmcliCmd2Mysql, cmdType[%d], nt[%d], prdInstId[%s], cmd[%s] error: %v", cmdType, nt, serviceNbr, cmdStr, err)
|
||||
return err
|
||||
}
|
||||
|
||||
l4g.MysqlLog.Debugf("AddSmcliCmd2Mysql, cmdType[%d], nt[%d], prdInstId[%s], cmd[%s] succ!", cmdType, nt, serviceNbr, cmdStr)
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateSyncMobileState(preId int, state int) error {
|
||||
sqlStr := fmt.Sprintf("UPDATE tb_sync_mobile set STATE=%d where PRE_ID=%d;", state, preId)
|
||||
stmt, _ := MySqlDb.Prepare(sqlStr)
|
||||
defer stmt.Close()
|
||||
|
||||
_, err := stmt.Exec()
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("UpdateSyncMobileState, preId[%d], state[%d] error: %v", preId, state, err)
|
||||
return err
|
||||
}
|
||||
|
||||
l4g.MysqlLog.Debugf("UpdateSyncMobileState, preId[%d], state[%d] succ!", preId, state)
|
||||
return nil
|
||||
}
|
||||
|
||||
func QuerySyncCnCmd() ([]CnCmdInfo, error) {
|
||||
sqlStr := fmt.Sprintf("select id, node_type, command from tb_sync_cn_cmd where state=1 limit 40;")
|
||||
rows, err := MySqlDb.Query(sqlStr)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("Query tb_sync_cn_cmd fail: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var rsp []CnCmdInfo= nil
|
||||
for rows.Next(){
|
||||
var si CnCmdInfo
|
||||
if err = rows.Scan(&si.Id, &si.NodeType, &si.Command); err != nil {
|
||||
l4g.MysqlLog.Errorf("query row of tb_sync_cn_cmd fail: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rsp = append(rsp, si)
|
||||
}
|
||||
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
type CnCmdInfo struct {
|
||||
Id int// auto incr
|
||||
NodeType int
|
||||
Command string
|
||||
}
|
||||
|
||||
func UpdateCnCmdResult(id int, state int, result int, desc string) error {
|
||||
sqlStr := fmt.Sprintf("update tb_sync_cn_cmd set state=%d,result=%d,cause='%s',timestamp=NOW() where id=%d;",
|
||||
state, result, desc, id)
|
||||
stmt, _ := MySqlDb.Prepare(sqlStr)
|
||||
defer stmt.Close()
|
||||
|
||||
_, err := stmt.Exec()
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("UpdateCnCmdResult, id[%d], state[%d], result[%d], cause[%s] error: %v", id, state, result, desc, err)
|
||||
return err
|
||||
}
|
||||
|
||||
l4g.MysqlLog.Debugf("UpdateCnCmdResult, id[%d], state[%d], result[%d], cause[%s] succ!", id, state, result, desc)
|
||||
return nil
|
||||
}
|
||||
|
||||
29
proxy_go/Nmysql/smcli_cmd.go
Normal file
29
proxy_go/Nmysql/smcli_cmd.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package Nmysql
|
||||
|
||||
const (
|
||||
CT_CREATE_NA int= 0
|
||||
CT_CREATE_SUB int= 1
|
||||
CT_REPLACE_SUB int= 2
|
||||
CT_SUSPEND_ACCOUNT int= 3
|
||||
CT_DISABLE_ACCOUNT int= 4
|
||||
CT_RESUME_ACCOUNT int= 5
|
||||
CT_DISCONNECT_ACCOUNT int= 6
|
||||
CT_UPDATE_SUB int= 7
|
||||
CT_MAX int= 8
|
||||
)
|
||||
|
||||
const (
|
||||
NT_NA int= 0
|
||||
NT_OCS int= 1
|
||||
NT_HSS int= 2
|
||||
NT_AUC int= 3
|
||||
NT_VMS int= 4
|
||||
NT_PSTN int= 5
|
||||
NT_MSAN int= 6
|
||||
NT_ADSL int= 7
|
||||
NT_PCRF int= 8
|
||||
NT_MAX int= 9
|
||||
)
|
||||
|
||||
var NodeName = []string{"NULL","OCS","HSS","AUC","VMS","PSTN","MSAN","ADSL","PCRF"}
|
||||
|
||||
68
proxy_go/Nmysql/update_plan_usage.go
Normal file
68
proxy_go/Nmysql/update_plan_usage.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package Nmysql
|
||||
|
||||
import (
|
||||
l4g "proxy/logger"
|
||||
)
|
||||
|
||||
func UpdateDataPlanInfo(bundleId uint32, thisTimeAdded uint64, opFlag bool) error {
|
||||
var sqlStr string
|
||||
if !opFlag {
|
||||
sqlStr = "UPDATE ratable_history SET `VALUE`= `VALUE`+? WHERE ID=?;"
|
||||
} else {
|
||||
sqlStr = "UPDATE ratable_history SET `VALUE`= `VALUE`-? WHERE ID=?;"
|
||||
}
|
||||
stmt, _ := MySqlDb.Prepare(sqlStr)
|
||||
defer stmt.Close()
|
||||
|
||||
_, err := stmt.Exec(thisTimeAdded, bundleId)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("UpdateDataPlanInfo for bundle[%d], added[%d], op[%t] error: %v", bundleId, thisTimeAdded, opFlag, err)
|
||||
return err
|
||||
}
|
||||
|
||||
l4g.MysqlLog.Debugf("UpdateDataPlanInfo for bundle[%d], added[%d], op[%t] succ!", bundleId, thisTimeAdded, opFlag)
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateVoiceAndSmsPlanInfo(serviceType byte, bundleId uint32, usedValue uint64, totalValue uint64, updateExpiryDate bool) error {
|
||||
var sqlStr string
|
||||
var value uint64
|
||||
if updateExpiryDate {
|
||||
sqlStr = "UPDATE ratable_history SET `VALUE`=? WHERE ID=?;"
|
||||
value = totalValue
|
||||
} else {
|
||||
sqlStr = "UPDATE ratable_history SET `VALUE`=? WHERE ID=?;"
|
||||
value = usedValue
|
||||
}
|
||||
stmt, _ := MySqlDb.Prepare(sqlStr)
|
||||
defer stmt.Close()
|
||||
|
||||
_, err := stmt.Exec(value, bundleId)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("UpdateVoiceAndSmsPlanInfo for bundle[%d], value[%d] error: %v", bundleId, value, err)
|
||||
return err
|
||||
}
|
||||
|
||||
l4g.MysqlLog.Debugf("UpdateVoiceAndSmsPlanInfo for bundle[%d], value[%d] succ!", bundleId, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func AddNotificationSms(accountId uint32, planId uint32, serviceType byte, updateExpiryDate bool, rateUsed int) error {
|
||||
var sqlStr string= "INSERT into tb_sync_msg SET msg_type=2, rate_type=?, acct_id=?, rate_value=?, state=1, ofr_inst_id=?;"
|
||||
rate := rateUsed
|
||||
//if updateExpiryDate {
|
||||
// rate = 100
|
||||
//}
|
||||
|
||||
stmt, _ := MySqlDb.Prepare(sqlStr)
|
||||
defer stmt.Close()
|
||||
|
||||
_, err := stmt.Exec(serviceType, accountId, rate, planId)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("AddNotificationSms for plan[%d], acct[%d], type[%d], rate[%d/100] error: %v", planId, accountId, serviceType, rate, err)
|
||||
return err
|
||||
}
|
||||
|
||||
l4g.MysqlLog.Debugf("AddNotificationSms for plan[%d], acct[%d], type[%d], rate[%d/100] succ!", planId, accountId, serviceType, rate)
|
||||
return nil
|
||||
}
|
||||
303
proxy_go/Nmysql/user_status.go
Normal file
303
proxy_go/Nmysql/user_status.go
Normal file
@@ -0,0 +1,303 @@
|
||||
package Nmysql
|
||||
|
||||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
. "proxy/config"
|
||||
l4g "proxy/logger"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func UpdateUserStatus2Mdb(accountId uint32, status byte) error {
|
||||
statusInCrm := 1001
|
||||
switch status {
|
||||
case 1:
|
||||
statusInCrm = 1001
|
||||
case 2:
|
||||
statusInCrm = 1205
|
||||
case 4:
|
||||
statusInCrm = 1101
|
||||
case 6:
|
||||
statusInCrm = 1202
|
||||
}
|
||||
|
||||
if false {
|
||||
stmt, _ := MySqlDb.Prepare("update tb_prd_prd_inst_551 set PRD_INST_STAS_ID=?, MOD_DATE=NOW() where ACCT_ID=?;")
|
||||
defer stmt.Close()
|
||||
|
||||
_, _ = stmt.Exec(statusInCrm, accountId)
|
||||
}
|
||||
|
||||
if status == 4 {
|
||||
stmt, _ := MySqlDb.Prepare("INSERT INTO tb_mobile_disc_cmd set ACCT_ID=?, ACCT_STATUS=0, UPDATE_DATE=NOW();")
|
||||
defer stmt.Close()
|
||||
|
||||
_, err := stmt.Exec(accountId)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("UpdateUserStatus2Mdb acctId[%d] err: %v", accountId, err)
|
||||
//return err
|
||||
}
|
||||
|
||||
l4g.MysqlLog.Debugf("UpdateUserStatus2Mdb acctId[%d] succ!", accountId)
|
||||
|
||||
_ = InsertPrepaidUserDeleteCliCmd(accountId)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func InsertPrepaidUserDeleteCliCmd(accountId uint32) error {
|
||||
//sqlStr := "select PRE_ID, OPER_TYPE, IMSI, SERVICE_NBR, KI, OPC, CUST_ID, STATE, PRD_INST_ID, MOBILE_TYPE, VMS_FLAG, BIRTH_DATE, BALANCE, "
|
||||
//sqlStr += "BALANCE_EXP_DATE, OFR_ID, EXP_DATE, CUG_ID from tb_sync_mobile where ACCT_ID=? ORDER BY PRE_ID DESC limit 1;"
|
||||
sqlStr := "select IMSI, SERVICE_NBR, PRD_INST_ID "
|
||||
sqlStr += "from tb_sync_mobile where ACCT_ID=" + strconv.Itoa(int(accountId)) + " ORDER BY PRE_ID DESC limit 1;"
|
||||
rows, err := MySqlDb.Query(sqlStr)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("Query tb_sync_mobile, accountId[%d] fail: %v", accountId, err)
|
||||
return err
|
||||
}
|
||||
//defer rows.Close()
|
||||
|
||||
var imsi, msisdn string
|
||||
var prdInstId int
|
||||
for rows.Next(){
|
||||
if err = rows.Scan(&imsi, &msisdn, &prdInstId); err != nil {
|
||||
l4g.MysqlLog.Errorf("query row of tb_sync_mobile fail: %v", err)
|
||||
_ = rows.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
_ = rows.Close()
|
||||
|
||||
if imsi != "" {
|
||||
cmd := "delete subscriber -imsi " + imsi
|
||||
stmt, _ := MySqlDb.Prepare("INSERT INTO tb_sync_cn_cmd SET cmd_type=?, node_type=?, node_name=?, srv_num=?, command=?, state=1, result=0, timestamp=NOW();")
|
||||
//defer stmt.Close()
|
||||
_, err := stmt.Exec(CT_DISCONNECT_ACCOUNT, NT_HSS, NodeName[NT_HSS], prdInstId, cmd)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("add cmd[%s], msisdn[%s], prdInstId[%d] err: %v", cmd, msisdn, prdInstId, err)
|
||||
//return err
|
||||
} else {
|
||||
l4g.MysqlLog.Debugf("add cmd[%s], msisdn[%s], prdInstId[%d]", cmd, msisdn, prdInstId)
|
||||
}
|
||||
stmt.Close()
|
||||
|
||||
cmd = "delete aucSubscriber -imsi " + imsi
|
||||
stmt, _ = MySqlDb.Prepare("INSERT INTO tb_sync_cn_cmd SET cmd_type=?, node_type=?, node_name=?, srv_num=?, command=?, state=1, result=0, timestamp=NOW();")
|
||||
//defer stmt.Close()
|
||||
_, err = stmt.Exec(CT_DISCONNECT_ACCOUNT, NT_AUC, NodeName[NT_AUC], prdInstId, cmd)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("add cmd[%s], msisdn[%s], prdInstId[%d] err: %v", cmd, msisdn, prdInstId, err)
|
||||
//return err
|
||||
} else {
|
||||
l4g.MysqlLog.Debugf("add cmd[%s], msisdn[%s], prdInstId[%d]", cmd, msisdn, prdInstId)
|
||||
}
|
||||
stmt.Close()
|
||||
|
||||
cmd = "delete vmsSubscriber -msisdn " + msisdn
|
||||
stmt, _ = MySqlDb.Prepare("INSERT INTO tb_sync_cn_cmd SET cmd_type=?, node_type=?, node_name=?, srv_num=?, command=?, state=1, result=0, timestamp=NOW();")
|
||||
//defer stmt.Close()
|
||||
_, err = stmt.Exec(CT_DISCONNECT_ACCOUNT, NT_VMS, NodeName[NT_VMS], prdInstId, cmd)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("add cmd[%s], msisdn[%s], prdInstId[%d] err: %v", cmd, msisdn, prdInstId, err)
|
||||
//return err
|
||||
} else {
|
||||
l4g.MysqlLog.Debugf("add cmd[%s], msisdn[%s], prdInstId[%d]", cmd, msisdn, prdInstId)
|
||||
}
|
||||
stmt.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CrmDeleteSubsProfile(serviceNbr string, acctType string, acctId string) {
|
||||
if acctType == "1" {
|
||||
//sqlStr := "select PRE_ID, OPER_TYPE, IMSI, SERVICE_NBR, KI, OPC, CUST_ID, STATE, PRD_INST_ID, MOBILE_TYPE, VMS_FLAG, BIRTH_DATE, BALANCE, "
|
||||
//sqlStr += "BALANCE_EXP_DATE, OFR_ID, EXP_DATE, CUG_ID from tb_sync_mobile where ACCT_ID=? ORDER BY PRE_ID DESC limit 1;"
|
||||
sqlStr := "select IMSI, SERVICE_NBR, PRD_INST_ID, MOBILE_TYPE, VMS_FLAG "
|
||||
sqlStr += "from tb_sync_mobile where ACCT_ID=" + acctId + " ORDER BY PRE_ID DESC limit 1;"
|
||||
rows, err := MySqlDb.Query(sqlStr)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("Query tb_sync_mobile, accountId[%d] fail: %v", acctId, err)
|
||||
return
|
||||
}
|
||||
//defer rows.Close()
|
||||
|
||||
var imsi, msisdn string
|
||||
var prdInstId, mobileType, vmsFlag int
|
||||
for rows.Next(){
|
||||
if err = rows.Scan(&imsi, &msisdn, &prdInstId, &mobileType, &vmsFlag); err != nil {
|
||||
l4g.MysqlLog.Errorf("query row of tb_sync_mobile fail: %v", err)
|
||||
_ = rows.Close()
|
||||
return
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
_ = rows.Close()
|
||||
|
||||
if imsi != "" {
|
||||
cmd := "delete subscriber -imsi " + imsi
|
||||
stmt, _ := MySqlDb.Prepare("INSERT INTO tb_sync_cn_cmd SET cmd_type=?, node_type=?, node_name=?, srv_num=?, command=?, state=1, result=0, timestamp=NOW();")
|
||||
//defer stmt.Close()
|
||||
_, err := stmt.Exec(CT_DISCONNECT_ACCOUNT, NT_HSS, NodeName[NT_HSS], strconv.Itoa(prdInstId), cmd)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("add cmd[%s], msisdn[%s], prdInstId[%d] err: %v", cmd, msisdn, prdInstId, err)
|
||||
//return err
|
||||
} else {
|
||||
l4g.MysqlLog.Debugf("add cmd[%s], msisdn[%s], prdInstId[%d]", cmd, msisdn, prdInstId)
|
||||
}
|
||||
stmt.Close()
|
||||
|
||||
cmd = "delete aucSubscriber -imsi " + imsi
|
||||
stmt, _ = MySqlDb.Prepare("INSERT INTO tb_sync_cn_cmd SET cmd_type=?, node_type=?, node_name=?, srv_num=?, command=?, state=1, result=0, timestamp=NOW();")
|
||||
//defer stmt.Close()
|
||||
_, err = stmt.Exec(CT_DISCONNECT_ACCOUNT, NT_AUC, NodeName[NT_AUC], strconv.Itoa(prdInstId), cmd)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("add cmd[%s], msisdn[%s], prdInstId[%d] err: %v", cmd, msisdn, prdInstId, err)
|
||||
//return err
|
||||
} else {
|
||||
l4g.MysqlLog.Debugf("add cmd[%s], msisdn[%s], prdInstId[%d]", cmd, msisdn, prdInstId)
|
||||
}
|
||||
stmt.Close()
|
||||
|
||||
if mobileType == 1{
|
||||
cmd = "delete ppsSubscriber -msisdn " + msisdn
|
||||
stmt, _ = MySqlDb.Prepare("INSERT INTO tb_sync_cn_cmd SET cmd_type=?, node_type=?, node_name=?, srv_num=?, command=?, state=2, result=1, cause='0000:Command successful', timestamp=NOW();")
|
||||
//defer stmt.Close()
|
||||
_, err = stmt.Exec(CT_DISCONNECT_ACCOUNT, NT_OCS, NodeName[NT_OCS], strconv.Itoa(prdInstId), cmd)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("add cmd[%s], msisdn[%s], prdInstId[%d] err: %v", cmd, msisdn, prdInstId, err)
|
||||
//return err
|
||||
} else {
|
||||
l4g.MysqlLog.Debugf("add cmd[%s], msisdn[%s], prdInstId[%d]", cmd, msisdn, prdInstId)
|
||||
}
|
||||
stmt.Close()
|
||||
}
|
||||
|
||||
if vmsFlag == 1{
|
||||
cmd = "delete vmsSubscriber -msisdn " + msisdn
|
||||
stmt, _ = MySqlDb.Prepare("INSERT INTO tb_sync_cn_cmd SET cmd_type=?, node_type=?, node_name=?, srv_num=?, command=?, state=1, result=0, timestamp=NOW();")
|
||||
//defer stmt.Close()
|
||||
_, err = stmt.Exec(CT_DISCONNECT_ACCOUNT, NT_VMS, NodeName[NT_VMS], strconv.Itoa(prdInstId), cmd)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("add cmd[%s], msisdn[%s], prdInstId[%d] err: %v", cmd, msisdn, prdInstId, err)
|
||||
//return err
|
||||
} else {
|
||||
l4g.MysqlLog.Debugf("add cmd[%s], msisdn[%s], prdInstId[%d]", cmd, msisdn, prdInstId)
|
||||
}
|
||||
stmt.Close()
|
||||
}
|
||||
}
|
||||
} else if acctType == "2" {
|
||||
ssEntryIdInCrm := Config.Provision.SsEntryIdInCrm
|
||||
orderId := queryOrderIdByServiceNbr(serviceNbr)
|
||||
if orderId == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
examNumStr := queryResourceExamNumByOrderId(orderId)
|
||||
if examNumStr == "" {
|
||||
return
|
||||
}
|
||||
kronePairId, _ := strconv.Atoi(examNumStr)
|
||||
if kronePairId <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
examNumStr = querySsEntryByResIdAndOrderId(ssEntryIdInCrm, orderId)
|
||||
if examNumStr == "" {
|
||||
return
|
||||
}
|
||||
ssEntry, _ := strconv.Atoi(examNumStr)
|
||||
|
||||
cmdDelPstn := fmt.Sprintf("delete pstn user isdn=%s, krone_pair_id=%d, ssentry=%d, state=Deleted, account_id=%s;",
|
||||
serviceNbr,kronePairId, ssEntry,acctId)
|
||||
insertCmdDelPstn(cmdDelPstn, serviceNbr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func insertCmdDelPstn(cmd string, serviceNbr string) {
|
||||
stmt, _ := MySqlDb.Prepare("INSERT INTO tb_sync_cn_cmd SET cmd_type=?, node_type=?, node_name=?, srv_num=?, command=?, state=1, result=1, timestamp=NOW();")
|
||||
//defer stmt.Close()
|
||||
_, err := stmt.Exec(CT_DISCONNECT_ACCOUNT, NT_PSTN, NodeName[NT_PSTN], serviceNbr, cmd)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("insertCmdDelPstn cmd[%s], msisdn[%s] err: %v", cmd, serviceNbr, err)
|
||||
//return err
|
||||
} else {
|
||||
l4g.MysqlLog.Debugf("insertCmdDelPstn cmd[%s], msisdn[%s]", cmd, serviceNbr)
|
||||
}
|
||||
stmt.Close()
|
||||
}
|
||||
|
||||
func queryOrderIdByServiceNbr(serviceNbr string) int {
|
||||
sqlStr := "select ORDER_ID from tb_sync_pstn where SERVICE_NBR=" + serviceNbr + " ORDER by ORDER_ID desc limit 1;"
|
||||
rows, err := MySqlDb.Query(sqlStr)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("Query tb_sync_pstn, serviceNbr[%s] fail: %v", serviceNbr, err)
|
||||
return 0
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var orderId int
|
||||
for rows.Next(){
|
||||
if err = rows.Scan(&orderId); err != nil {
|
||||
l4g.MysqlLog.Errorf("query row of tb_sync_pstn fail: %v", err)
|
||||
//_ = rows.Close()
|
||||
return 0
|
||||
}
|
||||
|
||||
return orderId
|
||||
}
|
||||
//_ = rows.Close()
|
||||
return 0
|
||||
}
|
||||
|
||||
func queryResourceExamNumByOrderId(orderId int) string {
|
||||
sqlStr := "SELECT C.RESOURCE_EXAM_NUM FROM TB_PRD_RES_INST_551 A, TB_PRD_RES_INST_DETAIL_551 B, TB_RESOURCE_EXAM C "
|
||||
sqlStr += "WHERE A.RES_INST_ID=B.RES_INST_ID AND B.RESOURCE_EXAM_ID=C.RESOURCE_EXAM_ID AND C.RESOURCE_ID=1046 AND A.ORDER_ID=" + strconv.Itoa(orderId)+";"
|
||||
rows, err := MySqlDb.Query(sqlStr)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("Query TB_RESOURCE_EXAM, orderId[%d] fail: %v", orderId, err)
|
||||
return ""
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var resExamNum string
|
||||
for rows.Next(){
|
||||
if err = rows.Scan(&resExamNum); err != nil {
|
||||
l4g.MysqlLog.Errorf("query row of TB_RESOURCE_EXAM fail: %v", err)
|
||||
//_ = rows.Close()
|
||||
return ""
|
||||
}
|
||||
|
||||
return resExamNum
|
||||
}
|
||||
//_ = rows.Close()
|
||||
return ""
|
||||
}
|
||||
|
||||
func querySsEntryByResIdAndOrderId(resId, orderId int) string {
|
||||
sqlStr := fmt.Sprintf("%s%sC.RESOURCE_ID=%d AND A.ORDER_ID=%d;",
|
||||
"SELECT C.RESOURCE_EXAM_NUM FROM TB_PRD_RES_INST_551 A, TB_PRD_RES_INST_DETAIL_551 B, TB_RESOURCE_EXAM C ",
|
||||
"WHERE A.RES_INST_ID=B.RES_INST_ID AND B.RESOURCE_EXAM_ID=C.RESOURCE_EXAM_ID AND ", resId, orderId)
|
||||
rows, err := MySqlDb.Query(sqlStr)
|
||||
if err != nil {
|
||||
l4g.MysqlLog.Errorf("Query TB_RESOURCE_EXAM, orderId[%d] fail: %v", orderId, err)
|
||||
return ""
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var resExamNum string
|
||||
for rows.Next(){
|
||||
if err = rows.Scan(&resExamNum); err != nil {
|
||||
l4g.MysqlLog.Errorf("query row of TB_RESOURCE_EXAM fail: %v", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return resExamNum
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
115
proxy_go/Nredis/acctount.go
Normal file
115
proxy_go/Nredis/acctount.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package Nredis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
. "proxy/MsgDef"
|
||||
l4g "proxy/logger"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// "Acct:"+acctData.ServiceNbr
|
||||
func RdbDelAcctRecord(serviceNbr string) {
|
||||
var cursor uint64= 0
|
||||
for {
|
||||
keys, cursorNew, err := rdb.SScan(ctx, "Acct:"+serviceNbr, cursor, "", 0).Result()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("SScan Acct:%s, err: %v", serviceNbr, err)
|
||||
break
|
||||
}
|
||||
|
||||
//fmt.Printf("\nfound %d keys\n", len(keys))
|
||||
for _, key := range keys {
|
||||
acctKey := "Acct:"+serviceNbr+":"+key
|
||||
err := rdb.Del(ctx, acctKey).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Del %s, err: %v", acctKey, err)
|
||||
} else {
|
||||
//subFields := strings.Split(key, ":")
|
||||
//if len(subFields) == 3 && subFields[2] != "" {
|
||||
_ = rdb.SRem(ctx, "Acct:"+serviceNbr, key)
|
||||
//return srem.Err()
|
||||
//}
|
||||
}
|
||||
}
|
||||
if cursorNew == 0 {
|
||||
break
|
||||
} else {
|
||||
cursor = cursorNew
|
||||
}
|
||||
}
|
||||
_ = rdb.Del(ctx, "Acct:"+serviceNbr).Err()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func RdbDelAcctOfr(serviceNbr string, ofrId int) error {
|
||||
acctKey := "Acct:"+serviceNbr+":"+strconv.Itoa(ofrId)
|
||||
err := rdb.Del(ctx, acctKey).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Del %s, err: %v", acctKey, err)
|
||||
} else {
|
||||
_ = rdb.SRem(ctx, "Acct:"+serviceNbr, strconv.Itoa(ofrId))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func RdbSetAcctRecord(acctData *AcctData) error {
|
||||
//&prd_inst_id, &service_nbr, &prd_inst_stas_id, &acct_id, &cust_id
|
||||
key := "Acct:"+acctData.ServiceNbr+":"+strconv.Itoa(acctData.OfrId)
|
||||
err := rdb.HMSet(ctx, key, "acctType", acctData.AcctType, "prdInstId", acctData.PrdInstId, "serviceNbr", acctData.ServiceNbr,
|
||||
"prdInstStasId", acctData.PrdInstStasId, "acctId", acctData.AcctId, "custId", acctData.CustId, "ofrId", acctData.OfrId, "ofrInstId", acctData.OfrInstId,
|
||||
"effTime", acctData.EffTime, "expTime", acctData.ExpTime).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("HMSet Acct:%s, err: %v", acctData.ServiceNbr, err)
|
||||
} else {
|
||||
sadd := rdb.SAdd(ctx, "Acct:"+acctData.ServiceNbr, strconv.Itoa(acctData.OfrId))
|
||||
if sadd.Err() != nil {
|
||||
l4g.RedisLog.Errorf("SAdd Acct:%s %d, err: %v", acctData.ServiceNbr, acctData.OfrId, sadd.Err())
|
||||
return sadd.Err()
|
||||
} else {
|
||||
l4g.RedisLog.Debugf("SAdd Acct:%s %d", acctData.ServiceNbr, acctData.OfrId)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func RdbGetAcctRecordByServiceNbr(serviceNbr string) (rsp string, err error) {
|
||||
var cursor uint64= 0
|
||||
for {
|
||||
keys, cursorNew, err := rdb.SScan(ctx, "Acct:"+serviceNbr, cursor, "", 0).Result()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("SScan Acct:%s, err: %v", serviceNbr, err)
|
||||
break
|
||||
}
|
||||
|
||||
//fmt.Printf("\nfound %d keys\n", len(keys))
|
||||
for _, key := range keys {
|
||||
acctKey := "Acct:"+serviceNbr+":"+key
|
||||
res := rdb.HGetAll(ctx, acctKey)
|
||||
if res.Err() != nil {
|
||||
l4g.RedisLog.Errorf("HGetAll %s, err: %v", acctKey, err)
|
||||
} else {
|
||||
var acctData AcctData
|
||||
if err = res.Scan(&acctData); err != nil {
|
||||
l4g.RedisLog.Errorf("HGetAll Scan %s, err: %v", acctKey, err)
|
||||
} else {
|
||||
l4g.RedisLog.Debugf("Get %s, [%v]", acctKey, acctData)
|
||||
rsp += fmt.Sprintf("%s:***************************************\r\nPrdInstId: %d\r\nPrdInstStasId: %d\r\nCustId: %d\r\nAcctId: %d\r\n",
|
||||
acctKey, acctData.PrdInstId, acctData.PrdInstStasId, acctData.CustId, acctData.AcctId)
|
||||
rsp += fmt.Sprintf("OfrId: %d\r\nOfrInstId: %d\r\nEffTime: %s\r\nExpTime: %s\r\n",
|
||||
acctData.OfrId, acctData.OfrInstId, acctData.EffTime, acctData.ExpTime)
|
||||
return rsp, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if cursorNew == 0 {
|
||||
break
|
||||
} else {
|
||||
cursor = cursorNew
|
||||
}
|
||||
}
|
||||
|
||||
return "", err
|
||||
}
|
||||
63
proxy_go/Nredis/alert_sms.go
Normal file
63
proxy_go/Nredis/alert_sms.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package Nredis
|
||||
|
||||
import (
|
||||
l4g "proxy/logger"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func RdbSetAlertSmsRecord(alertId int, serviceNbr, smsContent string) error {
|
||||
key := "AlertSms:"+serviceNbr+":"+strconv.Itoa(alertId)
|
||||
err := rdb.Set(ctx, key, smsContent, 0).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Set %s, err: %v", key, err)
|
||||
} else {
|
||||
l4g.RedisLog.Debugf("Set %s, err: %s", key, smsContent)
|
||||
sadd := rdb.SAdd(ctx, "Msg2OcsSet", key)
|
||||
if sadd.Err() != nil {
|
||||
l4g.RedisLog.Errorf("SAdd Msg2OcsSet %s, err: %v", key, sadd.Err())
|
||||
return sadd.Err()
|
||||
} else {
|
||||
l4g.RedisLog.Debugf("SAdd Msg2OcsSet %s", key)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func RdbGetAlertSmsContent(key string) string {
|
||||
var sms string
|
||||
var err error
|
||||
sms, err = rdb.Get(ctx, key).Result()
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return sms
|
||||
}
|
||||
}
|
||||
|
||||
func RdbScanAlertSmsRecord() []string {
|
||||
var cursor uint64= 0
|
||||
var keysFound []string
|
||||
for {
|
||||
keys, cursor, err := rdb.SScan(ctx, "Msg2OcsSet", cursor, "", 50).Result()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("SScan Msg2OcsSet, err: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
keysFound = append(keysFound, keys...)
|
||||
|
||||
if cursor == 0 {
|
||||
break
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return keysFound
|
||||
}
|
||||
|
||||
func RdsDelMsg2OcsKey(key string) {
|
||||
_ = rdb.SRem(ctx, "Msg2OcsSet", key)
|
||||
_ = rdb.Del(ctx, key).Err()
|
||||
}
|
||||
169
proxy_go/Nredis/bundleclr.go
Normal file
169
proxy_go/Nredis/bundleclr.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package Nredis
|
||||
|
||||
import (
|
||||
"proxy/MsgDef"
|
||||
. "proxy/MsgDef"
|
||||
l4g "proxy/logger"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func CheckIfRrExpired(rr *MsgDef.RrData) bool {
|
||||
if rr == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if rr.MeasureDomain == "01" || rr.MeasureDomain == "03" {
|
||||
if rr.UsedValue >= rr.FreeValue {
|
||||
return true
|
||||
}
|
||||
nowStr := time.Now().Format("20060102150405")
|
||||
if strings.Compare(nowStr, rr.EndTime) > 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if rr.MeasureDomain == "02" {
|
||||
nowStr := time.Now().Format("20060102150405")
|
||||
if strings.Compare(nowStr, rr.EndTime) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func RdbGetRrByKey(key string) (*RrData, error) {
|
||||
res := rdb.HGetAll(ctx, key)
|
||||
err := res.Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("HGetAll %s, err: %v", key, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var rr RrData
|
||||
if err = res.Scan(&rr); err != nil {
|
||||
l4g.RedisLog.Errorf("HGetAll Scan %s, err: %v", key, err)
|
||||
return nil, err
|
||||
} else {
|
||||
//l4g.RedisLog.Debugf("Get %s, [%v]", key, rr)
|
||||
}
|
||||
|
||||
return &rr, err
|
||||
}
|
||||
|
||||
func RdbClrExpireRrRecord() error {
|
||||
l4g.RedisLog.Warnf("cron clear expire rr!")
|
||||
var cursor uint64= 0
|
||||
var err error
|
||||
for {
|
||||
var keys []string
|
||||
keys, cursor, err = rdb.Scan(ctx, cursor, "Rr:*", 30).Result()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Scan Rr:*, err: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
subFields := strings.Split(key, ":")
|
||||
if len(subFields) == 4 {
|
||||
rr, err1 := RdbGetRrByKey(key)
|
||||
if rr != nil && err1 == nil && CheckIfRrExpired(rr) {
|
||||
err1 = rdb.Del(ctx, key).Err()
|
||||
if err1 != nil {
|
||||
l4g.RedisLog.Errorf("Del Rr[%s], err: %v", key, err1)
|
||||
} else {
|
||||
l4g.RedisLog.Warnf("Del Rr[%s] succ", key)
|
||||
|
||||
l4g.RedisLog.Warnf("SRem RrSet:%s:%s %s", subFields[1], subFields[2], subFields[3])
|
||||
_ = rdb.SRem(ctx, "RrSet:"+subFields[1]+":"+subFields[2], subFields[3])
|
||||
l4g.RedisLog.Warnf("Del RrId:%s", subFields[3])
|
||||
_ = rdb.Del(ctx, "RrId:"+subFields[3]).Err()
|
||||
//return srem.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func RdbGetAcctPlanByKey(key string) (*AcctData, error) {
|
||||
res := rdb.HGetAll(ctx, key)
|
||||
err := res.Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("HGetAll %s, err: %v", key, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var acct AcctData
|
||||
if err = res.Scan(&acct); err != nil {
|
||||
l4g.RedisLog.Errorf("HGetAll Scan %s, err: %v", key, err)
|
||||
return nil, err
|
||||
} else {
|
||||
//l4g.RedisLog.Debugf("Get %s, [%v]", key, acct)
|
||||
}
|
||||
|
||||
return &acct, err
|
||||
}
|
||||
|
||||
func CheckIfAcctExpired(acct *MsgDef.AcctData) bool {
|
||||
if acct == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
nowStr := time.Now().Format("2006-01-02 15:04:05")
|
||||
if strings.Compare(nowStr, acct.ExpTime[:len(nowStr)]) > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func RdbClrExpirePlanRecord() error {
|
||||
l4g.RedisLog.Warnf("cron clear expire plan!")
|
||||
var cursor uint64= 0
|
||||
var err error
|
||||
for {
|
||||
var keys []string
|
||||
keys, cursor, err = rdb.Scan(ctx, cursor, "Acct:*", 30).Result()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Scan Acct:*, err: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
subFields := strings.Split(key, ":")
|
||||
if len(subFields) == 3 {
|
||||
acct, err1 := RdbGetAcctPlanByKey(key)
|
||||
if acct != nil && err1 == nil && CheckIfAcctExpired(acct) {
|
||||
err1 = rdb.Del(ctx, key).Err()
|
||||
if err1 != nil {
|
||||
l4g.RedisLog.Errorf("Del Acct Plan[%s], err: %v", key, err1)
|
||||
} else {
|
||||
l4g.RedisLog.Warnf("Del Acct Plan[%s] succ", key)
|
||||
|
||||
l4g.RedisLog.Warnf("SRem Acct:%s %s", subFields[1], subFields[2])
|
||||
_ = rdb.SRem(ctx, "Acct:"+subFields[1], subFields[2])
|
||||
//return srem.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func ClrExpRes() {
|
||||
_ = RdbClrExpireRrRecord()
|
||||
_ = RdbClrExpirePlanRecord()
|
||||
}
|
||||
40
proxy_go/Nredis/canal_info.go
Normal file
40
proxy_go/Nredis/canal_info.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package Nredis
|
||||
|
||||
import (
|
||||
l4g "proxy/logger"
|
||||
)
|
||||
|
||||
type BinLogPos struct {
|
||||
File string `redis:"file"`
|
||||
Position uint32 `redis:"position"`
|
||||
}
|
||||
|
||||
func RdbSetBinLogPos(file string, pos uint32) error {
|
||||
err := rdb.HMSet(ctx, "binlogpos", "file", file, "position", pos).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("HMSet binlogpos %s %d, err: %v", file, pos, err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func RdbGetBinLogPos() *BinLogPos {
|
||||
// Get the map. The same approach works for HmGet().
|
||||
res := rdb.HGetAll(ctx, "binlogpos")
|
||||
err := res.Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("HGetAll binlogpos, err: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Scan the results into the struct.
|
||||
var pos BinLogPos
|
||||
if err = res.Scan(&pos); err != nil {
|
||||
l4g.RedisLog.Errorf("HGetAll Scan binlogpos, err: %v", err)
|
||||
return nil
|
||||
} else {
|
||||
l4g.RedisLog.Debugf("Get binlogpos, [%v]", pos)
|
||||
return &pos
|
||||
}
|
||||
}
|
||||
|
||||
47
proxy_go/Nredis/create_acct.go
Normal file
47
proxy_go/Nredis/create_acct.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package Nredis
|
||||
|
||||
import (
|
||||
"proxy/MsgDef"
|
||||
l4g "proxy/logger"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func RdbSetCreateAcctRecord(r *MsgDef.ChgSyncMobile) error {
|
||||
key := "CreateAcct:"+r.ServiceNbr+":"+strconv.Itoa(r.PreId)
|
||||
err := rdb.HMSet(ctx, key, "prdInstId", r.PrdInstId, "serviceNbr", r.ServiceNbr,
|
||||
"operType", r.OperType, "state", r.State, "ofrId", r.OfrId,
|
||||
"custId", r.CustId, "acctId", r.AcctId, "mobileType", r.MobileType, "balance", r.Balance, "birthDate", r.BirthDate, "balanceExpDate", r.BalanceExpDate).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("HMSet %s, err: %v", key, err)
|
||||
} else {
|
||||
l4g.RedisLog.Debugf("HMSet %s, [%#v]", key, r)
|
||||
sadd := rdb.SAdd(ctx, "Msg2OcsSet", key)
|
||||
if sadd.Err() != nil {
|
||||
l4g.RedisLog.Errorf("SAdd Msg2OcsSet %s, err: %v", key, sadd.Err())
|
||||
return sadd.Err()
|
||||
} else {
|
||||
l4g.RedisLog.Debugf("SAdd Msg2OcsSet %s", key)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func RdbGetCreateAcct(key string) *MsgDef.ChgSyncMobile {
|
||||
res := rdb.HGetAll(ctx, key)
|
||||
err := res.Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("HGetAll %s, err: %v", key, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Scan the results into the struct.
|
||||
var acctInfo MsgDef.ChgSyncMobile
|
||||
if err = res.Scan(&acctInfo); err != nil {
|
||||
l4g.RedisLog.Errorf("HGetAll Scan %s, err: %v", key, err)
|
||||
return nil
|
||||
} else {
|
||||
l4g.RedisLog.Debugf("Get %s, [%v]", key, acctInfo)
|
||||
return &acctInfo
|
||||
}
|
||||
}
|
||||
30
proxy_go/Nredis/holiday.go
Normal file
30
proxy_go/Nredis/holiday.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package Nredis
|
||||
|
||||
import (
|
||||
l4g "proxy/logger"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func SetHdId2TariffId(id int, tariffId int) bool {
|
||||
err := rdb.Set(ctx, "HdId:"+strconv.Itoa(id), strconv.Itoa(tariffId), 0).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Set HdId:%d %s, err: %v", id, strconv.Itoa(tariffId), err)
|
||||
return false
|
||||
} else {
|
||||
l4g.RedisLog.Infof("Set HdId:%d %s", id, tariffId)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func GetTariffIdByHdId(id int) string {
|
||||
var tariffIdStr string
|
||||
var err error
|
||||
tariffIdStr, err = rdb.Get(ctx, "HdId:"+strconv.Itoa(id)).Result()
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return tariffIdStr
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
33
proxy_go/Nredis/plan_expiry.go
Normal file
33
proxy_go/Nredis/plan_expiry.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package Nredis
|
||||
|
||||
import (
|
||||
l4g "proxy/logger"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func CheckIfBundleLimitSent(planId uint32, rate int) bool {
|
||||
n, err := rdb.Exists(ctx, "BundleLimit:"+strconv.Itoa(int(planId))+":"+strconv.Itoa(rate)).Result()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Check Exists PlanExpiry:%d, err: %v", planId, err)
|
||||
return false
|
||||
} else {
|
||||
if n >= 1 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func SetPlanExpire(planId uint32, rate int) error {
|
||||
key := "BundleLimit:"+strconv.Itoa(int(planId))+":"+strconv.Itoa(rate)
|
||||
err := rdb.Set(ctx, key, "1", time.Hour * 24 * 15).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Set BundleLimit:%d:%d, err: %v", planId, rate, err)
|
||||
} else {
|
||||
l4g.RedisLog.Debugf("Set BundleLimit:%d:%d, succ", planId, rate)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
601
proxy_go/Nredis/rds_client.go
Normal file
601
proxy_go/Nredis/rds_client.go
Normal file
@@ -0,0 +1,601 @@
|
||||
package Nredis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
rds "github.com/go-redis/redis/v8"
|
||||
"os"
|
||||
l4g "proxy/logger" //"github.com/sirupsen/logrus"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
. "proxy/MsgDef"
|
||||
)
|
||||
|
||||
var ctx = context.Background()
|
||||
|
||||
var rdb *rds.Client
|
||||
var locRdb *rds.Client
|
||||
func NewRedisClient(netType, addr, passwd string, sentinels []string) {
|
||||
if sentinels != nil {
|
||||
rdb = rds.NewFailoverClient(&rds.FailoverOptions{
|
||||
MasterName: "mymaster",
|
||||
SentinelAddrs: sentinels,
|
||||
Password: passwd,
|
||||
DB: 0,
|
||||
PoolSize: 5,
|
||||
})
|
||||
locRdb = rds.NewClient(&rds.Options{
|
||||
Network: netType,// The network type, either tcp or unix. Default is tcp.
|
||||
Addr: addr,// can be: /var/run/redis/redis-server.sock???
|
||||
Password: passwd, // no password set
|
||||
DB: 0, // use default DB
|
||||
PoolSize: 5,
|
||||
})
|
||||
} else {
|
||||
rdb = rds.NewClient(&rds.Options{
|
||||
Network: netType,// The network type, either tcp or unix. Default is tcp.
|
||||
Addr: addr,// can be: /var/run/redis/redis-server.sock???
|
||||
Password: passwd, // no password set
|
||||
DB: 0, // use default DB
|
||||
PoolSize: 5,
|
||||
})
|
||||
locRdb = rdb
|
||||
}
|
||||
|
||||
pong, err := rdb.Ping(ctx).Result()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("%v, err: %v", pong, err)// Output: PONG <nil>
|
||||
os.Exit(1)
|
||||
} else {
|
||||
l4g.RedisLog.Debugf("%v", pong)// Output: PONG <nil>
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func CheckIfNeedToSyncDb() (bool, error) {
|
||||
/*if CheckIfRdbMaster() == false {
|
||||
return false, nil
|
||||
}*/
|
||||
|
||||
size, err := rdb.DBSize(ctx).Result()
|
||||
if err == nil {
|
||||
if size < 10 {
|
||||
return true, nil
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
} else {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
func ClrTariffAndBundle() {
|
||||
rdb.FlushDB(ctx)
|
||||
/*n, err := rdb.Del(ctx, "Acct:*").Result()
|
||||
if err != nil {
|
||||
l4g.Errorf("Del Acct, err: %v", err)
|
||||
} else {
|
||||
l4g.Debugf("Del Acct, num[%d]", n)
|
||||
}
|
||||
|
||||
n, err = rdb.Del(ctx, "Rr:*").Result()
|
||||
if err != nil {
|
||||
l4g.Errorf("Del Rr, err: %v", err)
|
||||
} else {
|
||||
l4g.Debugf("Del Rr, num[%d]", n)
|
||||
}
|
||||
|
||||
n, err = rdb.Del(ctx, "Prefix:*").Result()
|
||||
if err != nil {
|
||||
l4g.Errorf("Del Prefix, err: %v", err)
|
||||
} else {
|
||||
l4g.Debugf("Del Prefix, num[%d]", n)
|
||||
}
|
||||
|
||||
n, err = rdb.Del(ctx, "Tariff:*").Result()
|
||||
if err != nil {
|
||||
l4g.Errorf("Del Tariff, err: %v", err)
|
||||
} else {
|
||||
l4g.Debugf("Del Tariff, num[%d]", n)
|
||||
}*/
|
||||
}
|
||||
|
||||
func CheckIfRdbMaster() bool {
|
||||
info := locRdb.Info(ctx, "Replication")
|
||||
if strings.Contains(info.Val(), "role:master") || strings.Contains(info.Val(), "role:active-replica") {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func SetRrId2RrKey(id int64, rrKey string) bool {
|
||||
err := rdb.Set(ctx, "RrId:"+strconv.FormatInt(id, 10), rrKey, 0).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Set RrId:%d %s, err: %v", id, rrKey, err)
|
||||
return false
|
||||
} else {
|
||||
l4g.RedisLog.Infof("Set RrId:%d %s", id, rrKey)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func GetRrKeyByRrId(id int64) string {
|
||||
var rrKey string
|
||||
var err error
|
||||
rrKey, err = rdb.Get(ctx, "RrId:"+strconv.FormatInt(id, 10)).Result()
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return rrKey
|
||||
}
|
||||
}
|
||||
|
||||
func RdbSetRrRecord(rr *RrData) error {
|
||||
key := "Rr:"+rr.ServiceNbr+":"+rr.MeasureDomain+":"+strconv.FormatInt(rr.RrId, 10)
|
||||
err := rdb.HMSet(ctx, key, "prdInstId", rr.PrdInstId, "serviceNbr", rr.ServiceNbr,
|
||||
"pricingSectionId", rr.PricingSectionId, "ofrInstId", rr.OfrInstId, "ofrId", rr.OfrId,
|
||||
"ofrName", rr.OfrName, "beginTime", rr.BeginTime, "endTime", rr.EndTime, "freeValue", rr.FreeValue, "rateUnit", rr.RateUnit, "unitFee", rr.UnitFee,
|
||||
"usedValue", rr.UsedValue, "measureDomain", rr.MeasureDomain, "tariffId", rr.TariffId, "strategyId", rr.StrategyId,
|
||||
"startRefTime", rr.StartRefTime, "endRefTime", rr.EndRefTime, "calcPriority", rr.CalcPriority, "eventPriority", rr.EventPriority).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("HMSet Rr:%s, err: %v", rr.ServiceNbr, err)
|
||||
} else {
|
||||
l4g.RedisLog.Debugf("HMSet Rr:%s, [%#v]", rr.ServiceNbr, rr)
|
||||
sadd := rdb.SAdd(ctx, "RrSet:"+rr.ServiceNbr+":"+rr.MeasureDomain, strconv.FormatInt(rr.RrId, 10))
|
||||
if sadd.Err() != nil {
|
||||
l4g.RedisLog.Errorf("SAdd RrSet:%s:%s %d, err: %v", rr.ServiceNbr, rr.MeasureDomain, rr.RrId, sadd.Err())
|
||||
return sadd.Err()
|
||||
} else {
|
||||
l4g.RedisLog.Debugf("SAdd RrSet:%s:%s %d", rr.ServiceNbr, rr.MeasureDomain, rr.RrId)
|
||||
}
|
||||
SetRrId2RrKey(rr.RrId, key)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func RdbSetStrategyEventPriority(rr *RrData) error {
|
||||
err := rdb.HMSet(ctx, "Rr:"+rr.ServiceNbr+":"+rr.MeasureDomain+":"+strconv.FormatInt(rr.RrId, 10), "eventPriority", rr.EventPriority).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("HMSet Rr:%s, err: %v", rr.ServiceNbr, err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func RdbSetOfrCalcPriority(rr *RrData) error {
|
||||
err := rdb.HMSet(ctx, "Rr:"+rr.ServiceNbr+":"+rr.MeasureDomain+":"+strconv.FormatInt(rr.RrId, 10), "calcPriority", rr.CalcPriority).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("HMSet Rr:%s, err: %v", rr.ServiceNbr, err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// "Rr:"+rr.ServiceNbr+":"+rr.MeasureDomain+":"+strconv.Itoa(rr.RrId)
|
||||
func RdbUpdateRrRecordOnly(rrId int64, ratableVal, usedVal int64, beginTime, endTime string) (int, error) {
|
||||
key := GetRrKeyByRrId(rrId)
|
||||
if key != "" {
|
||||
err := rdb.HMSet(ctx, key, "beginTime", beginTime, "endTime", endTime, "freeValue", ratableVal,
|
||||
"usedValue", usedVal).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("HMSet %s, err: %v", key, err)
|
||||
return -1, err
|
||||
} else {
|
||||
l4g.RedisLog.Debugf("HMSet %s, beginTime[%s], endTime[%s], free/used:[%d/%d]", key, beginTime, endTime, ratableVal, usedVal)
|
||||
return 1, nil
|
||||
}
|
||||
} else {
|
||||
l4g.RedisLog.Errorf("HMSet rrId[%d], err not found, beginTime[%s], endTime[%s], free/used:[%d/%d]", rrId, beginTime, endTime, ratableVal, usedVal)
|
||||
return 0, nil
|
||||
}
|
||||
/*var cursor uint64
|
||||
var err error
|
||||
for {
|
||||
var keys []string
|
||||
// 20 keys each scan
|
||||
keys, cursor, err = rdb.Scan(ctx, cursor, "Rr:*:"+strconv.FormatInt(rrId, 10), 2).Result()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Scan Rr:*:%d, err: %v", rrId, err)
|
||||
break
|
||||
}
|
||||
|
||||
//fmt.Printf("\nfound %d keys\n", len(keys))
|
||||
for _, key := range keys {
|
||||
err := rdb.HMSet(ctx, key, "beginTime", beginTime, "endTime", endTime, "freeValue", ratableVal,
|
||||
"usedValue", usedVal).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("HMSet %s, err: %v", key, err)
|
||||
}
|
||||
}
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return err*/
|
||||
}
|
||||
|
||||
// "Rr:"+rr.ServiceNbr+":"+rr.MeasureDomain+":"+strconv.Itoa(rr.RrId)
|
||||
func RdbDelRrRecordByRrId(rrId int64) error {
|
||||
key := GetRrKeyByRrId(rrId)
|
||||
if key != "" {
|
||||
err := rdb.Del(ctx, key).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Del %s, err: %v", key, err)
|
||||
} else {
|
||||
l4g.RedisLog.Infof("Del %s succ", key)
|
||||
subFields := strings.Split(key, ":")
|
||||
if len(subFields) == 4 {
|
||||
_ = rdb.SRem(ctx, "RrSet:"+subFields[1]+":"+subFields[2], strconv.FormatInt(rrId, 10))
|
||||
l4g.RedisLog.Infof("SRem %d from RrSet:%s:%s", rrId, subFields[1], subFields[2])
|
||||
//return srem.Err()
|
||||
}
|
||||
|
||||
}
|
||||
rdb.Del(ctx, "RrId:"+strconv.FormatInt(rrId, 10))
|
||||
l4g.RedisLog.Infof("Del RrId:%d", rrId)
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// "Rr:"+rr.ServiceNbr+":"+rr.MeasureDomain+":"+strconv.Itoa(rr.RrId)
|
||||
func RdbDelRrRecordByServiceNbr(serviceNbr string) error {
|
||||
var cursor uint64
|
||||
var err error
|
||||
for {
|
||||
var keys []string
|
||||
// 20 keys each scan
|
||||
keys, cursor, err = rdb.Scan(ctx, cursor, "Rr:"+serviceNbr+":*", 30).Result()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Scan Rr:%s:*, err: %v", serviceNbr, err)
|
||||
break
|
||||
}
|
||||
|
||||
//fmt.Printf("\nfound %d keys\n", len(keys))
|
||||
for _, key := range keys {
|
||||
err = rdb.Del(ctx, key).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Del %s, err: %v", key, err)
|
||||
} else {
|
||||
subFields := strings.Split(key, ":")
|
||||
if len(subFields) == 4 {
|
||||
_ = rdb.SRem(ctx, "RrSet:"+serviceNbr+":"+subFields[2], subFields[3])
|
||||
//return srem.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func RdbDelRrRecord(rr *RrData) error {
|
||||
err := rdb.Del(ctx, "Rr:"+rr.ServiceNbr+":"+rr.MeasureDomain+":"+strconv.FormatInt(rr.RrId, 10)).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Del Rr:%s:%d, err: %v", rr.ServiceNbr, rr.RrId, err)
|
||||
} else {
|
||||
_ = rdb.SRem(ctx, "RrSet:"+rr.ServiceNbr+":"+rr.MeasureDomain, strconv.FormatInt(rr.RrId, 10))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func RdbSetOfrRecord(ofrId int) error {
|
||||
err := rdb.Set(ctx, "Ofr:"+strconv.Itoa(ofrId), ofrId, 0).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Set Ofr:%d, err: %v", ofrId, err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func RdbCheckIfOfrExist(ofrId int) bool {
|
||||
n, err := rdb.Exists(ctx, "Ofr:"+strconv.Itoa(ofrId)).Result()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Check Exists Ofr:%d, err: %v", ofrId, err)
|
||||
return false
|
||||
} else {
|
||||
if n >= 1 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func RdbSetPrefixRecord(prefix *PrefixData) error {
|
||||
sadd := rdb.SAdd(ctx, "Prefix:"+strconv.Itoa(prefix.StrategyId), prefix.AreaCode)
|
||||
return sadd.Err()
|
||||
}
|
||||
|
||||
// Add holiday discount table
|
||||
// Hd:tariff_id:tariff_seq:hdId, group, name, discount, state;;; type, subState, priority, begin, end;
|
||||
// triggers: 1, create, modify(state), delete of tb_bil_holiday_rel;
|
||||
// 2, modify(STATE), delete of tb_bil_holiday;
|
||||
/* holiday discount
|
||||
SELECT hd.HOLIDAY_GROUP, hd.HOLIDAY_PRIORITY, COUNT(1) AS HOLIDAY_COUNT FROM tb_bil_holiday hd
|
||||
WHERE (((hd.HOLIDAY_TYPE = '02' AND hd.HOLIDAY_BEGIN_DATE <= (WEEKDAY(SYSDATE()) + 1) AND hd.holiday_end_date >= (WEEKDAY(SYSDATE()) + 1))
|
||||
OR (hd.holiday_type = '03' AND DATE_FORMAT(SYSDATE(),'%m%d') >= DATE_FORMAT(CONCAT('2020', hd.HOLIDAY_BEGIN_DATE), '%m%d') AND DATE_FORMAT(SYSDATE(),'%m%d') <= DATE_FORMAT(CONCAT('2020', hd.holiday_end_date), '%m%d'))
|
||||
OR (hd.holiday_type = '04' AND DATE_FORMAT(SYSDATE(), '%H%i%s') >= DATE_FORMAT(STR_TO_DATE(CONCAT('20200101', hd.HOLIDAY_BEGIN_DATE), '%Y%m%d%H%i%s'),'%H%i%s')
|
||||
AND DATE_FORMAT(SYSDATE(), '%H%i%s') <= DATE_FORMAT(STR_TO_DATE(CONCAT('20200101', hd.holiday_end_date), '%Y%m%d%H%i%s'),'%H%i%s')))
|
||||
and hd.STATE='L0R')
|
||||
GROUP BY hd.HOLIDAY_PRIORITY, hd.HOLIDAY_GROUP;
|
||||
*/
|
||||
func RdbSetHolidayDiscountRecord(hd *HolidayDiscountData) error {
|
||||
err := rdb.HMSet(ctx, "Hd:"+strconv.Itoa(hd.TariffId)+":"+strconv.Itoa(hd.HdId),
|
||||
"ofrId", hd.OfrId, "tariffId", hd.TariffId, "tariffSeq", hd.TariffSeq, "hdGroup", hd.HdGroup,
|
||||
"hrState", hd.HrState, "hrDiscount", hd.HrDiscount, "hdId", hd.HdId, "hdName", hd.HdName,
|
||||
"hdType", hd.HdType, "hdState", hd.HdState, "hdPriority", hd.HdPriority,
|
||||
"hdBeginDate", hd.HdBeginDate, "hdEndDate", hd.HdEndDate).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("HMSet Hd:%d:%d, err: %v", hd.TariffId, hd.HdId, err)
|
||||
} else {
|
||||
sadd := rdb.SAdd(ctx, "HdSet:"+strconv.Itoa(hd.TariffId), strconv.Itoa(hd.HdId))
|
||||
if sadd.Err() != nil {
|
||||
l4g.RedisLog.Errorf("SAdd HdSet:%d %d, err: %v", hd.TariffId, hd.HdId, sadd.Err())
|
||||
return sadd.Err()
|
||||
}
|
||||
SetHdId2TariffId(hd.HdId, hd.TariffId)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func RdbDelHolidayDiscountRecord(tariffId int) {
|
||||
var cursor uint64= 0
|
||||
for {
|
||||
// 20 keys each scan
|
||||
keys, cursorNew, err := rdb.SScan(ctx, "HdSet:"+strconv.Itoa(tariffId), cursor, "", 0).Result()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("SScan HdSet:%d, err: %v", tariffId, err)
|
||||
break
|
||||
}
|
||||
|
||||
//fmt.Printf("\nfound %d keys\n", len(keys))
|
||||
for _, key := range keys {
|
||||
hdKey := "Hd:"+strconv.Itoa(tariffId) + ":" + key
|
||||
err := rdb.Del(ctx, hdKey).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Del %s, err: %v", key, err)
|
||||
} else {
|
||||
//subFields := strings.Split(key, ":")
|
||||
//if len(subFields) == 3 && subFields[2] != "" {
|
||||
_ = rdb.SRem(ctx, "HdSet:"+strconv.Itoa(tariffId), key)
|
||||
//return srem.Err()
|
||||
//}
|
||||
}
|
||||
}
|
||||
if cursorNew == 0 {
|
||||
break
|
||||
} else {
|
||||
cursor = cursorNew
|
||||
}
|
||||
}
|
||||
_ = rdb.Del(ctx, "HdSet:"+strconv.Itoa(tariffId)).Err()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func RdbDelHolidayDiscountRecordByHd(holidayId int) error {
|
||||
tariffIdStr := GetTariffIdByHdId(holidayId)
|
||||
tariffId, err := strconv.Atoi(tariffIdStr)
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Get tariffId by HdId %d, err: %v", holidayId, err)
|
||||
} else {
|
||||
key := "Hd:" + strconv.Itoa(tariffId) + ":"+strconv.Itoa(holidayId)
|
||||
err := rdb.Del(ctx, key).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Del %s, err: %v", key, err)
|
||||
} else {
|
||||
_ = rdb.SRem(ctx, "HdSet:"+tariffIdStr, strconv.Itoa(holidayId))
|
||||
}
|
||||
}
|
||||
/*var cursor uint64
|
||||
for {
|
||||
var keys []string
|
||||
// 20 keys each scan
|
||||
keys, cursor, err = rdb.Scan(ctx, cursor, "Hd:*:"+strconv.Itoa(holidayId), 200).Result()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Scan Hd:*:%d, err: %v", holidayId, err)
|
||||
break
|
||||
}
|
||||
|
||||
//fmt.Printf("\nfound %d keys\n", len(keys))
|
||||
for _, key := range keys {
|
||||
err := rdb.Del(ctx, key).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Del %s, err: %v", key, err)
|
||||
} else {
|
||||
subFields := strings.Split(key, ":")
|
||||
if len(subFields) == 3 && subFields[1] != "" {
|
||||
_ = rdb.SRem(ctx, "HdSet:"+subFields[1], strconv.Itoa(holidayId))
|
||||
//return srem.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}*/
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// "Tariff:"+strconv.Itoa(tariff.OfrId)+":"+tariff.MeasureDomain+":"+strconv.Itoa(tariff.TariffId)
|
||||
func RdbDelTariffRecord(tariffId int) error {
|
||||
var cursor uint64
|
||||
var err error
|
||||
for {
|
||||
var keys []string
|
||||
// 20 keys each scan
|
||||
keys, cursor, err = rdb.Scan(ctx, cursor, "Tariff:*:"+strconv.Itoa(tariffId), 2).Result()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Scan Tariff:*:%d, err: %v", tariffId, err)
|
||||
break
|
||||
}
|
||||
|
||||
//fmt.Printf("\nfound %d keys\n", len(keys))
|
||||
for _, key := range keys {
|
||||
err := rdb.Del(ctx, key).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Del %s, err: %v", key, err)
|
||||
} else {
|
||||
subFields := strings.Split(key, ":")
|
||||
if len(subFields) == 4 {
|
||||
_ = rdb.SRem(ctx, "TariffSet:"+subFields[1]+":"+subFields[2], strconv.Itoa(tariffId))
|
||||
//return srem.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// "Tariff:"+strconv.Itoa(tariff.OfrId)+":"+tariff.MeasureDomain+":"+strconv.Itoa(tariff.TariffId)
|
||||
func RdbDelTariffRecordByOfr(ofrId int) error {
|
||||
var cursor uint64
|
||||
var err error
|
||||
for {
|
||||
var keys []string
|
||||
// 20 keys each scan
|
||||
keys, cursor, err = rdb.Scan(ctx, cursor, "Tariff:"+strconv.Itoa(ofrId)+":*", 2).Result()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Scan Tariff:%d:*, err: %v", ofrId, err)
|
||||
break
|
||||
}
|
||||
|
||||
//fmt.Printf("\nfound %d keys\n", len(keys))
|
||||
for _, key := range keys {
|
||||
err := rdb.Del(ctx, key).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Del %s, err: %v", key, err)
|
||||
} else {
|
||||
subFields := strings.Split(key, ":")
|
||||
if len(subFields) == 4 {
|
||||
_ = rdb.SRem(ctx, "TariffSet:"+subFields[1]+":"+subFields[2], subFields[3])
|
||||
//return srem.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func RdbDelPrefixRecord(strategyId int, areaCode string) error {
|
||||
srem := rdb.SRem(ctx, "Prefix:"+strconv.Itoa(strategyId), areaCode)
|
||||
return srem.Err()
|
||||
}
|
||||
|
||||
func RdbSetTariffRecord(tariff *TariffData) error {
|
||||
//&prd_inst_id, &service_nbr, &prd_inst_stas_id, &acct_id, &cust_id
|
||||
err := rdb.HMSet(ctx, "Tariff:"+strconv.Itoa(tariff.OfrId)+":"+tariff.MeasureDomain+":"+strconv.Itoa(tariff.TariffId), "tariffId", tariff.TariffId,
|
||||
"tariffSeq", tariff.TariffSeq, "measureDomain", tariff.MeasureDomain,
|
||||
"startTime", tariff.StartTime, "endTime", tariff.EndTime, "feeUnit", tariff.FeeUnit,
|
||||
"rateUnit", tariff.RateUnit, "calcPriority", tariff.CalcPriority, "eventPriority", tariff.EventPriority, "ofrId", tariff.OfrId,
|
||||
"ofrName", tariff.OfrName, "strategyId", tariff.StrategyId, "strategyName", tariff.StrategyName).Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("HMSet Tariff:%d, err: %v", tariff.TariffId, err)
|
||||
} else {
|
||||
sadd := rdb.SAdd(ctx, "TariffSet:"+strconv.Itoa(tariff.OfrId)+":"+tariff.MeasureDomain, strconv.Itoa(tariff.TariffId))
|
||||
if sadd.Err() != nil {
|
||||
l4g.RedisLog.Errorf("SAdd TariffSet:%d:%s %d, err: %v", tariff.OfrId, tariff.MeasureDomain, tariff.TariffId, sadd.Err())
|
||||
return sadd.Err()
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func RdbGetRrByServiceNbrAndDomain(serviceNbrAndDomain string) (rsp string, err error) {
|
||||
var cursor uint64= 0
|
||||
for {
|
||||
keys, cursorNew, err := rdb.SScan(ctx, "RrSet:"+serviceNbrAndDomain, cursor, "", 0).Result()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("SScan RrSet:%s, err: %v", serviceNbrAndDomain, err)
|
||||
break
|
||||
}
|
||||
|
||||
//fmt.Printf("\nfound %d keys\n", len(keys))
|
||||
for _, key := range keys {
|
||||
rrKey := "Rr:"+serviceNbrAndDomain+":"+key
|
||||
res := rdb.HGetAll(ctx, rrKey)
|
||||
if res.Err() != nil {
|
||||
l4g.RedisLog.Errorf("HGetAll %s, err: %v", rrKey, err)
|
||||
return "err: HGetAll "+rrKey, err
|
||||
} else {
|
||||
var rrData RrData
|
||||
if err = res.Scan(&rrData); err != nil {
|
||||
l4g.RedisLog.Errorf("HGetAll Scan %s, err: %v", rrKey, err)
|
||||
return "err: Scan "+rrKey, err
|
||||
} else {
|
||||
l4g.RedisLog.Debugf("Get %s, [%v]", rrKey, rrData)
|
||||
rsp += fmt.Sprintf("%s:***************************************\r\nPrdInstId: %d\r\nOfrInstId: %d\r\nOfrId: %d\r\nOfrName: %s\r\nBeginTime: %s\r\nEndTime: %s\r\n",
|
||||
rrKey, rrData.PrdInstId, rrData.OfrInstId, rrData.OfrId, rrData.OfrName, rrData.BeginTime, rrData.EndTime)
|
||||
rsp += fmt.Sprintf("FreeValue: %d\r\nUsedValue: %d\r\nTariffId: %d\r\nStrategyId: %d\r\nCalcPriority: %d\r\nEventPriority: %d\r\nRateUnit: %d\r\nUnitFee: %d\r\n",
|
||||
rrData.FreeValue, rrData.UsedValue, rrData.TariffId, rrData.StrategyId, rrData.CalcPriority, rrData.EventPriority, rrData.RateUnit, rrData.UnitFee)
|
||||
}
|
||||
}
|
||||
}
|
||||
if cursorNew == 0 {
|
||||
break
|
||||
} else {
|
||||
cursor = cursorNew
|
||||
}
|
||||
}
|
||||
|
||||
return rsp, err
|
||||
}
|
||||
|
||||
func RdbGetTariffByOfrId(ofrId string) (*TariffData, error) {
|
||||
keys := rdb.Keys(ctx, "Tariff:"+ofrId+":*")
|
||||
err := keys.Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("Get Keys of Tariff:%s:*, err: %v", ofrId, err)
|
||||
return nil, err
|
||||
} else {
|
||||
l4g.RedisLog.Debugf("Get Keys of Tariff:%s:*, keys:%v", ofrId, keys.Val())
|
||||
}
|
||||
|
||||
// Scan the results into the struct.
|
||||
var ta TariffData
|
||||
for _, value := range keys.Val() {
|
||||
// Get the map. The same approach works for HmGet().
|
||||
res := rdb.HGetAll(ctx, value)
|
||||
err := res.Err()
|
||||
if err != nil {
|
||||
l4g.RedisLog.Errorf("HGetAll %s, err: %v", value, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = res.Scan(&ta); err != nil {
|
||||
l4g.RedisLog.Errorf("HGetAll Scan %s, err: %v", value, err)
|
||||
} else {
|
||||
l4g.RedisLog.Debugf("Get %s, [%v]", value, ta)
|
||||
|
||||
// Get prefix related tariff
|
||||
sMembers := rdb.SMembers(ctx, "Prefix:"+strconv.Itoa(ta.StrategyId))
|
||||
if sMembers.Err() == nil {
|
||||
l4g.RedisLog.Debugf("Prefix, [%v]", sMembers.Val())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &ta, err
|
||||
}
|
||||
51
proxy_go/Nrestful/alert_sms.go
Normal file
51
proxy_go/Nrestful/alert_sms.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package Nrestful
|
||||
|
||||
import (
|
||||
mysql "proxy/Nmysql"
|
||||
rdb "proxy/Nredis"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func RdbScanAlertSmsRecord() {
|
||||
keys := rdb.RdbScanAlertSmsRecord()
|
||||
|
||||
for _, key := range keys {
|
||||
if strings.HasPrefix(key, "AlertSms:") {
|
||||
smsContent := rdb.RdbGetAlertSmsContent(key)
|
||||
if smsContent != "" {
|
||||
ss := strings.Split(key, ":")
|
||||
if len(ss) >= 3 {
|
||||
SendNtfSms2Ocs(ss[1], smsContent)
|
||||
|
||||
alertId, _ := strconv.Atoi(ss[2])
|
||||
_ = mysql.SetAlertSmsState2Sent(alertId)
|
||||
}
|
||||
}
|
||||
rdb.RdsDelMsg2OcsKey(key)
|
||||
} else if strings.HasPrefix(key, "CreateAcct:") {
|
||||
ai := rdb.RdbGetCreateAcct(key)
|
||||
if ai != nil {
|
||||
ss := strings.Split(key, ":")
|
||||
if len(ss) >= 3 {
|
||||
cugId, err := mysql.QueryOfrIdByPrdInstId(ai.PrdInstId)
|
||||
userClass := 1
|
||||
ofrLevel, err := mysql.QueryOfrLevelByOfrId(ai.OfrId)
|
||||
if err == nil {
|
||||
userClass = ofrLevel
|
||||
}
|
||||
rent, _ := mysql.QueryRentByOfrId(ai.OfrId)
|
||||
//SendCrtAcct2Ocs(ss[1], ai, cugId, userClass, rent)
|
||||
CreateAcct2Ocs(ss[1], ai, cugId, userClass, rent, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//for _, key := range keys {
|
||||
// rdb.RdsDelMsg2OcsKey(key)
|
||||
//}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
575
proxy_go/Nrestful/decode_udp.go
Normal file
575
proxy_go/Nrestful/decode_udp.go
Normal file
@@ -0,0 +1,575 @@
|
||||
package Nrestful
|
||||
|
||||
import (
|
||||
"time"
|
||||
"encoding/binary"
|
||||
_ "strings"
|
||||
"strconv"
|
||||
|
||||
l4g "proxy/logger"//"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func decode_authcode_rsp(buf []byte, msglen int, rsp *RestRsp) int {
|
||||
var offset int = 0
|
||||
var tag byte = 0
|
||||
var vallen byte = 0
|
||||
var result byte = 1
|
||||
|
||||
for {
|
||||
if offset >= msglen {
|
||||
break
|
||||
}
|
||||
tag = buf[offset];
|
||||
offset++
|
||||
vallen = buf[offset];
|
||||
offset++
|
||||
|
||||
switch tag {
|
||||
case IE_SRC_REF:
|
||||
rsp.Src_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_DST_REF:
|
||||
rsp.Dst_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_RESULT:
|
||||
result = buf[offset]
|
||||
if result == 0 {
|
||||
rsp.AuthCodeRsp.Code = strconv.Itoa(int(ERRCODE_SUCCESS))
|
||||
rsp.AuthCodeRsp.Message = Rest_proxy_errcodemsg(ERRCODE_SUCCESS)
|
||||
}
|
||||
case IE_ERROR_CODE:
|
||||
err_code := binary.BigEndian.Uint32(buf[offset:offset+4])
|
||||
rsp.AuthCodeRsp.Code = strconv.Itoa(int(err_code))
|
||||
rsp.AuthCodeRsp.Message = Rest_proxy_errcodemsg(err_code)
|
||||
default:
|
||||
}
|
||||
offset += int(vallen)
|
||||
}
|
||||
|
||||
l4g.RestLog.Debugf("decode_authcode_rsp: %#v", rsp.AuthCodeRsp)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
func decode_query_userData_rsp(buf []byte, msglen int, rsp *RestRsp) int {
|
||||
var offset int = 0
|
||||
var tag byte = 0
|
||||
var vallen byte = 0
|
||||
var result byte = 1
|
||||
|
||||
for {
|
||||
if offset >= msglen {
|
||||
break
|
||||
}
|
||||
tag = buf[offset];
|
||||
offset++
|
||||
vallen = buf[offset];
|
||||
offset++
|
||||
|
||||
switch tag {
|
||||
case IE_SRC_REF:
|
||||
rsp.Src_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_DST_REF:
|
||||
rsp.Dst_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_RESULT:
|
||||
result = buf[offset]
|
||||
if result == 0 {
|
||||
rsp.QueryUserDataRsp.Code = strconv.Itoa(int(ERRCODE_SUCCESS))
|
||||
rsp.QueryUserDataRsp.Message = Rest_proxy_errcodemsg(ERRCODE_SUCCESS)
|
||||
}
|
||||
case IE_ERROR_CODE:
|
||||
err_code := binary.BigEndian.Uint32(buf[offset:offset+4])
|
||||
rsp.QueryUserDataRsp.Code = strconv.Itoa(int(err_code))
|
||||
rsp.QueryUserDataRsp.Message = Rest_proxy_errcodemsg(err_code)
|
||||
case IE_MSISDN:
|
||||
rsp.QueryUserDataRsp.TelNumber = string(buf[offset:offset+int(vallen)])
|
||||
case IE_REMARK:
|
||||
rsp.QueryUserDataRsp.Remark = string(buf[offset:offset+int(vallen)])
|
||||
case IE_GROUP_NAME:
|
||||
rsp.QueryUserDataRsp.UserGroupName = string(buf[offset:offset+int(vallen)])
|
||||
case IE_STATUS:
|
||||
rsp.QueryUserDataRsp.Status = strconv.Itoa(int(buf[offset]))
|
||||
case IE_BALANCE:
|
||||
rsp.QueryUserDataRsp.Balance = strconv.Itoa(int(binary.BigEndian.Uint32(buf[offset:offset+4])))
|
||||
case IE_EXPIRY_TIME:
|
||||
tm := time.Unix(int64(binary.BigEndian.Uint64(buf[offset:offset+8])), 0)
|
||||
rsp.QueryUserDataRsp.ExpireDate = tm.Format("2006-01-02 15:04:05")
|
||||
case IE_REMAIN_MO_VOICE_MIN:
|
||||
rsp.QueryUserDataRsp.RemainMoVoiceMinute = strconv.Itoa(int(binary.BigEndian.Uint32(buf[offset:offset+4])))
|
||||
case IE_REMAIN_MT_VOICE_MIN:
|
||||
rsp.QueryUserDataRsp.RemainMtVoiceMinute = strconv.Itoa(int(binary.BigEndian.Uint32(buf[offset:offset+4])))
|
||||
case IE_REMAIN_SMS_NUM:
|
||||
rsp.QueryUserDataRsp.RemainSmsVolume = strconv.Itoa(int(binary.BigEndian.Uint32(buf[offset:offset+4])))
|
||||
case IE_REMAIN_DATA_VOL_MB:
|
||||
rsp.QueryUserDataRsp.RemainDataVolume = strconv.Itoa(int(binary.BigEndian.Uint32(buf[offset:offset+4])))
|
||||
default:
|
||||
}
|
||||
offset += int(vallen)
|
||||
}
|
||||
|
||||
l4g.RestLog.Debugf("decode_query_userData_rsp: %#v", rsp.QueryUserDataRsp)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
func decode_bundleSubs_rsp(buf []byte, msglen int, rsp *RestRsp) int {
|
||||
var offset int = 0
|
||||
var tag byte = 0
|
||||
var vallen byte = 0
|
||||
var result byte = 1
|
||||
|
||||
for {
|
||||
if offset >= msglen {
|
||||
break
|
||||
}
|
||||
tag = buf[offset];
|
||||
offset++
|
||||
vallen = buf[offset];
|
||||
offset++
|
||||
|
||||
switch tag {
|
||||
case IE_SRC_REF:
|
||||
rsp.Src_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_DST_REF:
|
||||
rsp.Dst_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_RESULT:
|
||||
result = buf[offset]
|
||||
if result == 0 {
|
||||
rsp.BundleSubsRsp.Code = strconv.Itoa(int(ERRCODE_SUCCESS))
|
||||
rsp.BundleSubsRsp.Message = Rest_proxy_errcodemsg(ERRCODE_SUCCESS)
|
||||
}
|
||||
case IE_ERROR_CODE:
|
||||
err_code := binary.BigEndian.Uint32(buf[offset:offset+4])
|
||||
rsp.BundleSubsRsp.Code = strconv.Itoa(int(err_code))
|
||||
rsp.BundleSubsRsp.Message = Rest_proxy_errcodemsg(err_code)
|
||||
default:
|
||||
}
|
||||
offset += int(vallen)
|
||||
}
|
||||
|
||||
l4g.RestLog.Debugf("decode_bundleSubs_rsp: %#v", rsp.BundleSubsRsp)
|
||||
return 0
|
||||
}
|
||||
|
||||
func decode_query_balance_rsp(buf []byte, msglen int, rsp *RestRsp) int {
|
||||
var offset int = 0
|
||||
var tag byte = 0
|
||||
var vallen byte = 0
|
||||
var result byte = 1
|
||||
|
||||
for {
|
||||
if offset >= msglen {
|
||||
break
|
||||
}
|
||||
tag = buf[offset];
|
||||
offset++
|
||||
vallen = buf[offset];
|
||||
offset++
|
||||
|
||||
switch tag {
|
||||
case IE_SRC_REF:
|
||||
rsp.Src_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_DST_REF:
|
||||
rsp.Dst_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_RESULT:
|
||||
result = buf[offset]
|
||||
if result == 0 {
|
||||
rsp.QueryBalanceRsp.Code = strconv.Itoa(int(ERRCODE_SUCCESS))
|
||||
rsp.QueryBalanceRsp.Message = Rest_proxy_errcodemsg(ERRCODE_SUCCESS)
|
||||
}
|
||||
case IE_ERROR_CODE:
|
||||
err_code := binary.BigEndian.Uint32(buf[offset:offset+4])
|
||||
rsp.QueryBalanceRsp.Code = strconv.Itoa(int(err_code))
|
||||
rsp.QueryBalanceRsp.Message = Rest_proxy_errcodemsg(err_code)
|
||||
case IE_STATUS:
|
||||
rsp.QueryBalanceRsp.Status = strconv.Itoa(int(buf[offset]))
|
||||
case IE_BALANCE:
|
||||
rsp.QueryBalanceRsp.Balance = strconv.Itoa(int(binary.BigEndian.Uint32(buf[offset:offset+4])))
|
||||
case IE_MO_EXPIRY:
|
||||
tm := time.Unix(int64(binary.BigEndian.Uint32(buf[offset:offset+4])), 0)
|
||||
rsp.QueryBalanceRsp.ExpireDate = tm.Format("2006-01-02 15:04:05")
|
||||
default:
|
||||
}
|
||||
offset += int(vallen)
|
||||
}
|
||||
|
||||
l4g.RestLog.Debugf("decode_query_balance_rsp: %#v", rsp.QueryBalanceRsp)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func decode_recharge_rsp(buf []byte, msglen int, rsp *RestRsp) int {
|
||||
var offset int = 0
|
||||
var tag byte = 0
|
||||
var vallen byte = 0
|
||||
var result byte = 1
|
||||
|
||||
for {
|
||||
if offset >= msglen {
|
||||
break
|
||||
}
|
||||
tag = buf[offset];
|
||||
offset++
|
||||
vallen = buf[offset];
|
||||
offset++
|
||||
|
||||
switch tag {
|
||||
case IE_SRC_REF:
|
||||
rsp.Src_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_DST_REF:
|
||||
rsp.Dst_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_RESULT:
|
||||
result = buf[offset]
|
||||
if result == 0 {
|
||||
rsp.RechargeRsp.Code = strconv.Itoa(int(ERRCODE_SUCCESS))
|
||||
rsp.RechargeRsp.Message = Rest_proxy_errcodemsg(ERRCODE_SUCCESS)
|
||||
}
|
||||
case IE_ERROR_CODE:
|
||||
var err_code uint32
|
||||
if vallen == 1 {
|
||||
err_code = uint32(buf[offset])
|
||||
} else {
|
||||
err_code = binary.BigEndian.Uint32(buf[offset:offset+4])
|
||||
}
|
||||
|
||||
rsp.RechargeRsp.Code = strconv.Itoa(int(err_code))
|
||||
rsp.RechargeRsp.Message = Rest_proxy_errcodemsg(err_code)
|
||||
case IE_STATUS:
|
||||
rsp.RechargeRsp.Status = strconv.Itoa(int(buf[offset]))
|
||||
case IE_BALANCE:
|
||||
rsp.RechargeRsp.Balance = strconv.Itoa(int(binary.BigEndian.Uint32(buf[offset:offset+4])))
|
||||
case IE_MO_EXPIRY:
|
||||
tm := time.Unix(int64(binary.BigEndian.Uint32(buf[offset:offset+4])), 0)
|
||||
rsp.RechargeRsp.ExpiredTimestamp = tm.Format("2006-01-02 15:04:05")
|
||||
default:
|
||||
}
|
||||
offset += int(vallen)
|
||||
}
|
||||
|
||||
l4g.RestLog.Debugf("decode_recharge_rsp: %#v", rsp.RechargeRsp)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func decode_transfer_rsp(buf []byte, msglen int, rsp *RestRsp) int {
|
||||
var offset int = 0
|
||||
var tag byte = 0
|
||||
var vallen byte = 0
|
||||
var result byte = 1
|
||||
|
||||
for {
|
||||
if offset >= msglen {
|
||||
break
|
||||
}
|
||||
tag = buf[offset];
|
||||
offset++
|
||||
vallen = buf[offset];
|
||||
offset++
|
||||
|
||||
switch tag {
|
||||
case IE_SRC_REF:
|
||||
rsp.Src_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_DST_REF:
|
||||
rsp.Dst_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_RESULT:
|
||||
result = buf[offset]
|
||||
if result == 0 {
|
||||
rsp.TransferRsp.Code = strconv.Itoa(int(ERRCODE_SUCCESS))
|
||||
rsp.TransferRsp.Message = Rest_proxy_errcodemsg(ERRCODE_SUCCESS)
|
||||
}
|
||||
case IE_ERROR_CODE:
|
||||
var err_code uint32
|
||||
if vallen == 1 {
|
||||
err_code = uint32(buf[offset])
|
||||
} else {
|
||||
err_code = binary.BigEndian.Uint32(buf[offset:offset+4])
|
||||
}
|
||||
|
||||
rsp.TransferRsp.Code = strconv.Itoa(int(err_code))
|
||||
rsp.TransferRsp.Message = Rest_proxy_errcodemsg(err_code)
|
||||
case IE_AMOUNT:
|
||||
rsp.TransferRsp.Balance = strconv.Itoa(int(binary.BigEndian.Uint32(buf[offset:offset+4])))
|
||||
default:
|
||||
}
|
||||
offset += int(vallen)
|
||||
}
|
||||
|
||||
l4g.RestLog.Debugf("decode_transfer_rsp: %#v", rsp.TransferRsp)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func decode_create_acct_rsp(buf []byte, msglen int, rsp *RestRsp) int {
|
||||
var offset int = 0
|
||||
var tag byte = 0
|
||||
var vallen byte = 0
|
||||
var result byte = 1
|
||||
|
||||
for {
|
||||
if offset >= msglen {
|
||||
break
|
||||
}
|
||||
tag = buf[offset];
|
||||
offset++
|
||||
vallen = buf[offset];
|
||||
offset++
|
||||
|
||||
switch tag {
|
||||
case IE_SRC_REF:
|
||||
rsp.Src_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_DST_REF:
|
||||
rsp.Dst_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_RESULT:
|
||||
result = buf[offset]
|
||||
if result == 0 {
|
||||
rsp.CreateAccountRsp.Code = strconv.Itoa(int(ERRCODE_SUCCESS))
|
||||
rsp.CreateAccountRsp.Message = Rest_proxy_errcodemsg(ERRCODE_SUCCESS)
|
||||
}
|
||||
case IE_ERROR_CODE:
|
||||
var err_code uint32
|
||||
if vallen == 1 {
|
||||
err_code = uint32(buf[offset])
|
||||
} else {
|
||||
err_code = binary.BigEndian.Uint32(buf[offset:offset+4])
|
||||
}
|
||||
|
||||
rsp.CreateAccountRsp.Code = strconv.Itoa(int(err_code))
|
||||
rsp.CreateAccountRsp.Message = Rest_proxy_errcodemsg(err_code)
|
||||
default:
|
||||
}
|
||||
offset += int(vallen)
|
||||
}
|
||||
|
||||
l4g.RestLog.Debugf("decode_create_acct_rsp: %#v", rsp.CreateAccountRsp)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func decode_update_subs_req(buf []byte, msglen int, rsp *RestRsp) int {
|
||||
var offset int = 0
|
||||
var tag byte = 0
|
||||
var vallen byte = 0
|
||||
|
||||
rsp.UpdateSubsReq.OptFlag = 0
|
||||
for {
|
||||
if offset >= msglen {
|
||||
break
|
||||
}
|
||||
tag = buf[offset]
|
||||
offset++
|
||||
vallen = buf[offset]
|
||||
offset++
|
||||
|
||||
switch tag {
|
||||
case IE_SRC_REF:
|
||||
rsp.Src_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_DST_REF:
|
||||
rsp.Dst_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_MSISDN:
|
||||
rsp.UpdateSubsReq.TelNumber = string(buf[offset:offset+int(vallen)])
|
||||
case IE_ACCOUNT_ID:
|
||||
rsp.UpdateSubsReq.AccountIdU32 = binary.BigEndian.Uint32(buf[offset:offset+4])
|
||||
case IE_STATUS:
|
||||
rsp.UpdateSubsReq.StatusU8 = buf[offset]
|
||||
rsp.UpdateSubsReq.OptFlag |= 0x01
|
||||
case IE_BALANCE:
|
||||
rsp.UpdateSubsReq.BalanceU32 = binary.BigEndian.Uint32(buf[offset:offset+4])
|
||||
rsp.UpdateSubsReq.OptFlag |= 0x02
|
||||
case IE_EXPIRY_TIME:
|
||||
rsp.UpdateSubsReq.ExpDateU32 = binary.BigEndian.Uint32(buf[offset:offset+4])
|
||||
rsp.UpdateSubsReq.OptFlag |= 0x04
|
||||
case IE_PLAN_ID:
|
||||
rsp.UpdateSubsReq.PackageIdU32 = binary.BigEndian.Uint32(buf[offset:offset+4])
|
||||
rsp.UpdateSubsReq.OptFlag |= 0x08
|
||||
case IE_RENT_CHARGE:
|
||||
rsp.UpdateSubsReq.RentChargeU32 = binary.BigEndian.Uint32(buf[offset:offset+4])
|
||||
rsp.UpdateSubsReq.OptFlag |= 0x10
|
||||
case IE_VAS_CUG_STATUS:
|
||||
rsp.UpdateSubsReq.VasCugStatusU8 = buf[offset]
|
||||
rsp.UpdateSubsReq.OptFlag |= 0x20
|
||||
default:
|
||||
}
|
||||
offset += int(vallen)
|
||||
}
|
||||
|
||||
l4g.RestLog.Debugf("decode_update_subs_req: %#v", rsp.UpdateSubsReq)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func decode_update_subs_rsp(buf []byte, msglen int, rsp *RestRsp) int {
|
||||
var offset int = 0
|
||||
var tag byte = 0
|
||||
var vallen byte = 0
|
||||
var result byte = 1
|
||||
|
||||
for {
|
||||
if offset >= msglen {
|
||||
break
|
||||
}
|
||||
tag = buf[offset];
|
||||
offset++
|
||||
vallen = buf[offset];
|
||||
offset++
|
||||
|
||||
switch tag {
|
||||
case IE_SRC_REF:
|
||||
rsp.Src_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_DST_REF:
|
||||
rsp.Dst_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_RESULT:
|
||||
result = buf[offset]
|
||||
if result == 0 {
|
||||
rsp.UpdateSubsRsp.Code = strconv.Itoa(int(ERRCODE_SUCCESS))
|
||||
rsp.UpdateSubsRsp.Message = Rest_proxy_errcodemsg(ERRCODE_SUCCESS)
|
||||
}
|
||||
case IE_ERROR_CODE:
|
||||
var err_code uint32
|
||||
if vallen == 1 {
|
||||
err_code = uint32(buf[offset])
|
||||
} else {
|
||||
err_code = binary.BigEndian.Uint32(buf[offset:offset+4])
|
||||
}
|
||||
|
||||
rsp.UpdateSubsRsp.Code = strconv.Itoa(int(err_code))
|
||||
rsp.UpdateSubsRsp.Message = Rest_proxy_errcodemsg(err_code)
|
||||
default:
|
||||
}
|
||||
offset += int(vallen)
|
||||
}
|
||||
|
||||
l4g.RestLog.Debugf("decode_update_subs_rsp: %#v", rsp.UpdateSubsRsp)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func decode_delete_subs_rsp(buf []byte, msglen int, rsp *RestRsp) int {
|
||||
var offset int = 0
|
||||
var tag byte = 0
|
||||
var vallen byte = 0
|
||||
var result byte = 1
|
||||
|
||||
for {
|
||||
if offset >= msglen {
|
||||
break
|
||||
}
|
||||
tag = buf[offset]
|
||||
offset++
|
||||
vallen = buf[offset]
|
||||
offset++
|
||||
|
||||
switch tag {
|
||||
case IE_SRC_REF:
|
||||
rsp.Src_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_DST_REF:
|
||||
rsp.Dst_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_RESULT:
|
||||
result = buf[offset]
|
||||
rsp.DeleteSubsRsp.Result = result
|
||||
if result == 0 {
|
||||
rsp.DeleteSubsRsp.Code = strconv.Itoa(int(ERRCODE_SUCCESS))
|
||||
rsp.DeleteSubsRsp.Message = Rest_proxy_errcodemsg(ERRCODE_SUCCESS)
|
||||
}
|
||||
case IE_ERROR_CODE:
|
||||
var err_code uint32
|
||||
if vallen == 1 {
|
||||
err_code = uint32(buf[offset])
|
||||
} else {
|
||||
err_code = binary.BigEndian.Uint32(buf[offset:offset+4])
|
||||
}
|
||||
|
||||
rsp.DeleteSubsRsp.ErrorCode = err_code
|
||||
rsp.DeleteSubsRsp.Code = strconv.Itoa(int(err_code))
|
||||
rsp.DeleteSubsRsp.Message = Rest_proxy_errcodemsg(err_code)
|
||||
default:
|
||||
}
|
||||
offset += int(vallen)
|
||||
}
|
||||
|
||||
l4g.RestLog.Debugf("decode_update_subs_rsp: %#v", rsp.UpdateSubsRsp)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func decodeUpdateBundleUsageReq(buf []byte, msglen int, rsp *RestRsp) int {
|
||||
var offset int = 0
|
||||
var tag byte = 0
|
||||
var vallen byte = 0
|
||||
|
||||
for {
|
||||
if offset >= msglen {
|
||||
break
|
||||
}
|
||||
tag = buf[offset]
|
||||
offset++
|
||||
vallen = buf[offset]
|
||||
offset++
|
||||
|
||||
switch tag {
|
||||
case IE_SRC_REF:
|
||||
rsp.Src_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_DST_REF:
|
||||
rsp.Dst_id = binary.BigEndian.Uint16(buf[offset:offset+2])
|
||||
case IE_MSISDN:
|
||||
rsp.UpdateBundleUsageReq.Msisdn = string(buf[offset:offset+int(vallen)])
|
||||
case IE_SERVICE_TYPE:
|
||||
rsp.UpdateBundleUsageReq.ServiceType = buf[offset]
|
||||
case IE_ACCOUNT_ID:
|
||||
rsp.UpdateBundleUsageReq.AccountId = binary.BigEndian.Uint32(buf[offset:offset+4])
|
||||
case IE_PLAN_ID:
|
||||
rsp.UpdateBundleUsageReq.PlanId = binary.BigEndian.Uint32(buf[offset:offset+4])
|
||||
case IE_BUNDLE_ID:
|
||||
rsp.UpdateBundleUsageReq.BundleId = binary.BigEndian.Uint32(buf[offset:offset+4])
|
||||
case IE_PLAN_VALUE:
|
||||
rsp.UpdateBundleUsageReq.PlanValue = binary.BigEndian.Uint64(buf[offset:offset+8])
|
||||
case IE_PLAN_USED_VALUE:
|
||||
rsp.UpdateBundleUsageReq.PlanUsedValue = binary.BigEndian.Uint64(buf[offset:offset+8])
|
||||
case IE_SESS_UPDATE_TIME:
|
||||
rsp.UpdateBundleUsageReq.SessUpdateTime = binary.BigEndian.Uint32(buf[offset:offset+4])
|
||||
case IE_PLAN_VALUE_ADD_THIS_TIME:
|
||||
rsp.UpdateBundleUsageReq.PlanValueAddThisTime = binary.BigEndian.Uint64(buf[offset:offset+8])
|
||||
default:
|
||||
}
|
||||
offset += int(vallen)
|
||||
}
|
||||
|
||||
l4g.RestLog.Debugf("decodeUpdateBundleUsageReq: %#v", rsp.UpdateSubsRsp)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func Decode_udp_msg(buf []byte, msglen int, rsp *RestRsp) int {
|
||||
var offset int = 0
|
||||
|
||||
if msglen <= 9 {
|
||||
return -1
|
||||
}
|
||||
|
||||
rsp.MsgType = buf[offset]
|
||||
offset++
|
||||
switch (rsp.MsgType) {
|
||||
case REST_SEND_AUTHCODE_RSP:
|
||||
return decode_authcode_rsp(buf[1:], msglen-1, rsp)
|
||||
case REST_QUERY_USERDATA_RSP:
|
||||
return decode_query_userData_rsp(buf[1:], msglen-1, rsp)
|
||||
case REST_BUNDLE_SUBS_RSP:
|
||||
return decode_bundleSubs_rsp(buf[1:], msglen-1, rsp)
|
||||
case REST_RECHARGE_RSP:
|
||||
return decode_recharge_rsp(buf[1:], msglen-1, rsp)
|
||||
case REST_TRANSFER_RSP:
|
||||
return decode_transfer_rsp(buf[1:], msglen-1, rsp)
|
||||
case REST_QUERY_BALANCE_RSP:
|
||||
return decode_query_balance_rsp(buf[1:], msglen-1, rsp)
|
||||
case REST_CRM_CREATE_ACCT_RES:
|
||||
return decode_create_acct_rsp(buf[1:], msglen-1, rsp)
|
||||
case REST_CRM_UPDATE_SUBS_REQ:
|
||||
return decode_update_subs_req(buf[1:], msglen-1, rsp)
|
||||
case REST_CRM_UPDATE_SUBS_RES:
|
||||
return decode_update_subs_rsp(buf[1:], msglen-1, rsp)
|
||||
case REST_CRM_DELETE_SUBS_RES:
|
||||
return decode_delete_subs_rsp(buf[1:], msglen-1, rsp)
|
||||
case REST_CRM_UPDATE_PLAN_INFO_REQ:
|
||||
return decodeUpdateBundleUsageReq(buf[1:], msglen-1, rsp)
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
44
proxy_go/Nrestful/error_code.go
Normal file
44
proxy_go/Nrestful/error_code.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package Nrestful
|
||||
|
||||
import (
|
||||
|
||||
)
|
||||
|
||||
const ERRCODE_SUCCESS uint32 = 2001
|
||||
const ERRCODE_COM_UNSUPPORT uint32 = 3001
|
||||
const ERRCODE_UNKNOWN_PEER uint32 = 3011
|
||||
const ERRCODE_INVALID_USER_STATUS uint32 = 4001
|
||||
const ERRCODE_DEST_USER_NOT_ALLOWED uint32 = 4008
|
||||
const ERRCODE_CREDIT_LIMIT uint32 = 4012
|
||||
const ERRCODE_INVALID_PARAM_VALUE uint32 = 5004
|
||||
const ERRCODE_MISSING_PARAM uint32 = 5005
|
||||
const ERRCODE_INVALID_RECHARGE_PASSWORD uint32 = 5006
|
||||
const ERRCODE_UNABLE_TO_COMPLY uint32 = 5012
|
||||
const ERRCODE_USER_UNKNOWN uint32 = 5030
|
||||
const MAX_ERRCODE uint32 = 5000
|
||||
|
||||
var restCode2StrMap = make(map[uint32]string)
|
||||
|
||||
func init() {
|
||||
restCode2StrMap[ERRCODE_SUCCESS] = "success"
|
||||
restCode2StrMap[ERRCODE_COM_UNSUPPORT] = "command unsupported"
|
||||
restCode2StrMap[ERRCODE_UNKNOWN_PEER] = "unknown peer"
|
||||
restCode2StrMap[ERRCODE_INVALID_USER_STATUS] = "invalid user status"
|
||||
restCode2StrMap[ERRCODE_DEST_USER_NOT_ALLOWED] = "dest user not allowed"
|
||||
restCode2StrMap[ERRCODE_CREDIT_LIMIT] = "credit limit"
|
||||
restCode2StrMap[ERRCODE_INVALID_PARAM_VALUE] = "invalid parameter value"
|
||||
restCode2StrMap[ERRCODE_MISSING_PARAM] = "missing parameter"
|
||||
restCode2StrMap[ERRCODE_INVALID_RECHARGE_PASSWORD] = "invalid recharge password"
|
||||
restCode2StrMap[ERRCODE_UNABLE_TO_COMPLY] = "unable to comply"
|
||||
restCode2StrMap[ERRCODE_USER_UNKNOWN] = "user unknown"
|
||||
}
|
||||
|
||||
func Rest_proxy_errcodemsg(errcode uint32) string {
|
||||
errStr, ok := restCode2StrMap[errcode]
|
||||
if !ok {
|
||||
return "unkown"
|
||||
} else {
|
||||
return errStr
|
||||
}
|
||||
}
|
||||
|
||||
104
proxy_go/Nrestful/handle_ocs_msg.go
Normal file
104
proxy_go/Nrestful/handle_ocs_msg.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package Nrestful
|
||||
|
||||
import (
|
||||
mysql "proxy/Nmysql"
|
||||
rds "proxy/Nredis"
|
||||
"proxy/config"
|
||||
)
|
||||
|
||||
func handleOcsDeleteSubsRsp(req *RestRsp) {
|
||||
}
|
||||
|
||||
func handleOcsUpdateSubsReq(req *RestRsp) {
|
||||
expNtfFlag := false
|
||||
var expiredDate int64= 0
|
||||
if req.UpdateSubsReq.OptFlag & 0x04 == 0x04 {
|
||||
expNtfFlag = true
|
||||
expiredDate = int64(req.UpdateSubsReq.ExpDateU32)
|
||||
}
|
||||
|
||||
accountId := req.UpdateSubsReq.AccountIdU32
|
||||
if expNtfFlag {
|
||||
planId := req.UpdateSubsReq.PackageIdU32
|
||||
_ = mysql.InsertExpiredNotifySms(accountId, planId, expiredDate)
|
||||
} else {
|
||||
status := req.UpdateSubsReq.StatusU8
|
||||
_ = mysql.UpdateUserStatus2Mdb(accountId, status)
|
||||
}
|
||||
}
|
||||
|
||||
func handleUpdatePlanUsage(req *RestRsp) {
|
||||
opFlag := false
|
||||
thisTimeAdded := req.UpdateBundleUsageReq.PlanValueAddThisTime
|
||||
if thisTimeAdded & 0x8000000000000000 == 0x8000000000000000 {
|
||||
opFlag = true
|
||||
}
|
||||
thisTimeAdded &= 0x7FFFFFFFFFFFFFFF
|
||||
|
||||
totalValue := req.UpdateBundleUsageReq.PlanValue
|
||||
if totalValue <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
usedValue := req.UpdateBundleUsageReq.PlanUsedValue
|
||||
serviceType := req.UpdateBundleUsageReq.ServiceType
|
||||
rateUsed, oldRateUsed := 0, 0
|
||||
sendNotifyFlag := false
|
||||
if bSendNotifySms(serviceType) {//(service_type == 2)//data only; 1: voice; 2: data; 3: sms;
|
||||
rateUsed = int(usedValue*100 / totalValue)
|
||||
if rateUsed > 75 {
|
||||
oldRateUsed = int((usedValue-thisTimeAdded)*100 / totalValue)
|
||||
if oldRateUsed <= 75 {
|
||||
sendNotifyFlag = true
|
||||
rateUsed = 75
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateExpiryDate := false
|
||||
if usedValue >= totalValue {
|
||||
usedValue = totalValue
|
||||
rateUsed = 100
|
||||
updateExpiryDate = true
|
||||
if false {
|
||||
sendNotifyFlag = true
|
||||
}
|
||||
}
|
||||
|
||||
bundleId := req.UpdateBundleUsageReq.BundleId
|
||||
if serviceType == 2 {
|
||||
mysql.UpdateDataPlanInfo(bundleId, thisTimeAdded, opFlag)
|
||||
} else {
|
||||
mysql.UpdateVoiceAndSmsPlanInfo(serviceType, bundleId, usedValue, totalValue, updateExpiryDate)
|
||||
}
|
||||
|
||||
if sendNotifyFlag {
|
||||
planId := req.UpdateBundleUsageReq.PlanId
|
||||
if !rds.CheckIfBundleLimitSent(planId, rateUsed) {
|
||||
accountId := req.UpdateBundleUsageReq.AccountId
|
||||
mysql.AddNotificationSms(accountId, planId, serviceType, updateExpiryDate, rateUsed)
|
||||
rds.SetPlanExpire(planId, rateUsed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func bSendNotifySms(serviceType byte) bool {
|
||||
ntfCfg := &config.Config.BundleUsageNotify
|
||||
switch serviceType {
|
||||
case 1:
|
||||
if ntfCfg.Voice75Percent {
|
||||
return true
|
||||
}
|
||||
case 2:
|
||||
if ntfCfg.Data75Percent {
|
||||
return true
|
||||
}
|
||||
case 3:
|
||||
if ntfCfg.Sms75Percent {
|
||||
return true
|
||||
}
|
||||
default:
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
92
proxy_go/Nrestful/handle_port.go
Normal file
92
proxy_go/Nrestful/handle_port.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package Nrestful
|
||||
|
||||
import (
|
||||
l4g "proxy/logger" //"github.com/sirupsen/logrus"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var mutex sync.Mutex
|
||||
|
||||
type Port struct {
|
||||
|
||||
CurIndex int
|
||||
UseFlag int
|
||||
|
||||
Dst_id uint16
|
||||
Rsp chan RestRsp
|
||||
}
|
||||
|
||||
const MAX_PORT_NUM uint16 = 8192
|
||||
|
||||
type PortMngt struct {
|
||||
Seq uint16
|
||||
CurIndex uint16
|
||||
Ports [MAX_PORT_NUM]Port
|
||||
}
|
||||
|
||||
var portMngt PortMngt
|
||||
|
||||
func AssignPort(req *RestReq) *Port {
|
||||
var id uint16 = 0
|
||||
var i uint16 = 0
|
||||
var port *Port = nil
|
||||
|
||||
mutex.Lock()
|
||||
for i=0; i<MAX_PORT_NUM; i++ {
|
||||
id = (i+portMngt.CurIndex) % MAX_PORT_NUM
|
||||
if portMngt.Ports[id].UseFlag == 0 {
|
||||
portMngt.CurIndex = (id + 1) % MAX_PORT_NUM
|
||||
portMngt.Ports[id].UseFlag = 1
|
||||
req.Src_id = id
|
||||
req.Dst_id = portMngt.Seq
|
||||
portMngt.Ports[id].Dst_id = req.Dst_id
|
||||
portMngt.Seq ++
|
||||
|
||||
port = &portMngt.Ports[id]
|
||||
break
|
||||
}
|
||||
}
|
||||
mutex.Unlock()
|
||||
|
||||
return port
|
||||
}
|
||||
|
||||
func GetPortInfo(src_id uint16, dst_id uint16) *Port {
|
||||
if src_id < 0 || src_id >= MAX_PORT_NUM {
|
||||
return nil
|
||||
}
|
||||
|
||||
port := &portMngt.Ports[src_id]
|
||||
if port.UseFlag == 0 || port.Dst_id != dst_id {
|
||||
l4g.RestLog.Errorf("Get port error, src[%d], dst[%d], used_flag[%d]", src_id, dst_id, port.UseFlag)
|
||||
return nil
|
||||
}
|
||||
|
||||
return port
|
||||
}
|
||||
|
||||
/*func ClosePortChan(id int, ts int64) {
|
||||
if id < 0 || id >= MAX_PORT_NUM {
|
||||
return
|
||||
}
|
||||
|
||||
mutex.Lock()
|
||||
if portMngt.Ports[id].UseFlag && (ts == portMngt.Ports[id].Ts) {
|
||||
close(portMngt.Ports[id].Rsp)
|
||||
}
|
||||
mutex.Unlock()
|
||||
|
||||
return
|
||||
}*/
|
||||
|
||||
func ReleasePort(id uint16) int {
|
||||
if id < 0 || id >= MAX_PORT_NUM {
|
||||
return -1
|
||||
}
|
||||
|
||||
mutex.Lock()
|
||||
portMngt.Ports[id].UseFlag = 0
|
||||
mutex.Unlock()
|
||||
|
||||
return 0
|
||||
}
|
||||
353
proxy_go/Nrestful/rest_msg.go
Normal file
353
proxy_go/Nrestful/rest_msg.go
Normal file
@@ -0,0 +1,353 @@
|
||||
package Nrestful
|
||||
|
||||
import (
|
||||
|
||||
)
|
||||
|
||||
type AuthCodeReq struct {
|
||||
CODE_TYPE string `json:"CODE_TYPE"`// ignore?
|
||||
CUST_CODE uint32 `json:"CUST_CODE"`// ignore?
|
||||
Content string `json:"content"`
|
||||
TelNumber string `json:"telNumber"`
|
||||
}
|
||||
|
||||
type AuthCodeRsp struct {
|
||||
|
||||
Code string `json:"code"`// 2001: succ; else: fail, from uint32
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type QueryUserDataReq struct {
|
||||
|
||||
TelNumber string `json:"telNumber"`
|
||||
}
|
||||
|
||||
type QueryUserDataRsp struct {
|
||||
|
||||
Code string `json:"code"`// 2001: succ; else: fail, from uint32
|
||||
Message string `json:"message"`
|
||||
Status string `json:"status"`// from uint8
|
||||
Remark string `json:"remark"`
|
||||
TelNumber string `json:"telNumber"`
|
||||
UserGroupName string `json:"userGroupName"`
|
||||
Balance string `json:"balance"`// from uint32
|
||||
ExpireDate string `json:"expireDate"`// "YYYY-MM-DD HH:MM:SS"
|
||||
VoiceMinute string `json:"VoiceMinute,omitempty"`// from uint32
|
||||
RemainMoVoiceMinute string `json:"remainVoiceMinute"`// from uint32
|
||||
RemainMtVoiceMinute string `json:"remainMtVoiceMinute,omitempty"`// from uint32
|
||||
SmsVolume string `json:"smsVolume,omitempty"`// from uint32
|
||||
RemainSmsVolume string `json:"remainSmsVolume"`// from uint32
|
||||
DataVolume string `json:"dataVolume,omitempty"`// from uint32
|
||||
RemainDataVolume string `json:"remainDataVolume"`// from uint32
|
||||
}
|
||||
|
||||
type BundleSubsReq struct {
|
||||
|
||||
TelNumber string `json:"telNumber"`
|
||||
PayType string `json:"payType"`// from uint8
|
||||
ChargedAmount string `json:"chargedAmount"`// from uint32
|
||||
MoVoiceMinute string `json:"moVoiceMinute"`// from uint32
|
||||
MtVoiceMinute string `json:"mtVoiceMinute"`// from uint32
|
||||
DataVolume string `json:"dataVolume"`// from uint32, in MB
|
||||
SmsVolume string `json:"smsVolume"`// from uint32
|
||||
ValidPeriod string `json:"validPeriod"`// from uint32
|
||||
}
|
||||
|
||||
type BundleSubsRsp struct {
|
||||
|
||||
Code string `json:"code"`// 2001: succ; else: fail// from uint32
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type BundleUsageReq struct {
|
||||
|
||||
TelNumber string `json:"telNumber"`
|
||||
}
|
||||
|
||||
type BundleUsageRsp struct {
|
||||
|
||||
Code uint32 `json:"code"`// 2001: succ; else: fail
|
||||
Message string `json:"message"`
|
||||
|
||||
MoVoiceInSecond uint32 `json:"moVoiceInSecond"`
|
||||
MtVoiceInSecond uint32 `json:"mtVoiceInSecond"`
|
||||
DataVolumeInKB uint32 `json:"dataVolumeInKB"`
|
||||
SmsVolume uint32 `json:"smsVolume"`
|
||||
ExpiredTimestamp string `json:"expiredTimestamp"`// "YYYY-MM-DD HH:MM:SS"
|
||||
}
|
||||
|
||||
type RechargeReq struct {
|
||||
AccountId string `json:"accountId"`
|
||||
Amount string `json:"amount"`// to uint32
|
||||
OpType string `json:"opType"`// to uint8
|
||||
StaffId string `json:"staffId"`
|
||||
TelNumber string `json:"telNumber"`
|
||||
ValidyDays string `json:"validyDays"`// to uint16
|
||||
}
|
||||
|
||||
type RechargeRsp struct {
|
||||
|
||||
Code string `json:"code"`// 2001: succ; else: fail, from uint32
|
||||
Message string `json:"message"`
|
||||
Status string `json:"status"` // from uint8
|
||||
Balance string `json:"balance"`// from uint32
|
||||
ExpiredTimestamp string `json:"expiredTimestamp"`// "YYYY-MM-DD HH:MM:SS"
|
||||
}
|
||||
|
||||
type TransferReq struct {
|
||||
|
||||
TransferOut string `json:"transferOut"`
|
||||
TransferIn string `json:"transferIn"`
|
||||
Amount string `json:"amount"`// from uint32
|
||||
}
|
||||
|
||||
type TransferRsp struct {
|
||||
|
||||
Code string `json:"code"`// 2001: succ; else: fail, from uint32
|
||||
Message string `json:"message"`
|
||||
Balance string `json:"balance"`// from uint32
|
||||
}
|
||||
|
||||
type RechargeCardReq struct {
|
||||
|
||||
TelNumber string `json:"telNumber"`
|
||||
CardPwd string `json:"cardPwd"`
|
||||
}
|
||||
|
||||
type RechargeCardRsp struct {
|
||||
|
||||
Code uint32 `json:"code"`// 2001: succ; else: fail
|
||||
Message string `json:"message"`
|
||||
RechargeAmount uint32 `json:"rechargeAmount"`
|
||||
Balance uint32 `json:"balance"`
|
||||
ExpiredTimestamp string `json:"expiredTimestamp"`// "%02d-%02d-%02d %02d:%02d:%02d"
|
||||
}
|
||||
|
||||
type CheckBalanceReq struct {
|
||||
|
||||
TelNumber string `json:"telNumber"`
|
||||
Amount uint32 `json:"amount"`
|
||||
}
|
||||
|
||||
type CheckBalanceRsp struct {
|
||||
|
||||
Code uint32 `json:"code"`// 2001: succ; else: fail
|
||||
Message string `json:"message"`
|
||||
Available uint32 `json:"available"`
|
||||
}
|
||||
|
||||
type QueryBalanceReq struct {
|
||||
|
||||
TelNumber string `json:"telNumber"`
|
||||
}
|
||||
|
||||
type QueryBalanceRsp struct {
|
||||
|
||||
Code string `json:"code"`// 2001: succ; else: fail, from uint32
|
||||
Message string `json:"message"`
|
||||
Status string `json:"status"`// from uint8
|
||||
Balance string `json:"balance"`// from uint32
|
||||
ExpireDate string `json:"expireDate"`// "YYYY-MM-DD HH:MM:SS"
|
||||
}
|
||||
|
||||
type QueryRechargeCardReq struct {
|
||||
|
||||
TelNumber string `json:"telNumber"`
|
||||
}
|
||||
|
||||
type QueryRechargeCardRsp struct {
|
||||
|
||||
Code uint32 `json:"code"`// 2001: succ; else: fail
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type UpdateRechargeCardReq struct {
|
||||
|
||||
TelNumber string `json:"telNumber"`
|
||||
}
|
||||
|
||||
type UpdateRechargeCardRsp struct {
|
||||
|
||||
Code uint32 `json:"code"`// 2001: succ; else: fail
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type CreateAccountReq struct {
|
||||
|
||||
ServiceNbr string `json:"serviceNbr"`
|
||||
CustId string `json:"custId"`// from uint32
|
||||
AccountId string `json:"accountId"`// from uint32
|
||||
ProductInstId string `json:"productInstId"`// from uint32
|
||||
PackageId string `json:"packageId"`// from uint32
|
||||
Balance string `json:"balance"`// from uint32
|
||||
//CustIdUint32 uint32 `json:"custIdUint32,omitempty"`// from uint32
|
||||
//AcctIdUint32 uint32 `json:"acctIdUint32,omitempty"`// from uint32
|
||||
|
||||
BalanceExpDate string `json:"balanceExpDate"`
|
||||
Birthday string `json:"birthday"`
|
||||
RentCharge string `json:"rentCharge,omitempty"`// from uint32
|
||||
CugId string `json:"cugId,omitempty"`// from uint32
|
||||
UserClass string `json:"userClass,omitempty"`// from uint32
|
||||
}
|
||||
|
||||
type CreateAccountRsp struct {
|
||||
|
||||
Code string `json:"code"`// 2001: succ; else: fail, from uint32
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type UpdateSubsReq struct {
|
||||
|
||||
TelNumber string `json:"telNumber"`
|
||||
AccountId string `json:"accountId,omitempty"`// from uint32
|
||||
Status string `json:"status,omitempty"`// from uint8
|
||||
Balance string `json:"balance,omitempty"`// from uint32
|
||||
BalanceExpDate string `json:"balanceExpDate,omitempty"`
|
||||
|
||||
AccountIdU32 uint32 `json:"accountIdU32,omitempty"`
|
||||
StatusU8 byte `json:"statusU8,omitempty"`
|
||||
BalanceU32 uint32 `json:"balanceU32,omitempty"`
|
||||
ExpDateU32 uint32 `json:"balanceExpDateU32,omitempty"`
|
||||
OptFlag uint32 `json:"optFlagU32,omitempty"`
|
||||
PackageIdU32 uint32 `json:"packageIdU32,omitempty"`// from uint32
|
||||
RentChargeU32 uint32 `json:"rentChargeU32,omitempty"`// from uint32
|
||||
VasCugStatusU8 byte `json:"vasCugStatusU8,omitempty"`// from uint8
|
||||
|
||||
PackageId string `json:"packageId,omitempty"`// from uint32
|
||||
RentCharge string `json:"rentCharge,omitempty"`// from uint32
|
||||
VasCugStatus string `json:"vasCugStatus,omitempty"`// from uint8
|
||||
//Optional_flag uint32 `json:"optional_flag,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateSubsRsp struct {
|
||||
|
||||
Code string `json:"code"`// 2001: succ; else: fail, from uint32
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type DeleteSubsReq struct {
|
||||
|
||||
TelNumber string `json:"telNumber"`
|
||||
AcctType string `json:"acctType,omitempty"`// from uint8
|
||||
AcctId string `json:"acctID,omitempty"`// from uint32
|
||||
Cause string `json:"reason,omitempty"`// from uint8
|
||||
}
|
||||
|
||||
type DeleteSubsRsp struct {
|
||||
|
||||
Result byte
|
||||
ErrorCode uint32
|
||||
|
||||
Code string `json:"code"`// 2001: succ; else: fail, from uint32
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type UpdateBundleUsageReq struct {
|
||||
Msisdn string // tag 2
|
||||
ServiceType byte
|
||||
AccountId uint32
|
||||
PlanId uint32
|
||||
BundleId uint32
|
||||
PlanValue uint64
|
||||
PlanUsedValue uint64
|
||||
SessUpdateTime uint32
|
||||
PlanValueAddThisTime uint64
|
||||
}
|
||||
|
||||
//type RestMsgType int
|
||||
|
||||
const (
|
||||
REST_SEND_AUTHCODE_REQ byte = 5
|
||||
REST_SEND_AUTHCODE_RSP byte = 6
|
||||
REST_QUERY_USERDATA_REQ byte = 7
|
||||
REST_QUERY_USERDATA_RSP byte = 8
|
||||
REST_BUNDLE_SUBS_REQ byte = 9
|
||||
REST_BUNDLE_SUBS_RSP byte = 10
|
||||
REST_BUNDLE_USAGE_REQ byte = 11
|
||||
REST_BUNDLE_USAGE_RSP byte = 12
|
||||
REST_RECHARGE_REQ byte = 13
|
||||
REST_RECHARGE_RSP byte = 14
|
||||
REST_TRANSFER_REQ byte = 15
|
||||
REST_TRANSFER_RSP byte = 16
|
||||
REST_RECHARGE_CARD_REQ byte = 17
|
||||
REST_RECHARGE_CARD_RSP byte = 18
|
||||
REST_CHECK_BALANCE_REQ byte = 19
|
||||
REST_CHECK_BALANCE_RSP byte = 20
|
||||
REST_QUERY_BALANCE_REQ byte = 21
|
||||
REST_QUERY_BALANCE_RSP byte = 22
|
||||
REST_QUERY_RECHARGE_CARD_REQ byte = 23
|
||||
REST_QUERY_RECHARGE_CARD_RSP byte = 24
|
||||
REST_UPDATE_RECHARGE_CARD_REQ byte = 25
|
||||
REST_UPDATE_RECHARGE_CARD_RSP byte = 26
|
||||
|
||||
REST_CRM_PAYMENT_REQ byte = 27
|
||||
REST_CRM_PAYMENT_RES byte = 28
|
||||
|
||||
REST_CRM_SMS_DELIVER_REQ byte = 29
|
||||
REST_CRM_SMS_DELIVER_RES byte = 30
|
||||
|
||||
REST_CRM_CREATE_ACCT_REQ byte = 31
|
||||
REST_CRM_CREATE_ACCT_RES byte = 32
|
||||
|
||||
REST_CRM_QUERY_TARIFF_REQ byte = 33
|
||||
REST_CRM_QUERY_TARIFF_RES byte = 34
|
||||
|
||||
REST_CRM_UPDATE_SUBS_REQ byte = 35
|
||||
REST_CRM_UPDATE_SUBS_RES byte = 36
|
||||
|
||||
REST_CRM_DELETE_SUBS_REQ byte = 37
|
||||
REST_CRM_DELETE_SUBS_RES byte = 38
|
||||
|
||||
REST_CRM_UPDATE_SESS_INFO_REQ byte = 39
|
||||
REST_CRM_UPDATE_SESS_INFO_RES byte = 40
|
||||
|
||||
REST_CRM_UPDATE_PLAN_INFO_REQ byte = 41
|
||||
REST_CRM_UPDATE_PLAN_INFO_RES byte = 42
|
||||
|
||||
REST_CRM_RENT_CHARGE byte = 43
|
||||
)
|
||||
|
||||
type RestReq struct {
|
||||
MsgType byte
|
||||
Src_id uint16
|
||||
Dst_id uint16
|
||||
|
||||
AuthCodeReq AuthCodeReq
|
||||
QueryUserDataReq QueryUserDataReq
|
||||
BundleSubsReq BundleSubsReq
|
||||
BundleUsageReq BundleUsageReq
|
||||
RechargeReq RechargeReq
|
||||
TransferReq TransferReq
|
||||
RechargeCardReq RechargeCardReq
|
||||
CheckBalanceReq CheckBalanceReq
|
||||
QueryBalanceReq QueryBalanceReq
|
||||
QueryRechargeCardReq QueryRechargeCardReq
|
||||
UpdateRechargeCardReq UpdateRechargeCardReq
|
||||
CreateAccountReq CreateAccountReq
|
||||
UpdateSubsReq UpdateSubsReq
|
||||
DeleteSubsReq DeleteSubsReq
|
||||
}
|
||||
|
||||
type RestRsp struct {
|
||||
MsgType byte
|
||||
Src_id uint16
|
||||
Dst_id uint16
|
||||
|
||||
AuthCodeRsp AuthCodeRsp
|
||||
QueryUserDataRsp QueryUserDataRsp
|
||||
BundleSubsRsp BundleSubsRsp
|
||||
BundleUsageRsp BundleUsageRsp
|
||||
RechargeRsp RechargeRsp
|
||||
TransferRsp TransferRsp
|
||||
RechargeCardRsp RechargeCardRsp
|
||||
CheckBalanceRsp CheckBalanceRsp
|
||||
QueryBalanceRsp QueryBalanceRsp
|
||||
QueryRechargeCardRsp QueryRechargeCardRsp
|
||||
UpdateRechargeCardRsp UpdateRechargeCardRsp
|
||||
CreateAccountRsp CreateAccountRsp
|
||||
UpdateSubsRsp UpdateSubsRsp
|
||||
DeleteSubsRsp DeleteSubsRsp
|
||||
UpdateBundleUsageReq UpdateBundleUsageReq
|
||||
UpdateSubsReq UpdateSubsReq
|
||||
}
|
||||
|
||||
148
proxy_go/Nrestful/rest_response.go
Normal file
148
proxy_go/Nrestful/rest_response.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package Nrestful
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
//"fmt"
|
||||
//"log"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
//"time"
|
||||
|
||||
l4g "proxy/logger"//"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func sendAuthCodeRsp(w http.ResponseWriter, statusCode int, rsp *RestRsp) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
if statusCode != http.StatusOK {
|
||||
rsp.AuthCodeRsp.Code = strconv.Itoa(5001)
|
||||
} else {
|
||||
//rsp.AuthCodeRsp.Code = 2001
|
||||
}
|
||||
w.WriteHeader(statusCode)
|
||||
l4g.RestLog.Warnf("Send AuthCodeRsp: %v", rsp.AuthCodeRsp)
|
||||
|
||||
// w.Write at last
|
||||
json.NewEncoder(w).Encode(rsp.AuthCodeRsp)
|
||||
//
|
||||
}
|
||||
|
||||
func sendQueryUserDataRsp(w http.ResponseWriter, statusCode int, rsp *RestRsp) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
if statusCode != http.StatusOK {
|
||||
rsp.QueryUserDataRsp.Code = strconv.Itoa(5001)
|
||||
} else {
|
||||
//rsp.QueryUserDataRsp.Code = 2001
|
||||
}
|
||||
w.WriteHeader(statusCode)
|
||||
l4g.RestLog.Warnf("Send QueryUserDataRsp: %v", rsp.QueryUserDataRsp)
|
||||
|
||||
// w.Write at last
|
||||
json.NewEncoder(w).Encode(rsp.QueryUserDataRsp)
|
||||
//
|
||||
}
|
||||
|
||||
func sendBundleSubsRsp(w http.ResponseWriter, statusCode int, rsp *RestRsp) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
if statusCode != http.StatusOK {
|
||||
rsp.BundleSubsRsp.Code = strconv.Itoa(5001)
|
||||
} else {
|
||||
//rsp.BundleSubsRsp.Code = 2001
|
||||
}
|
||||
w.WriteHeader(statusCode)
|
||||
l4g.RestLog.Warnf("Send QueryUserDataRsp: %v", rsp.BundleSubsRsp)
|
||||
|
||||
// w.Write at last
|
||||
json.NewEncoder(w).Encode(rsp.BundleSubsRsp)
|
||||
//
|
||||
}
|
||||
|
||||
func sendRechargeRsp(w http.ResponseWriter, statusCode int, rsp *RestRsp) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
if statusCode != http.StatusOK {
|
||||
rsp.RechargeRsp.Code = strconv.Itoa(5001)
|
||||
} else {
|
||||
//rsp.RechargeRsp.Code = 2001
|
||||
}
|
||||
w.WriteHeader(statusCode)
|
||||
l4g.RestLog.Warnf("Send RechargeRsp: %v", rsp.RechargeRsp)
|
||||
|
||||
// w.Write at last
|
||||
json.NewEncoder(w).Encode(rsp.RechargeRsp)
|
||||
//
|
||||
}
|
||||
|
||||
func sendTransferRsp(w http.ResponseWriter, statusCode int, rsp *RestRsp) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
if statusCode != http.StatusOK {
|
||||
rsp.TransferRsp.Code = strconv.Itoa(5001)
|
||||
} else {
|
||||
//rsp.TransferRsp.Code = 2001
|
||||
}
|
||||
w.WriteHeader(statusCode)
|
||||
l4g.RestLog.Warnf("Send TransferRsp: %v", rsp.TransferRsp)
|
||||
|
||||
// w.Write at last
|
||||
json.NewEncoder(w).Encode(rsp.TransferRsp)
|
||||
//
|
||||
}
|
||||
|
||||
func sendQueryBalanceRsp(w http.ResponseWriter, statusCode int, rsp *RestRsp) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
if statusCode != http.StatusOK {
|
||||
rsp.QueryBalanceRsp.Code = strconv.Itoa(5001)
|
||||
} else {
|
||||
//rsp.QueryBalanceRsp.Code = 2001
|
||||
}
|
||||
w.WriteHeader(statusCode)
|
||||
l4g.RestLog.Warnf("Send QueryBalanceRsp: %v", rsp.QueryBalanceRsp)
|
||||
|
||||
// w.Write at last
|
||||
json.NewEncoder(w).Encode(rsp.QueryBalanceRsp)
|
||||
//
|
||||
}
|
||||
|
||||
func sendCreateAccountRsp(w http.ResponseWriter, statusCode int, rsp *RestRsp) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
if statusCode != http.StatusOK {
|
||||
rsp.CreateAccountRsp.Code = strconv.Itoa(5001)
|
||||
} else {
|
||||
//rsp.CreateAccountRsp.Code = 2001
|
||||
}
|
||||
w.WriteHeader(statusCode)
|
||||
l4g.RestLog.Warnf("Send CreateAccountRsp: %v", rsp.CreateAccountRsp)
|
||||
|
||||
// w.Write at last
|
||||
json.NewEncoder(w).Encode(rsp.CreateAccountRsp)
|
||||
//
|
||||
}
|
||||
|
||||
func sendUpdateSubsRsp(w http.ResponseWriter, statusCode int, rsp *RestRsp) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
if statusCode != http.StatusOK {
|
||||
rsp.UpdateSubsRsp.Code = strconv.Itoa(5001)
|
||||
} else {
|
||||
//rsp.UpdateSubsRsp.Code = 2001
|
||||
}
|
||||
w.WriteHeader(statusCode)
|
||||
l4g.RestLog.Warnf("Send UpdateSubsRsp: %v", rsp.UpdateSubsRsp)
|
||||
|
||||
// w.Write at last
|
||||
json.NewEncoder(w).Encode(rsp.UpdateSubsRsp)
|
||||
//
|
||||
}
|
||||
|
||||
func sendDeleteSubsRsp(w http.ResponseWriter, statusCode int, rsp *RestRsp) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
if statusCode != http.StatusOK {
|
||||
rsp.DeleteSubsRsp.Code = strconv.Itoa(5001)
|
||||
} else {
|
||||
//rsp.DeleteSubsRsp.Code = 2001
|
||||
}
|
||||
w.WriteHeader(statusCode)
|
||||
l4g.RestLog.Warnf("Send DeleteSubsRsp: %v", rsp.DeleteSubsRsp)
|
||||
|
||||
// w.Write at last
|
||||
json.NewEncoder(w).Encode(rsp.DeleteSubsRsp)
|
||||
//
|
||||
}
|
||||
|
||||
423
proxy_go/Nrestful/rest_router.go
Normal file
423
proxy_go/Nrestful/rest_router.go
Normal file
@@ -0,0 +1,423 @@
|
||||
package Nrestful
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"fmt"
|
||||
"log"
|
||||
"encoding/json"
|
||||
mysql "proxy/Nmysql"
|
||||
|
||||
//"strconv"
|
||||
"time"
|
||||
|
||||
l4g "proxy/logger"//"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func StartHttpServer(addr string) {
|
||||
http.HandleFunc("/", handler)
|
||||
http.HandleFunc("/authcode", authcodeHandler)
|
||||
http.HandleFunc("/query_userdata", query_userdataHandler)
|
||||
http.HandleFunc("/bundle_subs", bundle_subsHandler)
|
||||
//http.HandleFunc("/bundle_usage", bundle_usageHandler)
|
||||
http.HandleFunc("/recharge", rechargeHandler)
|
||||
http.HandleFunc("/transfer", transferHandler)
|
||||
//http.HandleFunc("/recharge_card", recharge_cardHandler)
|
||||
//http.HandleFunc("/check_balance", check_balanceHandler)
|
||||
http.HandleFunc("/query_balane", query_balanceHandler)// query_balane in Pcap from CRM
|
||||
http.HandleFunc("/openPackage", openPackageHandler)
|
||||
http.HandleFunc("/updateSubs", updateSubsHandler)
|
||||
http.HandleFunc("/deleteSubs", deleteSubsHandler)
|
||||
|
||||
log.Fatal(http.ListenAndServe(addr, nil))
|
||||
}
|
||||
|
||||
// handler echoes the Path component of the requested URL.
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
|
||||
}
|
||||
|
||||
func authcodeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
rsp := RestRsp{MsgType: REST_SEND_AUTHCODE_RSP}
|
||||
var pRsp *RestRsp = &rsp
|
||||
if r.Body == nil {
|
||||
rsp.AuthCodeRsp.Message = "request body missing"
|
||||
sendAuthCodeRsp(w, http.StatusBadRequest, pRsp)
|
||||
return
|
||||
}
|
||||
|
||||
var req RestReq
|
||||
req.MsgType = REST_SEND_AUTHCODE_REQ
|
||||
err := json.NewDecoder(r.Body).Decode(&req.AuthCodeReq)
|
||||
if err != nil {
|
||||
rsp.AuthCodeRsp.Message = "err decode authCodeReq"
|
||||
sendAuthCodeRsp(w, http.StatusBadRequest, pRsp)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Got authcode request: %v", req.AuthCodeReq)
|
||||
|
||||
// handle ==========================================
|
||||
port := AssignPort(&req)
|
||||
if port == nil {
|
||||
rsp.AuthCodeRsp.Message = "err port full"
|
||||
sendAuthCodeRsp(w, http.StatusInternalServerError, pRsp)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Assign port for authcode request, src[%d], dst[%d]", req.Src_id, req.Dst_id)
|
||||
|
||||
ocsRsp := getOcsRsp(&req, port)
|
||||
if ocsRsp == nil {
|
||||
rsp.AuthCodeRsp.Message = "ocs rsp timeout"
|
||||
sendAuthCodeRsp(w, http.StatusInternalServerError, pRsp)
|
||||
} else {
|
||||
sendAuthCodeRsp(w, http.StatusOK, ocsRsp)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func query_userdataHandler(w http.ResponseWriter, r *http.Request) {
|
||||
rsp := RestRsp{MsgType: REST_QUERY_USERDATA_RSP}
|
||||
var pRsp *RestRsp = &rsp
|
||||
if r.Body == nil {
|
||||
rsp.QueryUserDataRsp.Message = "request body missing"
|
||||
sendQueryUserDataRsp(w, http.StatusBadRequest, pRsp)
|
||||
return
|
||||
}
|
||||
|
||||
var req RestReq
|
||||
req.MsgType = REST_QUERY_USERDATA_REQ
|
||||
err := json.NewDecoder(r.Body).Decode(&req.QueryUserDataReq)
|
||||
if err != nil {
|
||||
rsp.QueryUserDataRsp.Message = "err decode QueryUserDataReq"
|
||||
sendQueryUserDataRsp(w, http.StatusBadRequest, pRsp)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Got query_userdata request: %v", req.QueryUserDataReq)
|
||||
|
||||
// handle ==========================================
|
||||
port := AssignPort(&req)
|
||||
if port == nil {
|
||||
rsp.QueryUserDataRsp.Message = "err port full"
|
||||
sendQueryUserDataRsp(w, http.StatusInternalServerError, pRsp)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Assign port for query_userdata request, src[%d], dst[%d]", req.Src_id, req.Dst_id)
|
||||
|
||||
ocsRsp := getOcsRsp(&req, port)
|
||||
if ocsRsp == nil {
|
||||
rsp.QueryUserDataRsp.Message = "ocs rsp timeout"
|
||||
sendQueryUserDataRsp(w, http.StatusInternalServerError, pRsp)
|
||||
} else {
|
||||
sendQueryUserDataRsp(w, http.StatusOK, ocsRsp)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func bundle_subsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
rsp := RestRsp{MsgType: REST_BUNDLE_SUBS_RSP}
|
||||
var pRsp *RestRsp = &rsp
|
||||
if r.Body == nil {
|
||||
rsp.BundleSubsRsp.Message = "request body missing"
|
||||
sendBundleSubsRsp(w, http.StatusBadRequest, pRsp)
|
||||
return
|
||||
}
|
||||
|
||||
var req RestReq
|
||||
req.MsgType = REST_BUNDLE_SUBS_REQ
|
||||
err := json.NewDecoder(r.Body).Decode(&req.BundleSubsReq)
|
||||
if err != nil {
|
||||
rsp.BundleSubsRsp.Message = "err decode BundleSubsReq"
|
||||
sendBundleSubsRsp(w, http.StatusBadRequest, pRsp)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Got BundleSubs request: %v", req.BundleSubsReq)
|
||||
|
||||
// handle ==========================================
|
||||
port := AssignPort(&req)
|
||||
if port == nil {
|
||||
rsp.BundleSubsRsp.Message = "err port full"
|
||||
sendBundleSubsRsp(w, http.StatusInternalServerError, pRsp)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Assign port for BundleSubs request, src[%d], dst[%d]", req.Src_id, req.Dst_id)
|
||||
|
||||
ocsRsp := getOcsRsp(&req, port)
|
||||
if ocsRsp == nil {
|
||||
rsp.BundleSubsRsp.Message = "ocs rsp timeout"
|
||||
sendBundleSubsRsp(w, http.StatusInternalServerError, pRsp)
|
||||
} else {
|
||||
sendBundleSubsRsp(w, http.StatusOK, ocsRsp)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func rechargeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
rsp := RestRsp{MsgType: REST_RECHARGE_RSP}
|
||||
var pRsp *RestRsp = &rsp
|
||||
if r.Body == nil {
|
||||
rsp.RechargeRsp.Message = "request body missing"
|
||||
sendRechargeRsp(w, http.StatusBadRequest, pRsp)
|
||||
return
|
||||
}
|
||||
|
||||
var req RestReq
|
||||
req.MsgType = REST_RECHARGE_REQ
|
||||
err := json.NewDecoder(r.Body).Decode(&req.RechargeReq)
|
||||
if err != nil {
|
||||
rsp.RechargeRsp.Message = "err decode RechargeReq"
|
||||
sendRechargeRsp(w, http.StatusBadRequest, pRsp)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Got query_userdata request: %v", req.RechargeReq)
|
||||
|
||||
// handle ==========================================
|
||||
port := AssignPort(&req)
|
||||
if port == nil {
|
||||
rsp.RechargeRsp.Message = "err port full"
|
||||
sendRechargeRsp(w, http.StatusInternalServerError, pRsp)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Assign port for query_userdata request, src[%d], dst[%d]", req.Src_id, req.Dst_id)
|
||||
|
||||
ocsRsp := getOcsRsp(&req, port)
|
||||
if ocsRsp == nil {
|
||||
rsp.RechargeRsp.Message = "ocs rsp timeout"
|
||||
sendRechargeRsp(w, http.StatusInternalServerError, pRsp)
|
||||
} else {
|
||||
sendRechargeRsp(w, http.StatusOK, ocsRsp)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func transferHandler(w http.ResponseWriter, r *http.Request) {
|
||||
rsp := RestRsp{MsgType: REST_TRANSFER_RSP}
|
||||
var pRsp *RestRsp = &rsp
|
||||
if r.Body == nil {
|
||||
rsp.TransferRsp.Message = "request body missing"
|
||||
sendTransferRsp(w, http.StatusBadRequest, pRsp)
|
||||
return
|
||||
}
|
||||
|
||||
var req RestReq
|
||||
req.MsgType = REST_TRANSFER_REQ
|
||||
err := json.NewDecoder(r.Body).Decode(&req.TransferReq)
|
||||
if err != nil {
|
||||
rsp.TransferRsp.Message = "err decode TransferReq"
|
||||
sendTransferRsp(w, http.StatusBadRequest, pRsp)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Got Transfer request: %v", req.TransferReq)
|
||||
|
||||
// handle ==========================================
|
||||
port := AssignPort(&req)
|
||||
if port == nil {
|
||||
rsp.TransferRsp.Message = "err port full"
|
||||
sendTransferRsp(w, http.StatusInternalServerError, pRsp)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Assign port for Transfer request, src[%d], dst[%d]", req.Src_id, req.Dst_id)
|
||||
|
||||
ocsRsp := getOcsRsp(&req, port)
|
||||
if ocsRsp == nil {
|
||||
rsp.TransferRsp.Message = "ocs rsp timeout"
|
||||
sendTransferRsp(w, http.StatusInternalServerError, pRsp)
|
||||
} else {
|
||||
sendTransferRsp(w, http.StatusOK, ocsRsp)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func query_balanceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
rsp := RestRsp{MsgType: REST_QUERY_BALANCE_RSP}
|
||||
var pRsp *RestRsp = &rsp
|
||||
if r.Body == nil {
|
||||
rsp.QueryBalanceRsp.Message = "request body missing"
|
||||
sendQueryBalanceRsp(w, http.StatusBadRequest, pRsp)
|
||||
return
|
||||
}
|
||||
|
||||
var req RestReq
|
||||
req.MsgType = REST_QUERY_BALANCE_REQ
|
||||
err := json.NewDecoder(r.Body).Decode(&req.QueryBalanceReq)
|
||||
if err != nil {
|
||||
rsp.QueryBalanceRsp.Message = "err decode QueryBalanceReq"
|
||||
sendQueryBalanceRsp(w, http.StatusBadRequest, pRsp)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Got QueryBalance request: %v", req.QueryBalanceReq)
|
||||
|
||||
// handle ==========================================
|
||||
port := AssignPort(&req)
|
||||
if port == nil {
|
||||
rsp.QueryBalanceRsp.Message = "err port full"
|
||||
sendQueryBalanceRsp(w, http.StatusInternalServerError, pRsp)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Assign port for QueryBalance request, src[%d], dst[%d]", req.Src_id, req.Dst_id)
|
||||
|
||||
ocsRsp := getOcsRsp(&req, port)
|
||||
if ocsRsp == nil {
|
||||
rsp.QueryBalanceRsp.Message = "ocs rsp timeout"
|
||||
sendQueryBalanceRsp(w, http.StatusInternalServerError, pRsp)
|
||||
} else {
|
||||
sendQueryBalanceRsp(w, http.StatusOK, ocsRsp)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func openPackageHandler(w http.ResponseWriter, r *http.Request) {
|
||||
rsp := RestRsp{MsgType: REST_CRM_CREATE_ACCT_RES}
|
||||
var pRsp *RestRsp = &rsp
|
||||
if r.Body == nil {
|
||||
rsp.CreateAccountRsp.Message = "request body missing"
|
||||
sendCreateAccountRsp(w, http.StatusBadRequest, pRsp)
|
||||
return
|
||||
}
|
||||
|
||||
var req RestReq
|
||||
req.MsgType = REST_CRM_CREATE_ACCT_REQ
|
||||
err := json.NewDecoder(r.Body).Decode(&req.CreateAccountReq)
|
||||
if err != nil {
|
||||
rsp.CreateAccountRsp.Message = "err decode CreateAccountReq"
|
||||
sendCreateAccountRsp(w, http.StatusBadRequest, pRsp)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Got CreateAccount request: %v", req.CreateAccountReq)
|
||||
|
||||
// handle ==========================================
|
||||
port := AssignPort(&req)
|
||||
if port == nil {
|
||||
rsp.CreateAccountRsp.Message = "err port full"
|
||||
sendCreateAccountRsp(w, http.StatusInternalServerError, pRsp)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Assign port for CreateAccount request, src[%d], dst[%d]", req.Src_id, req.Dst_id)
|
||||
|
||||
ocsRsp := getOcsRsp(&req, port)
|
||||
if ocsRsp == nil {
|
||||
rsp.CreateAccountRsp.Message = "ocs rsp timeout"
|
||||
sendCreateAccountRsp(w, http.StatusInternalServerError, pRsp)
|
||||
} else {
|
||||
sendCreateAccountRsp(w, http.StatusOK, ocsRsp)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func updateSubsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
rsp := RestRsp{MsgType: REST_CRM_UPDATE_SUBS_RES}
|
||||
var pRsp *RestRsp = &rsp
|
||||
if r.Body == nil {
|
||||
rsp.UpdateSubsRsp.Message = "request body missing"
|
||||
sendUpdateSubsRsp(w, http.StatusBadRequest, pRsp)
|
||||
return
|
||||
}
|
||||
|
||||
var req RestReq
|
||||
req.MsgType = REST_CRM_UPDATE_SUBS_REQ
|
||||
err := json.NewDecoder(r.Body).Decode(&req.UpdateSubsReq)
|
||||
if err != nil {
|
||||
rsp.UpdateSubsRsp.Message = "err decode UpdateSubsReq"
|
||||
sendUpdateSubsRsp(w, http.StatusBadRequest, pRsp)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Got UpdateSubs request: %v", req.UpdateSubsReq)
|
||||
|
||||
// handle ==========================================
|
||||
port := AssignPort(&req)
|
||||
if port == nil {
|
||||
rsp.UpdateSubsRsp.Message = "err port full"
|
||||
sendUpdateSubsRsp(w, http.StatusInternalServerError, pRsp)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Assign port for UpdateSubs request, src[%d], dst[%d]", req.Src_id, req.Dst_id)
|
||||
|
||||
ocsRsp := getOcsRsp(&req, port)
|
||||
if ocsRsp == nil {
|
||||
rsp.UpdateSubsRsp.Message = "ocs rsp timeout"
|
||||
sendUpdateSubsRsp(w, http.StatusInternalServerError, pRsp)
|
||||
} else {
|
||||
sendUpdateSubsRsp(w, http.StatusOK, ocsRsp)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func deleteSubsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
rsp := RestRsp{MsgType: REST_CRM_DELETE_SUBS_RES}
|
||||
var pRsp *RestRsp = &rsp
|
||||
if r.Body == nil {
|
||||
rsp.DeleteSubsRsp.Message = "request body missing"
|
||||
sendDeleteSubsRsp(w, http.StatusBadRequest, pRsp)
|
||||
return
|
||||
}
|
||||
|
||||
var req RestReq
|
||||
req.MsgType = REST_CRM_DELETE_SUBS_REQ
|
||||
err := json.NewDecoder(r.Body).Decode(&req.DeleteSubsReq)
|
||||
if err != nil {
|
||||
rsp.DeleteSubsRsp.Message = "err decode DeleteSubsReq"
|
||||
sendDeleteSubsRsp(w, http.StatusBadRequest, pRsp)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Got DeleteSubs request: %v", req.DeleteSubsReq)
|
||||
|
||||
mysql.CrmDeleteSubsProfile(req.DeleteSubsReq.TelNumber, req.DeleteSubsReq.AcctType, req.DeleteSubsReq.AcctId)
|
||||
if req.DeleteSubsReq.AcctType != "1" {// not mobile
|
||||
pRsp.DeleteSubsRsp.Result = 0
|
||||
pRsp.DeleteSubsRsp.ErrorCode = 2001
|
||||
sendDeleteSubsRsp(w, http.StatusOK, pRsp)
|
||||
return
|
||||
}
|
||||
|
||||
// handle ==========================================
|
||||
port := AssignPort(&req)
|
||||
if port == nil {
|
||||
rsp.DeleteSubsRsp.Message = "err port full"
|
||||
sendDeleteSubsRsp(w, http.StatusInternalServerError, pRsp)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Assign port for DeleteSubs request, src[%d], dst[%d]", req.Src_id, req.Dst_id)
|
||||
|
||||
ocsRsp := getOcsRsp(&req, port)
|
||||
if ocsRsp == nil {
|
||||
rsp.DeleteSubsRsp.Message = "ocs rsp timeout"
|
||||
sendDeleteSubsRsp(w, http.StatusInternalServerError, pRsp)
|
||||
} else {
|
||||
sendDeleteSubsRsp(w, http.StatusOK, ocsRsp)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getOcsRsp(req *RestReq, port *Port) (*RestRsp) {
|
||||
c := make(chan RestRsp)
|
||||
//port.Rsp = make(chan RestRsp)
|
||||
port.Rsp = c
|
||||
|
||||
go SendOcsUdpReq(req)
|
||||
|
||||
defer ReleasePort(req.Src_id)
|
||||
|
||||
timeout := time.After(5 * time.Second)
|
||||
select {
|
||||
case rsp, ok := <-c:
|
||||
if !ok {// chan closed
|
||||
l4g.RestLog.Warnf("chan getOcsRsp closed by other!")
|
||||
return nil
|
||||
} else {
|
||||
close(c)
|
||||
l4g.RestLog.Debugf("getOcsRsp succ, close chan.")
|
||||
return &rsp
|
||||
}
|
||||
case <-timeout:
|
||||
l4g.RestLog.Warnf("chan getOcsRsp timeout, close chan!")
|
||||
close(c)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
5
proxy_go/Nrestful/udp_msg.go
Normal file
5
proxy_go/Nrestful/udp_msg.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package Nrestful
|
||||
|
||||
import (
|
||||
)
|
||||
|
||||
688
proxy_go/Nrestful/udp_routers.go
Normal file
688
proxy_go/Nrestful/udp_routers.go
Normal file
@@ -0,0 +1,688 @@
|
||||
package Nrestful
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"proxy/MsgDef"
|
||||
rdb "proxy/Nredis"
|
||||
"strconv"
|
||||
_ "strings"
|
||||
"time"
|
||||
|
||||
l4g "proxy/logger" //"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var ocsConn *net.UDPConn
|
||||
|
||||
func closeConnect() {
|
||||
ocsConn.Close()
|
||||
ocsConn = nil
|
||||
}
|
||||
|
||||
var locIp, ocsIp string
|
||||
var locPort, ocsPort int
|
||||
|
||||
func Connect_ocs(loc string, rem string, loc_port int, rem_port int) error {
|
||||
locIp = loc
|
||||
ocsIp = rem
|
||||
locPort = loc_port
|
||||
ocsPort = rem_port
|
||||
|
||||
return connect_ocs()
|
||||
}
|
||||
|
||||
func connect_ocs() error {
|
||||
loc_ip := net.ParseIP(locIp)
|
||||
rem_ip := net.ParseIP(ocsIp)
|
||||
|
||||
srcAddr := &net.UDPAddr{IP: loc_ip, Port: locPort}
|
||||
dstAddr := &net.UDPAddr{IP: rem_ip, Port: ocsPort}
|
||||
|
||||
var err error
|
||||
ocsConn, err = net.DialUDP("udp", srcAddr, dstAddr)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
defer closeConnect()
|
||||
|
||||
//conn.Write([]byte("hello"))
|
||||
data := make([]byte, 1500)
|
||||
//var recLen uint16 = 0
|
||||
//var msgId uint8 = 0
|
||||
for {
|
||||
n, err := ocsConn.Read(data)
|
||||
|
||||
//msgId = data[1]
|
||||
//recLen = uint16(data[2])
|
||||
//recLen = (recLen << 8) + uint16(data[3])
|
||||
if err != nil {
|
||||
//break
|
||||
} else {
|
||||
// check param
|
||||
handle_udp_msg_from_ocs(data, n)
|
||||
}
|
||||
//fmt.Printf("read %s from <%s>\n", data[:n], conn.RemoteAddr())
|
||||
}
|
||||
l4g.RestLog.Errorln("ocs read thread exit")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//type UdpMsgIE byte
|
||||
|
||||
const (
|
||||
IE_NULL = iota
|
||||
IE_SRC_REF // UdpMsgIE = 1
|
||||
IE_DST_REF
|
||||
IE_MSISDN
|
||||
IE_RESULT
|
||||
IE_ERROR_CODE
|
||||
IE_BALANCE// 6
|
||||
IE_MO_EXPIRY
|
||||
IE_MT_EXPIRY
|
||||
IE_USERNAME
|
||||
IE_PASSWORD
|
||||
IE_MSG_CONTENT //value=11
|
||||
IE_STATUS
|
||||
IE_REMARK
|
||||
IE_GROUP_NAME// 0x0e
|
||||
IE_MO_VOICE_MIN// in minute
|
||||
IE_REMAIN_MO_VOICE_SEC// in second
|
||||
IE_REMAIN_MO_VOICE_MIN// in minute
|
||||
IE_MT_VOICE_MIN// in minute
|
||||
IE_REMAIN_MT_VOICE_SEC// in second
|
||||
IE_REMAIN_MT_VOICE_MIN// in minute
|
||||
IE_SMS_NUM//value=21
|
||||
IE_REMAIN_SMS_NUM
|
||||
IE_DATA_VOL_MB// in MB
|
||||
IE_REMAIN_DATA_VOL_KB// in KB
|
||||
IE_REMAIN_DATA_VOL_MB// in MB
|
||||
IE_PAY_TYPE
|
||||
IE_AMOUNT
|
||||
IE_VALID_DAYS
|
||||
IE_EXPIRY_TIME
|
||||
IE_MSISDN_TRANS_OUT
|
||||
IE_MSISDN_TRANS_IN //value=31
|
||||
IE_BALANCE_AVAILABLE
|
||||
IE_RECHARGE_AMOUNT
|
||||
IE_RECHARGE_TYPE
|
||||
IE_RECHARGE_CARD_STATUS
|
||||
IE_RECHARGE_CARD_FACE_VALUE
|
||||
IE_RECHARGE_CARD_EXPIRED_TS
|
||||
IE_RECHARGE_CARD_UPDATED_TS
|
||||
IE_CUSTOMER_ID
|
||||
IE_ACCOUNT_ID
|
||||
IE_PRODUCT_ID//value=41
|
||||
IE_PLAN_ID
|
||||
IE_RENT_CHARGE
|
||||
IE_BIRTHDAY
|
||||
IE_SMS_CONTENT
|
||||
IE_SERVICE_TYPE
|
||||
IE_TARIIFF_PREFIX
|
||||
IE_TARIFF_UNIT
|
||||
IE_TARIFF_CHARGE
|
||||
IE_TARIFF_DISCOUNT
|
||||
IE_PLAN_VALUE//value=51
|
||||
IE_PLAN_USED_VALUE
|
||||
IE_BUNDLE_ID
|
||||
IE_CAUSE
|
||||
IE_SESS_FLAG
|
||||
IE_TIMESTAMP
|
||||
IE_CONSUME_VALUE
|
||||
IE_CALLED_NUMBER
|
||||
IE_UE_IP
|
||||
IE_GW_IP
|
||||
IE_CUG_ID
|
||||
IE_VAS_CUG_STATUS
|
||||
IE_SESS_UPDATE_TIME
|
||||
IE_PLAN_VALUE_ADD_THIS_TIME
|
||||
IE_USER_CLASS
|
||||
IE_MAX_NUM
|
||||
)
|
||||
|
||||
func handle_udp_msg_from_ocs(buf []byte, msglen int) {
|
||||
var rsp RestRsp
|
||||
|
||||
ret := Decode_udp_msg(buf, msglen, &rsp)
|
||||
if ret < 0 {
|
||||
l4g.RestLog.Errorf("Deocde UDP msg fail, msg[%v]", buf[:msglen])
|
||||
return
|
||||
}
|
||||
|
||||
switch rsp.MsgType {
|
||||
case REST_CRM_UPDATE_PLAN_INFO_REQ:
|
||||
go handleUpdatePlanUsage(&rsp)
|
||||
return
|
||||
case REST_CRM_DELETE_SUBS_RES:
|
||||
handleOcsDeleteSubsRsp(&rsp)
|
||||
case REST_CRM_CREATE_ACCT_RES:
|
||||
//handleOcsDeleteSubsRsp(&rsp)
|
||||
case REST_CRM_UPDATE_SUBS_REQ:
|
||||
go handleOcsUpdateSubsReq(&rsp)
|
||||
return
|
||||
}
|
||||
|
||||
l4g.RestLog.Debugf("Recv UDP msg from OCS, msg_id[%d], src[%d], dst[%d]", rsp.MsgType, rsp.Src_id, rsp.Dst_id)
|
||||
if rsp.Dst_id >= MAX_PORT_NUM {
|
||||
return
|
||||
}
|
||||
port := GetPortInfo(rsp.Dst_id, rsp.Src_id)
|
||||
if port == nil {
|
||||
l4g.RestLog.Errorf("Get port by dst_id[%d] fail, msg id[%d]", rsp.Dst_id, rsp.MsgType)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Get port by dst_id[%d] from UDP rsp, msg id[%d]", rsp.Dst_id, rsp.MsgType)
|
||||
|
||||
go func() {
|
||||
port.Rsp <- rsp
|
||||
}()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func encode_udp_msg(req *RestReq, buf []byte) int {
|
||||
switch req.MsgType {
|
||||
case REST_SEND_AUTHCODE_REQ:
|
||||
case REST_QUERY_USERDATA_REQ:
|
||||
case REST_BUNDLE_SUBS_REQ:
|
||||
case REST_RECHARGE_REQ:
|
||||
case REST_TRANSFER_REQ:
|
||||
case REST_QUERY_BALANCE_REQ:
|
||||
case REST_CRM_CREATE_ACCT_REQ:
|
||||
case REST_CRM_UPDATE_SUBS_REQ:
|
||||
case REST_CRM_DELETE_SUBS_REQ:
|
||||
default:
|
||||
l4g.RestLog.Errorf("Encode rest unsupport msg id[%d]", req.MsgType)
|
||||
return 0
|
||||
}
|
||||
|
||||
var msglen int = 0
|
||||
buf[msglen] = req.MsgType
|
||||
msglen++
|
||||
buf[msglen] = IE_SRC_REF
|
||||
msglen++
|
||||
buf[msglen] = 0x02
|
||||
msglen++
|
||||
binary.BigEndian.PutUint16(buf[msglen:msglen+2], req.Src_id)
|
||||
msglen += 2
|
||||
buf[msglen] = IE_DST_REF
|
||||
msglen++
|
||||
buf[msglen] = 0x02
|
||||
msglen++
|
||||
binary.BigEndian.PutUint16(buf[msglen:msglen+2], req.Dst_id)
|
||||
msglen += 2
|
||||
|
||||
var vallen int = 0
|
||||
switch (req.MsgType) {
|
||||
case REST_SEND_AUTHCODE_REQ:
|
||||
l4g.RestLog.Debugf("Encode REST_SEND_AUTHCODE_REQ[%d], %#v", req.MsgType, req.AuthCodeReq)
|
||||
// IE_MSISDN
|
||||
buf[msglen] = IE_MSISDN
|
||||
msglen++
|
||||
vallen = len(req.AuthCodeReq.TelNumber)
|
||||
buf[msglen] = byte(vallen)
|
||||
msglen++
|
||||
copy(buf[msglen:msglen+vallen], ([]byte(req.AuthCodeReq.TelNumber)))
|
||||
msglen += vallen
|
||||
|
||||
// IE_MSG_CONTENT
|
||||
buf[msglen] = IE_MSG_CONTENT
|
||||
msglen++
|
||||
vallen = len(req.AuthCodeReq.Content)
|
||||
binary.BigEndian.PutUint16(buf[msglen:msglen+2], uint16(vallen))
|
||||
msglen += 2
|
||||
copy(buf[msglen:msglen+vallen], ([]byte(req.AuthCodeReq.Content)))
|
||||
msglen += vallen
|
||||
|
||||
case REST_QUERY_USERDATA_REQ:
|
||||
l4g.RestLog.Debugf("Encode REST_QUERY_USERDATA_REQ[%d], %#v", req.MsgType, req.QueryUserDataReq)
|
||||
// IE_MSISDN
|
||||
buf[msglen] = IE_MSISDN
|
||||
msglen++
|
||||
vallen = len(req.QueryUserDataReq.TelNumber)
|
||||
buf[msglen] = byte(vallen)
|
||||
msglen++
|
||||
copy(buf[msglen:msglen+vallen], ([]byte(req.QueryUserDataReq.TelNumber)))
|
||||
msglen += vallen
|
||||
|
||||
case REST_BUNDLE_SUBS_REQ:
|
||||
l4g.RestLog.Debugf("Encode REST_BUNDLE_SUBS_REQ[%d], %#v", req.MsgType, req.BundleSubsReq)
|
||||
// IE_MSISDN
|
||||
buf[msglen] = IE_MSISDN
|
||||
msglen++
|
||||
vallen = len(req.BundleSubsReq.TelNumber)
|
||||
buf[msglen] = byte(vallen)
|
||||
msglen++
|
||||
copy(buf[msglen:msglen+vallen], ([]byte(req.BundleSubsReq.TelNumber)))
|
||||
msglen += vallen
|
||||
|
||||
buf[msglen] = IE_PAY_TYPE
|
||||
msglen++
|
||||
buf[msglen] = 0x01
|
||||
msglen++
|
||||
pay_type, _ := strconv.Atoi(req.BundleSubsReq.PayType)
|
||||
buf[msglen] = uint8(pay_type)
|
||||
msglen += 0x01
|
||||
|
||||
buf[msglen] = IE_AMOUNT
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
amount, _ := strconv.Atoi(req.BundleSubsReq.ChargedAmount)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(amount))
|
||||
msglen += 0x04
|
||||
|
||||
buf[msglen] = IE_MO_VOICE_MIN
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
moVocMin, _ := strconv.Atoi(req.BundleSubsReq.MoVoiceMinute)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(moVocMin))
|
||||
msglen += 0x04
|
||||
|
||||
buf[msglen] = IE_MT_VOICE_MIN
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
mtVocMin, _ := strconv.Atoi(req.BundleSubsReq.MtVoiceMinute)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(mtVocMin))
|
||||
msglen += 0x04
|
||||
|
||||
buf[msglen] = IE_SMS_NUM
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
smsNum, _ := strconv.Atoi(req.BundleSubsReq.SmsVolume)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(smsNum))
|
||||
msglen += 0x04
|
||||
|
||||
buf[msglen] = IE_DATA_VOL_MB
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
dataVol, _ := strconv.Atoi(req.BundleSubsReq.DataVolume)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(dataVol))
|
||||
msglen += 0x04
|
||||
|
||||
buf[msglen] = IE_VALID_DAYS
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
validDays, _ := strconv.Atoi(req.BundleSubsReq.ValidPeriod)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(validDays))
|
||||
msglen += 0x04
|
||||
|
||||
case REST_RECHARGE_REQ:
|
||||
l4g.RestLog.Debugf("Encode REST_RECHARGE_REQ[%d], %#v", req.MsgType, req.RechargeReq)
|
||||
// IE_MSISDN
|
||||
buf[msglen] = IE_MSISDN
|
||||
msglen++
|
||||
vallen = len(req.RechargeReq.TelNumber)
|
||||
buf[msglen] = byte(vallen)
|
||||
msglen++
|
||||
copy(buf[msglen:msglen+vallen], ([]byte(req.RechargeReq.TelNumber)))
|
||||
msglen += vallen
|
||||
|
||||
buf[msglen] = IE_AMOUNT
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
amount, _ := strconv.Atoi(req.RechargeReq.Amount)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(amount))
|
||||
msglen += 0x04
|
||||
|
||||
buf[msglen] = IE_RECHARGE_TYPE
|
||||
msglen++
|
||||
buf[msglen] = 0x01
|
||||
msglen++
|
||||
op_type, _ := strconv.Atoi(req.RechargeReq.OpType)
|
||||
buf[msglen] = uint8(op_type)
|
||||
msglen += 0x01
|
||||
|
||||
buf[msglen] = IE_VALID_DAYS
|
||||
msglen++
|
||||
buf[msglen] = 0x02
|
||||
msglen++
|
||||
valid_days, _ := strconv.Atoi(req.RechargeReq.ValidyDays)
|
||||
binary.BigEndian.PutUint16(buf[msglen:msglen+2], uint16(valid_days))
|
||||
msglen += 0x02
|
||||
|
||||
case REST_TRANSFER_REQ:
|
||||
l4g.RestLog.Debugf("Encode REST_TRANSFER_REQ[%d], %#v", req.MsgType, req.TransferReq)
|
||||
// IE_MSISDN_TRANS_OUT
|
||||
buf[msglen] = IE_MSISDN_TRANS_OUT
|
||||
msglen++
|
||||
vallen = len(req.TransferReq.TransferOut)
|
||||
buf[msglen] = byte(vallen)
|
||||
msglen++
|
||||
copy(buf[msglen:msglen+vallen], ([]byte(req.TransferReq.TransferOut)))
|
||||
msglen += vallen
|
||||
|
||||
buf[msglen] = IE_MSISDN_TRANS_IN
|
||||
msglen++
|
||||
vallen = len(req.TransferReq.TransferIn)
|
||||
buf[msglen] = byte(vallen)
|
||||
msglen++
|
||||
copy(buf[msglen:msglen+vallen], ([]byte(req.TransferReq.TransferIn)))
|
||||
msglen += vallen
|
||||
|
||||
buf[msglen] = IE_AMOUNT
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
amount, _ := strconv.Atoi(req.TransferReq.Amount)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(amount))
|
||||
msglen += 0x04
|
||||
|
||||
case REST_QUERY_BALANCE_REQ:
|
||||
l4g.RestLog.Debugf("Encode REST_QUERY_BALANCE_REQ[%d], %#v", req.MsgType, req.QueryBalanceReq)
|
||||
// IE_MSISDN
|
||||
buf[msglen] = IE_MSISDN
|
||||
msglen++
|
||||
vallen = len(req.QueryBalanceReq.TelNumber)
|
||||
buf[msglen] = byte(vallen)
|
||||
msglen++
|
||||
copy(buf[msglen:msglen+vallen], ([]byte(req.QueryBalanceReq.TelNumber)))
|
||||
msglen += vallen
|
||||
|
||||
case REST_CRM_CREATE_ACCT_REQ:
|
||||
l4g.RestLog.Debugf("Encode REST_CRM_CREATE_ACCT_REQ[%d], %#v", req.MsgType, req.CreateAccountReq)
|
||||
// IE_MSISDN
|
||||
buf[msglen] = IE_MSISDN
|
||||
msglen++
|
||||
vallen = len(req.CreateAccountReq.ServiceNbr)
|
||||
buf[msglen] = byte(vallen)
|
||||
msglen++
|
||||
copy(buf[msglen:msglen+vallen], ([]byte(req.CreateAccountReq.ServiceNbr)))
|
||||
msglen += vallen
|
||||
|
||||
buf[msglen] = IE_CUSTOMER_ID
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
custId, _ := strconv.Atoi(req.CreateAccountReq.CustId)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(custId))
|
||||
msglen += 0x04
|
||||
|
||||
buf[msglen] = IE_ACCOUNT_ID
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
acctId, _ := strconv.Atoi(req.CreateAccountReq.AccountId)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(acctId))
|
||||
msglen += 0x04
|
||||
|
||||
buf[msglen] = IE_PRODUCT_ID
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
prdId, _ := strconv.Atoi(req.CreateAccountReq.ProductInstId)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(prdId))
|
||||
msglen += 0x04
|
||||
|
||||
buf[msglen] = IE_PLAN_ID
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
planId, _ := strconv.Atoi(req.CreateAccountReq.PackageId)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(planId))
|
||||
msglen += 0x04
|
||||
|
||||
buf[msglen] = IE_BALANCE
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
balance, _ := strconv.Atoi(req.CreateAccountReq.Balance)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(balance))
|
||||
msglen += 0x04
|
||||
|
||||
buf[msglen] = IE_EXPIRY_TIME
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
expire_time, err := time.Parse("2006-01-02 15:04:05", req.CreateAccountReq.BalanceExpDate)
|
||||
if err != nil {
|
||||
l4g.RestLog.Errorf("Error CreateAccountReq BalanceExpDate[%s]", req.CreateAccountReq.BalanceExpDate)
|
||||
return -1
|
||||
}
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(expire_time.Unix()))
|
||||
msglen += 0x04
|
||||
|
||||
buf[msglen] = IE_RENT_CHARGE
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
rent_charge, _ := strconv.Atoi(req.CreateAccountReq.RentCharge)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(rent_charge))
|
||||
msglen += 0x04
|
||||
|
||||
buf[msglen] = IE_BIRTHDAY
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
birthday, err := time.Parse("2006-01-02", req.CreateAccountReq.Birthday)
|
||||
if err != nil {
|
||||
l4g.RestLog.Errorf("Error CreateAccountReq Birthday[%s]", req.CreateAccountReq.Birthday)
|
||||
return -1
|
||||
}
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(birthday.Unix()))
|
||||
msglen += 0x04
|
||||
|
||||
buf[msglen] = IE_CUG_ID
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
cugId, _ := strconv.Atoi(req.CreateAccountReq.CugId)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(cugId))
|
||||
msglen += 0x04
|
||||
|
||||
buf[msglen] = IE_USER_CLASS
|
||||
msglen++
|
||||
buf[msglen] = 0x01
|
||||
msglen++
|
||||
userClass, _ := strconv.Atoi(req.CreateAccountReq.UserClass)
|
||||
buf[msglen] = byte(userClass)
|
||||
msglen += 0x01
|
||||
|
||||
case REST_CRM_UPDATE_SUBS_REQ:
|
||||
l4g.RestLog.Debugf("Encode REST_CRM_UPDATE_SUBS_REQ[%d], %#v", req.MsgType, req.UpdateSubsReq)
|
||||
// IE_MSISDN
|
||||
buf[msglen] = IE_MSISDN
|
||||
msglen++
|
||||
vallen = len(req.UpdateSubsReq.TelNumber)
|
||||
buf[msglen] = byte(vallen)
|
||||
msglen++
|
||||
copy(buf[msglen:msglen+vallen], ([]byte(req.UpdateSubsReq.TelNumber)))
|
||||
msglen += vallen
|
||||
|
||||
buf[msglen] = IE_ACCOUNT_ID
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
acctId, _ := strconv.Atoi(req.UpdateSubsReq.AccountId)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(acctId))
|
||||
msglen += 0x04
|
||||
|
||||
if (len(req.UpdateSubsReq.Status) > 0) {
|
||||
buf[msglen] = IE_STATUS
|
||||
msglen++
|
||||
buf[msglen] = 0x01
|
||||
msglen++
|
||||
status, _ := strconv.Atoi(req.UpdateSubsReq.Status)
|
||||
buf[msglen] = byte(status)
|
||||
msglen += 0x01
|
||||
}
|
||||
|
||||
if (len(req.UpdateSubsReq.Balance) > 0) {
|
||||
buf[msglen] = IE_BALANCE
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
balance, _ := strconv.Atoi(req.UpdateSubsReq.Balance)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(balance))
|
||||
msglen += 0x04
|
||||
}
|
||||
|
||||
if (len(req.UpdateSubsReq.BalanceExpDate) > 0) {
|
||||
buf[msglen] = IE_EXPIRY_TIME
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
expiryTime, err := time.Parse("2006-01-02 15:04:05", req.UpdateSubsReq.BalanceExpDate)
|
||||
if err != nil {
|
||||
l4g.RestLog.Errorf("Error UpdateSubsReq BalanceExpDate[%s]", req.UpdateSubsReq.BalanceExpDate)
|
||||
return -1
|
||||
}
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(expiryTime.Unix()))
|
||||
msglen += 0x04
|
||||
}
|
||||
|
||||
if (len(req.UpdateSubsReq.PackageId) > 0) {
|
||||
buf[msglen] = IE_PLAN_ID
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
planId, _ := strconv.Atoi(req.UpdateSubsReq.PackageId)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(planId))
|
||||
msglen += 0x04
|
||||
}
|
||||
|
||||
if (len(req.UpdateSubsReq.RentCharge) > 0) {
|
||||
buf[msglen] = IE_RENT_CHARGE
|
||||
msglen++
|
||||
buf[msglen] = 0x04
|
||||
msglen++
|
||||
rentCharge, _ := strconv.Atoi(req.UpdateSubsReq.RentCharge)
|
||||
binary.BigEndian.PutUint32(buf[msglen:msglen+4], uint32(rentCharge))
|
||||
msglen += 0x04
|
||||
}
|
||||
|
||||
if (len(req.UpdateSubsReq.VasCugStatus) > 0) {
|
||||
buf[msglen] = IE_VAS_CUG_STATUS
|
||||
msglen++
|
||||
buf[msglen] = 0x01
|
||||
msglen++
|
||||
vasCugStatus, _ := strconv.Atoi(req.UpdateSubsReq.VasCugStatus)
|
||||
buf[msglen] = byte(vasCugStatus)
|
||||
msglen += 0x01
|
||||
}
|
||||
|
||||
case REST_CRM_DELETE_SUBS_REQ:
|
||||
l4g.RestLog.Debugf("Encode REST_CRM_DELETE_SUBS_REQ[%d], %#v", req.MsgType, req.DeleteSubsReq)
|
||||
// IE_MSISDN
|
||||
buf[msglen] = IE_MSISDN
|
||||
msglen++
|
||||
vallen = len(req.DeleteSubsReq.TelNumber)
|
||||
buf[msglen] = byte(vallen)
|
||||
msglen++
|
||||
copy(buf[msglen:msglen+vallen], ([]byte(req.DeleteSubsReq.TelNumber)))
|
||||
msglen += vallen
|
||||
|
||||
buf[msglen] = IE_CAUSE
|
||||
msglen++
|
||||
buf[msglen] = 0x01
|
||||
msglen++
|
||||
buf[msglen] = 0
|
||||
msglen += 0x01
|
||||
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
|
||||
return msglen
|
||||
}
|
||||
|
||||
func SendOcsUdpReq(req *RestReq) {
|
||||
if ocsConn == nil {
|
||||
return
|
||||
}
|
||||
|
||||
l4g.RestLog.Debugf("Send UDP msg to OCS, msg_id[%d], src[%d], dst[%d]", req.MsgType, req.Src_id, req.Dst_id)
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
|
||||
msglen := encode_udp_msg(req, buf)
|
||||
|
||||
if msglen > 0 {
|
||||
ocsConn.Write([]byte(buf[:msglen]))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func SendNtfSms2Ocs(serviceNbr string, smsContent string) {
|
||||
var req RestReq
|
||||
req.MsgType = REST_SEND_AUTHCODE_REQ
|
||||
req.Src_id = 0xFFFF
|
||||
req.Dst_id = 0xFFFF
|
||||
req.AuthCodeReq.TelNumber = serviceNbr
|
||||
req.AuthCodeReq.Content = smsContent
|
||||
|
||||
SendOcsUdpReq(&req)
|
||||
}
|
||||
|
||||
func SendCrtAcct2Ocs(serviceNbr string, ai *MsgDef.ChgSyncMobile, cugId, userClass, rent int) {
|
||||
var req RestReq
|
||||
req.MsgType = REST_CRM_CREATE_ACCT_REQ
|
||||
req.Src_id = 0xFFFF
|
||||
req.Dst_id = 0xFFFF
|
||||
req.CreateAccountReq.ServiceNbr = serviceNbr
|
||||
req.CreateAccountReq.CustId = strconv.Itoa(ai.CustId)
|
||||
req.CreateAccountReq.AccountId = strconv.Itoa(ai.AcctId)
|
||||
req.CreateAccountReq.ProductInstId = strconv.Itoa(ai.PrdInstId)
|
||||
req.CreateAccountReq.PackageId = strconv.Itoa(ai.OfrId)
|
||||
req.CreateAccountReq.UserClass = strconv.Itoa(userClass)
|
||||
req.CreateAccountReq.CugId = strconv.Itoa(cugId)
|
||||
req.CreateAccountReq.Balance = strconv.Itoa(ai.Balance)
|
||||
|
||||
//t, _ := time.Parse("2006-01-02 15:04:05", ai.BirthDate)
|
||||
req.CreateAccountReq.Birthday = ai.BirthDate
|
||||
//t, _ := time.Parse("2006-01-02 15:04:05", ai.BalanceExpDate)
|
||||
req.CreateAccountReq.BalanceExpDate = ai.BalanceExpDate
|
||||
req.CreateAccountReq.RentCharge = strconv.Itoa(rent)
|
||||
|
||||
SendOcsUdpReq(&req)
|
||||
}
|
||||
|
||||
func CreateAcct2Ocs(serviceNbr string, ai *MsgDef.ChgSyncMobile, cugId, userClass, rent int, key string) {
|
||||
var req RestReq
|
||||
req.MsgType = REST_CRM_CREATE_ACCT_REQ
|
||||
req.Src_id = 0xFFFF
|
||||
req.Dst_id = 0xFFFF
|
||||
req.CreateAccountReq.ServiceNbr = serviceNbr
|
||||
req.CreateAccountReq.CustId = strconv.Itoa(ai.CustId)
|
||||
req.CreateAccountReq.AccountId = strconv.Itoa(ai.AcctId)
|
||||
req.CreateAccountReq.ProductInstId = strconv.Itoa(ai.PrdInstId)
|
||||
req.CreateAccountReq.PackageId = strconv.Itoa(ai.OfrId)
|
||||
req.CreateAccountReq.UserClass = strconv.Itoa(userClass)
|
||||
req.CreateAccountReq.CugId = strconv.Itoa(cugId)
|
||||
req.CreateAccountReq.Balance = strconv.Itoa(ai.Balance)
|
||||
|
||||
//t, _ := time.Parse("2006-01-02 15:04:05", ai.BirthDate)
|
||||
req.CreateAccountReq.Birthday = ai.BirthDate
|
||||
//t, _ := time.Parse("2006-01-02 15:04:05", ai.BalanceExpDate)
|
||||
req.CreateAccountReq.BalanceExpDate = ai.BalanceExpDate
|
||||
req.CreateAccountReq.RentCharge = strconv.Itoa(rent)
|
||||
|
||||
l4g.RestLog.Debugf("Got createAcct request: %v", req.CreateAccountReq)
|
||||
|
||||
// handle ==========================================
|
||||
port := AssignPort(&req)
|
||||
if port == nil {
|
||||
l4g.RestLog.Warnf("Assign port for createAcct request fail, serviceNbr[%s], key[%s]", serviceNbr, key)
|
||||
return
|
||||
}
|
||||
l4g.RestLog.Debugf("Assign port for createAcct request, serviceNbr, src[%d], dst[%d]", serviceNbr, req.Src_id, req.Dst_id)
|
||||
|
||||
ocsRsp := getOcsRsp(&req, port)
|
||||
if ocsRsp == nil {
|
||||
l4g.RestLog.Warnf("getOcsRsp timeout for createAcct request, serviceNbr[%s], key[%s]", serviceNbr, key)
|
||||
} else {
|
||||
l4g.RestLog.Infof("getOcsRsp succ for createAcct request, serviceNbr[%s], key[%s]", serviceNbr, key)
|
||||
rdb.RdsDelMsg2OcsKey(key)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
80
proxy_go/Readme.txt
Normal file
80
proxy_go/Readme.txt
Normal file
@@ -0,0 +1,80 @@
|
||||
Ubuntu18.04
|
||||
1.<2E><>װredis-server
|
||||
#apt-get install redis-server
|
||||
#vim /etc/redis/redis.conf
|
||||
1)<29><><EFBFBD><EFBFBD>master
|
||||
bind 192.168.7.90
|
||||
protected-mode no
|
||||
unixsocket /var/run/redis/redis-server.sock
|
||||
unixsocketperm 700
|
||||
2)<29><><EFBFBD><EFBFBD>slave
|
||||
bind 192.168.4.61
|
||||
protected-mode no
|
||||
unixsocket /var/run/redis/redis-server.sock
|
||||
unixsocketperm 700
|
||||
slaveof 192.168.7.90 6379
|
||||
|
||||
2.<2E><>װredis-sentinel
|
||||
#apt-get install redis-sentinel
|
||||
#vim /etc/redis/sentinel.conf
|
||||
1)sentinel 1
|
||||
bind 192.168.4.63
|
||||
protected-mode no
|
||||
sentinel monitor mymaster 192.168.4.63 6379 1
|
||||
2)sentinel 2
|
||||
bind 192.168.4.64
|
||||
protected-mode no
|
||||
sentinel monitor mymaster 192.168.4.63 6379 1
|
||||
|
||||
2.<2E><>װsctp
|
||||
sudo apt install lksctp-tools
|
||||
sudo apt install libsctp-dev
|
||||
Ubuntu20.04<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ
|
||||
diam/diam_adaptor<6F><72>makefile<6C>ļ<EFBFBD><C4BC><EFBFBD>CFLAGS<47><53> -DOLD_SCTP_SOCKET_API
|
||||
|
||||
3.<2E><>װlibhiredis
|
||||
sudo apt install libhiredis-dev
|
||||
<EFBFBD><EFBFBD>װglib
|
||||
#sudo apt-get install libglib2.0-dev ## 20.04
|
||||
<EFBFBD><EFBFBD>װidn
|
||||
#sudo apt -y install idn
|
||||
<EFBFBD><EFBFBD>װgnutls
|
||||
#sudo apt-get install -y libgnutls-dev
|
||||
#apt-get install libghc-gnutls-dev ## 20.04
|
||||
#apt-get install libgcrypt20-dev
|
||||
#idna.h: No such file or directory<72><79><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
#apt-get install libidn11-dev
|
||||
|
||||
4.<2E><>װmysqldump
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>mysqldump,Ȼ<><C8BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE>װ<EFBFBD><D7B0>Ҫ<EFBFBD>İ汾
|
||||
#apt install mysql-client-5.7 ## 18.04
|
||||
#apt install mysql-client libmysqlclient-dev ## mysql-server 20.04
|
||||
|
||||
5.<2E><>װcanal
|
||||
#dpkg -i canal-1.0.1.amd64.deb
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>canal<EFBFBD><EFBFBD>
|
||||
#vim /usr/local/etc/canal/config.yaml
|
||||
log:
|
||||
output: file
|
||||
level: info #log<6F><67><EFBFBD><EFBFBD>
|
||||
path: /var/log/proxy_go.log
|
||||
maxAge: 120
|
||||
rotationTime: 3
|
||||
mysqlDb:
|
||||
addr: 192.168.1.211:3306 #mysql DB,nfk mysql DB: 10.10.1.156:3306
|
||||
username: canal
|
||||
password: canal
|
||||
redisDb:
|
||||
netType: unix
|
||||
addr: /var/run/kvdb.sock
|
||||
# sentinelAddrs: # <20><>sentinel addr
|
||||
# - 192.168.7.90:26379
|
||||
# - 192.168.4.61:26379
|
||||
telnetServer:
|
||||
addr: 192.168.7.90:4100
|
||||
canalServer:
|
||||
reinit: true # <20><><EFBFBD><EFBFBD>ʱ<EFBFBD>Ƿ<EFBFBD><C7B7><EFBFBD><EFBFBD><EFBFBD>ȫ<EFBFBD><C8AB>ͬ<EFBFBD><CDAC>mysql<71><6C>redis
|
||||
standalone: false # <20>Ƿ<C7B7><F1B5A5BB><EFBFBD><EFBFBD>У<EFBFBD>trueʱ<65><CAB1><EFBFBD><EFBFBD>ʵʱͬ<CAB1><CDAC>mysql<71><6C><EFBFBD><EFBFBD><EFBFBD><EFBFBD>falseʱֻ<CAB1>б<EFBFBD><D0B1><EFBFBD>redis<69><73>masterʱ<72><CAB1>ͬ<EFBFBD><CDAC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD>canal<61><6C>ͬ<EFBFBD><CDAC><EFBFBD><EFBFBD>
|
||||
|
||||
6.<2E><>װocs
|
||||
|
||||
331
proxy_go/TelnetShellServer/shell_server.go
Normal file
331
proxy_go/TelnetShellServer/shell_server.go
Normal file
@@ -0,0 +1,331 @@
|
||||
package TelnetShellServer
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-oi"
|
||||
"github.com/reiver/go-telnet"
|
||||
"github.com/reiver/go-telnet/telsh"
|
||||
"proxy/canal"
|
||||
"proxy/config"
|
||||
|
||||
"io"
|
||||
|
||||
sdb "proxy/Nmysql"
|
||||
rdb "proxy/Nredis"
|
||||
)
|
||||
|
||||
// Command not found!
|
||||
func notFoundHandler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
_, _ = oi.LongWriteString(stdout, "\rCommand not found!")
|
||||
_, _ = oi.LongWriteString(stdout, "\n\r")
|
||||
_, _ = oi.LongWriteString(stdout, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Syn DB
|
||||
func syncDbHandler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
//rdb.ClrTariffAndBundle()
|
||||
_ = sdb.LoadAcctTblFromMysql()
|
||||
_, _ = oi.LongWriteString(stdout, "\rSync DB, done!")
|
||||
_, _ = oi.LongWriteString(stdout, "\n\r")
|
||||
_, _ = oi.LongWriteString(stdout, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Syn msisdn
|
||||
func syncMsisdnHandler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
//rdb.ClrTariffAndBundle()
|
||||
_ = sdb.LoadOneAcctFromMysql(args[1])
|
||||
_, _ = oi.LongWriteString(stdout, "\rSync DB, done!")
|
||||
_, _ = oi.LongWriteString(stdout, "\n\r")
|
||||
_, _ = oi.LongWriteString(stdout, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func syncDbProducer(ctx telnet.Context, name string, args ...string) telsh.Handler{
|
||||
if len(args) > 0 {
|
||||
if args[0] == "db" {
|
||||
return telsh.PromoteHandlerFunc(syncDbHandler)
|
||||
} else if args[0] == "msisdn" && len(args)>=2 {
|
||||
return telsh.PromoteHandlerFunc(syncMsisdnHandler, args...)
|
||||
} else {
|
||||
return telsh.PromoteHandlerFunc(notFoundHandler)
|
||||
}
|
||||
} else {
|
||||
return telsh.PromoteHandlerFunc(notFoundHandler)
|
||||
}
|
||||
}
|
||||
|
||||
// Clr DB
|
||||
func clearDbHandler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
rdb.ClrTariffAndBundle()
|
||||
_, _ = oi.LongWriteString(stdout, "\rClear DB Finish!")
|
||||
_, _ = oi.LongWriteString(stdout, "\n\r")
|
||||
_, _ = oi.LongWriteString(stdout, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clr Expired rr
|
||||
func clearRrHandler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
rdb.ClrExpRes()
|
||||
_, _ = oi.LongWriteString(stdout, "\rClear Expired RR Finish!")
|
||||
_, _ = oi.LongWriteString(stdout, "\n\r")
|
||||
_, _ = oi.LongWriteString(stdout, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func clearDbProducer(ctx telnet.Context, name string, args ...string) telsh.Handler{
|
||||
if len(args) > 0 {
|
||||
if args[0] == "db" {
|
||||
return telsh.PromoteHandlerFunc(clearDbHandler)
|
||||
} else if args[0] == "rr" {
|
||||
return telsh.PromoteHandlerFunc(clearRrHandler)
|
||||
} else {
|
||||
return telsh.PromoteHandlerFunc(notFoundHandler)
|
||||
}
|
||||
} else {
|
||||
return telsh.PromoteHandlerFunc(notFoundHandler)
|
||||
}
|
||||
}
|
||||
|
||||
// get ofr
|
||||
func getOfrHandler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
rdb.RdbGetTariffByOfrId(args[1])
|
||||
_, _ = oi.LongWriteString(stdout, "\rGet ofr Finish!")
|
||||
_, _ = oi.LongWriteString(stdout, "\n\r")
|
||||
_, _ = oi.LongWriteString(stdout, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// get act
|
||||
func getActHandler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
//_, _ = oi.LongWriteString(stdout, "\n\rdebug2, len(args)="+fmt.Sprintf("%d:%v", len(args), args))
|
||||
rsp, _ := rdb.RdbGetAcctRecordByServiceNbr(args[1])
|
||||
_, _ = oi.LongWriteString(stdout, "\r"+rsp+"\n\rGet acct Finish!")
|
||||
_, _ = oi.LongWriteString(stdout, "\n\r")
|
||||
_, _ = oi.LongWriteString(stdout, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// get rat
|
||||
func getRatHandler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
rsp, _ := rdb.RdbGetRrByServiceNbrAndDomain(args[1])
|
||||
_, _ = oi.LongWriteString(stdout, "\r"+rsp+"\n\rGet Rr Finish!")
|
||||
_, _ = oi.LongWriteString(stdout, "\n\r")
|
||||
_, _ = oi.LongWriteString(stdout, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// get state
|
||||
func getStateHandler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
var state string
|
||||
/*if rdb.CheckIfRdbMaster() == true {
|
||||
state = "Redis: master; state: " + canal.CurState
|
||||
} else {*/
|
||||
state = "Redis: master; state: " + canal.CurState
|
||||
//}
|
||||
_, _ = oi.LongWriteString(stdout, "\r"+state)
|
||||
_, _ = oi.LongWriteString(stdout, "\n\r")
|
||||
_, _ = oi.LongWriteString(stdout, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getRecordProducer(ctx telnet.Context, name string, args ...string) telsh.Handler{
|
||||
if len(args) >= 2 {
|
||||
/*if args[0] == "ofr" {
|
||||
return telsh.PromoteHandlerFunc(getOfrHandler)
|
||||
} else */if args[0] == "act" {
|
||||
return telsh.PromoteHandlerFunc(getActHandler, args...)
|
||||
} else if args[0] == "rat" {
|
||||
return telsh.PromoteHandlerFunc(getRatHandler, args...)
|
||||
} else if args[0] == "state" {
|
||||
return telsh.PromoteHandlerFunc(getStateHandler, args...)
|
||||
} else {
|
||||
return telsh.PromoteHandlerFunc(notFoundHandler)
|
||||
}
|
||||
} else if len(args) == 1 {
|
||||
if args[0] == "state" {
|
||||
return telsh.PromoteHandlerFunc(getStateHandler)
|
||||
} else {
|
||||
return telsh.PromoteHandlerFunc(notFoundHandler)
|
||||
}
|
||||
} else {
|
||||
return telsh.PromoteHandlerFunc(notFoundHandler)
|
||||
}
|
||||
}
|
||||
|
||||
// enable bundle notify
|
||||
func enableVoiceBundleNtfHandler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
config.SetBundleUsageNtf(1, true)
|
||||
_, _ = oi.LongWriteString(stdout, "\r"+"Comand OK")
|
||||
_, _ = oi.LongWriteString(stdout, "\n\r")
|
||||
_, _ = oi.LongWriteString(stdout, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func enableDataBundleNtfHandler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
config.SetBundleUsageNtf(2, true)
|
||||
_, _ = oi.LongWriteString(stdout, "\r"+"Comand OK")
|
||||
_, _ = oi.LongWriteString(stdout, "\n\r")
|
||||
_, _ = oi.LongWriteString(stdout, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func enableSmsBundleNtfHandler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
config.SetBundleUsageNtf(3, true)
|
||||
_, _ = oi.LongWriteString(stdout, "\r"+"Comand OK")
|
||||
_, _ = oi.LongWriteString(stdout, "\n\r")
|
||||
_, _ = oi.LongWriteString(stdout, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func enableParamProducer(ctx telnet.Context, name string, args ...string) telsh.Handler{
|
||||
if len(args) == 1 {
|
||||
if args[0] == "voc75ntf" {
|
||||
return telsh.PromoteHandlerFunc(enableVoiceBundleNtfHandler)
|
||||
} else if args[0] == "dat75ntf" {
|
||||
return telsh.PromoteHandlerFunc(enableDataBundleNtfHandler)
|
||||
} else if args[0] == "sms75ntf" {
|
||||
return telsh.PromoteHandlerFunc(enableSmsBundleNtfHandler)
|
||||
} else {
|
||||
return telsh.PromoteHandlerFunc(notFoundHandler)
|
||||
}
|
||||
} else {
|
||||
return telsh.PromoteHandlerFunc(notFoundHandler)
|
||||
}
|
||||
}
|
||||
|
||||
// disable bundle notify
|
||||
func disableVoiceBundleNtfHandler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
config.SetBundleUsageNtf(1, false)
|
||||
_, _ = oi.LongWriteString(stdout, "\r"+"Comand OK")
|
||||
_, _ = oi.LongWriteString(stdout, "\n\r")
|
||||
_, _ = oi.LongWriteString(stdout, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func disableDataBundleNtfHandler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
config.SetBundleUsageNtf(2, false)
|
||||
_, _ = oi.LongWriteString(stdout, "\r"+"Comand OK")
|
||||
_, _ = oi.LongWriteString(stdout, "\n\r")
|
||||
_, _ = oi.LongWriteString(stdout, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func disableSmsBundleNtfHandler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
config.SetBundleUsageNtf(3, false)
|
||||
_, _ = oi.LongWriteString(stdout, "\r"+"Comand OK")
|
||||
_, _ = oi.LongWriteString(stdout, "\n\r")
|
||||
_, _ = oi.LongWriteString(stdout, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func disableParamProducer(ctx telnet.Context, name string, args ...string) telsh.Handler{
|
||||
if len(args) == 1 {
|
||||
if args[0] == "voc75ntf" {
|
||||
return telsh.PromoteHandlerFunc(disableVoiceBundleNtfHandler)
|
||||
} else if args[0] == "dat75ntf" {
|
||||
return telsh.PromoteHandlerFunc(disableDataBundleNtfHandler)
|
||||
} else if args[0] == "sms75ntf" {
|
||||
return telsh.PromoteHandlerFunc(disableSmsBundleNtfHandler)
|
||||
} else {
|
||||
return telsh.PromoteHandlerFunc(notFoundHandler)
|
||||
}
|
||||
} else {
|
||||
return telsh.PromoteHandlerFunc(notFoundHandler)
|
||||
}
|
||||
}
|
||||
|
||||
/* version handler
|
||||
func versionHandler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
oi.LongWriteString(stdout, "\rCurrent PCF version:\n\r")
|
||||
oi.LongWriteString(stdout, "\r" + n.GetPcfVersion())
|
||||
oi.LongWriteString(stdout, "\n\r")
|
||||
oi.LongWriteString(stdout, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func versionProducer(ctx telnet.Context, name string, args ...string) telsh.Handler{
|
||||
return telsh.PromoteHandlerFunc(versionHandler)
|
||||
}*/
|
||||
|
||||
func Server(addr string) {
|
||||
|
||||
shellHandler := telsh.NewShellHandler()
|
||||
|
||||
shellHandler.WelcomeMessage = `
|
||||
__ __ ______ _ _____ ____ __ __ ______
|
||||
\ \ / /| ____|| | / ____| / __ \ | \/ || ____|
|
||||
\ \ /\ / / | |__ | | | | | | | || \ / || |__
|
||||
\ \/ \/ / | __| | | | | | | | || |\/| || __|
|
||||
\ /\ / | |____ | |____ | |____ | |__| || | | || |____
|
||||
\/ \/ |______||______| \_____| \____/ |_| |_||______|
|
||||
|
||||
\033[2J\033[1;1HAVAILABLE COMMANDS\n" +
|
||||
========================================================================
|
||||
| Command | Remark |
|
||||
========================================================================
|
||||
| help | Help page. |
|
||||
| date | Current date. |
|
||||
| syn db | Syn Mysql DB to Redis DB. |
|
||||
| clr db | Clear Redis DB. |
|
||||
| clr rr | Clear Expired RR. |
|
||||
| get state | Get loading/init state. |
|
||||
| get act serviceNbr | Get acct info by service number. |
|
||||
| get rat num:domain | Get RR info by service number and domain. |
|
||||
| [en|dis]able voc75ntf| Enable[Disable] %75 usage of voice bundle. |
|
||||
| [en|dis]able dat75ntf| Enable[Disable] %75 usage of data bundle. |
|
||||
| [en|dis]able sms75ntf| Enable[Disable] %75 usage of sms bundle. |
|
||||
| q | Quit. |
|
||||
========================================================================
|
||||
`
|
||||
//shellHandler.Prompt = "pcf:~$"
|
||||
shellHandler.ExitCommandName = "q"
|
||||
shellHandler.ExitMessage = "Good bye...\r\n"
|
||||
|
||||
// Register the "sync db" command.
|
||||
commandName := "syn"
|
||||
commandProducer := telsh.ProducerFunc(syncDbProducer)
|
||||
_ = shellHandler.Register(commandName, commandProducer)
|
||||
|
||||
// Register the "clear db" command.
|
||||
commandName = "clr"
|
||||
commandProducer = clearDbProducer
|
||||
_ = shellHandler.Register(commandName, commandProducer)
|
||||
|
||||
commandName = "get"
|
||||
commandProducer = getRecordProducer
|
||||
_ = shellHandler.Register(commandName, commandProducer)
|
||||
|
||||
commandName = "enable"
|
||||
commandProducer = enableParamProducer
|
||||
_ = shellHandler.Register(commandName, commandProducer)
|
||||
|
||||
commandName = "disable"
|
||||
commandProducer = disableParamProducer
|
||||
_ = shellHandler.Register(commandName, commandProducer)
|
||||
|
||||
// Register the "version" command.
|
||||
//commandName = "version"
|
||||
//commandProducer = telsh.ProducerFunc(versionProducer)
|
||||
//shellHandler.Register(commandName, commandProducer)
|
||||
|
||||
if err := telnet.ListenAndServe(addr, shellHandler); nil != err {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
197
proxy_go/canal/canal.go
Normal file
197
proxy_go/canal/canal.go
Normal file
@@ -0,0 +1,197 @@
|
||||
package canal
|
||||
|
||||
import (
|
||||
"github.com/go-mysql-org/go-mysql/canal"
|
||||
"github.com/go-mysql-org/go-mysql/mysql"
|
||||
"github.com/go-mysql-org/go-mysql/replication"
|
||||
mdb "proxy/Nmysql"
|
||||
rds "proxy/Nredis"
|
||||
"proxy/config"
|
||||
|
||||
//"github.com/siddontang/go-log/log"
|
||||
//"strconv"
|
||||
|
||||
"proxy/logger"
|
||||
)
|
||||
|
||||
type MyEventHandler struct {
|
||||
canal.DummyEventHandler
|
||||
}
|
||||
|
||||
// 监听数据记录
|
||||
func (h *MyEventHandler) OnRow(e *canal.RowsEvent) error {
|
||||
|
||||
logger.CanalLog.Infof("OnRow: %s.%s %s %v", e.Table.Schema, e.Table.Name, e.Action, e.Rows)
|
||||
|
||||
/*if !config.Config.CanalServer.Standalone && rds.CheckIfRdbMaster() == false {
|
||||
logger.CanalLog.Warnf("not stand alone, and change to Slave!")
|
||||
cnl.Close()
|
||||
return nil
|
||||
}*/
|
||||
c := ParseAndFilterBinLog(e)
|
||||
updateRedisTable(c)
|
||||
for columnIndex, curColumn := range e.Table.Columns {
|
||||
logger.CanalLog.Debugf("row info: %v %v %v", curColumn.Name, columnIndex, e.Rows[len(e.Rows)-1][columnIndex])
|
||||
}
|
||||
//cnl.Close()// if slave
|
||||
return nil
|
||||
}
|
||||
|
||||
// 创建,更改,重命名或者删除表时触发,通常会需要清除与表相关的数据,如缓存。 It will be called before OnDDL.
|
||||
func (h *MyEventHandler) OnTableChanged(schema string, table string) error {
|
||||
//
|
||||
logger.CanalLog.Infof("OnTableChanged: %s %s", schema, table)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 监听binlog日志的变化文件与记录的位置
|
||||
func (h *MyEventHandler) OnPosSynced(pos mysql.Position, set mysql.GTIDSet, force bool) error {// update and save
|
||||
// if force == true, 立即同步位置
|
||||
rds.RdbSetBinLogPos(pos.Name, pos.Pos)
|
||||
logger.CanalLog.Infof("OnPosSynced: Name[%v] Pos[%v], force[%t]", pos.Name, pos.Pos, force)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 当产生新的binlog日志后触发(在达到内存的使用限制后(默认为1GB),会开启另一个文件,每个新文件的名称后都会有一个增量.)
|
||||
func (h *MyEventHandler) OnRotate(r *replication.RotateEvent) error {
|
||||
// record := fmt.Sprintf("On Rotate: %v \n", &mysql.Position{Name: string(r.NextLogName), Pos: uint32(r.Position)})
|
||||
// binlog的记录位置,新binlog的文件名
|
||||
logger.CanalLog.Infof("On Rotate: Pos[%v] NextLogName[%v] \n", r.Position, r.NextLogName)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create alter drop truncate(删除当前表再新建一个一模一样的表结构)
|
||||
func (h *MyEventHandler) OnDDL(nextPos mysql.Position, queryEvent *replication.QueryEvent) error {
|
||||
// binlog日志的变化文件与记录的位置
|
||||
logger.CanalLog.Infof("OnDDL: Name[%v] Pos[%v]\n", nextPos.Name, nextPos.Pos)
|
||||
logger.CanalLog.Infof("%v\n %v\n %v\n %v\n %v\n",
|
||||
queryEvent.ExecutionTime,// 猜是执行时间,但测试显示0
|
||||
string(queryEvent.Schema),// 库名
|
||||
string(queryEvent.Query),// 变更的sql语句
|
||||
string(queryEvent.StatusVars[:]),//测试显示乱码
|
||||
queryEvent.SlaveProxyID)// 从库代理ID?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *MyEventHandler) String() string {
|
||||
return "MyEventHandler"
|
||||
}
|
||||
|
||||
var cnl *canal.Canal
|
||||
func StartMyCanal(addr string, username string, password string) {
|
||||
cfg := canal.NewDefaultConfig()
|
||||
cfg.Addr = addr//"192.168.1.211:3306"
|
||||
cfg.User = username
|
||||
cfg.Password = password
|
||||
// We only care table canal_test in test db
|
||||
cfg.Dump.TableDB = "boss"
|
||||
cfg.Dump.Tables = []string{"tb_prd_ofr_detail_inst_551",
|
||||
"tb_bil_tariff", "tb_prd_ofr", "tb_bil_evt_pricing_strategy", "config_area",
|
||||
"tb_bil_pricing_area", "ratable_history", "tb_bil_holiday_rel", "tb_bil_holiday"}//, "tb_prd_prd_inst_551"}
|
||||
cfg.IncludeTableRegex = make([]string, 0, 1)
|
||||
cfg.IncludeTableRegex = append(cfg.IncludeTableRegex, "boss\\.tb_prd_prd_inst_551")
|
||||
cfg.IncludeTableRegex = append(cfg.IncludeTableRegex, "boss\\.tb_prd_ofr_detail_inst_551")
|
||||
cfg.IncludeTableRegex = append(cfg.IncludeTableRegex, "boss\\.tb_bil_tariff")
|
||||
cfg.IncludeTableRegex = append(cfg.IncludeTableRegex, "boss\\.tb_prd_ofr")
|
||||
cfg.IncludeTableRegex = append(cfg.IncludeTableRegex, "boss\\.tb_bil_evt_pricing_strategy")
|
||||
cfg.IncludeTableRegex = append(cfg.IncludeTableRegex, "boss\\.config_area")
|
||||
cfg.IncludeTableRegex = append(cfg.IncludeTableRegex, "boss\\.tb_bil_pricing_area")
|
||||
cfg.IncludeTableRegex = append(cfg.IncludeTableRegex, "boss\\.ratable_history")
|
||||
cfg.IncludeTableRegex = append(cfg.IncludeTableRegex, "boss\\.tb_bil_holiday_rel")
|
||||
cfg.IncludeTableRegex = append(cfg.IncludeTableRegex, "boss\\.tb_bil_holiday")
|
||||
if config.Config.CronCfg.Enabled && config.Config.CronCfg.NtfSms != "" {
|
||||
|
||||
cfg.IncludeTableRegex = append(cfg.IncludeTableRegex, "boss\\.tb_sms_info")
|
||||
}
|
||||
if config.Config.Rest.Enabled {
|
||||
cfg.Dump.Tables = append(cfg.Dump.Tables, "tb_sync_mobile")
|
||||
cfg.IncludeTableRegex = append(cfg.IncludeTableRegex, "boss\\.tb_sync_mobile")
|
||||
}
|
||||
|
||||
c, err := canal.NewCanal(cfg)
|
||||
if err != nil {
|
||||
logger.CanalLog.Errorf("NewCanal, err: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Register a handler to handle RowsEvent
|
||||
c.SetEventHandler(&MyEventHandler{})
|
||||
|
||||
var pos *mysql.Position
|
||||
if config.Config.CanalServer.Reinit {
|
||||
pos = QueryBinLogPos(c)
|
||||
if pos == nil {
|
||||
logger.CanalLog.Errorln("fail to get binlog position.")
|
||||
return
|
||||
}
|
||||
|
||||
logger.CanalLog.Infoln("start to sync mysql DB.")
|
||||
CurState = "loading"
|
||||
if config.Config.CanalServer.FlushBeforeInit {
|
||||
rds.ClrTariffAndBundle()
|
||||
}
|
||||
_ = mdb.LoadAcctTblFromMysql()
|
||||
_ = mdb.LoadAlertSmsFromMysql()
|
||||
//_ = mdb.LoadCreateAcctFromMysql()
|
||||
config.Config.CanalServer.Reinit = false
|
||||
config.SavePcfCfg()
|
||||
logger.CanalLog.Infoln("finish sync mysql DB...")
|
||||
CurState = "synchronize"
|
||||
} else {
|
||||
ret := rds.RdbGetBinLogPos()
|
||||
if ret == nil {
|
||||
logger.CanalLog.Warnln("fail to get redis binlog position, query current position from mysql.")
|
||||
pos = QueryBinLogPos(c)
|
||||
if pos == nil {
|
||||
logger.CanalLog.Errorln("fail to get binlog position.")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
pos = &mysql.Position{Name: ret.File, Pos: ret.Position}
|
||||
}
|
||||
}
|
||||
|
||||
// Start canal
|
||||
cnl = c
|
||||
logger.CanalLog.Infof("Go run")
|
||||
err = c.RunFrom(*pos)
|
||||
if err != nil {
|
||||
logger.CanalLog.Errorf("RunFrom err: %v", err)
|
||||
} else {
|
||||
logger.CanalLog.Warnf("Canal exit!!!!!!")
|
||||
c.Close()
|
||||
}
|
||||
|
||||
/* 从头开始监听
|
||||
err = c.Run()
|
||||
if err != nil {
|
||||
logger.CanalLog.Infof("Run err: %v", err)
|
||||
}*/
|
||||
|
||||
// mysql-bin.000004, 1027
|
||||
// startPos := mysql.Position{Name: "mysql-bin.000004", Pos: 1027}
|
||||
// c.RunFrom(startPos)
|
||||
}
|
||||
|
||||
func QueryBinLogPos(c *canal.Canal) *mysql.Position {
|
||||
var pos mysql.Position
|
||||
ret, err := c.Execute("SHOW MASTER STATUS;")
|
||||
if err != nil {
|
||||
logger.CanalLog.Errorf("SHOW MASTER STATUS err: %v", err)
|
||||
return nil
|
||||
}
|
||||
pos.Name, err = ret.Resultset.GetStringByName(0, "File")
|
||||
if err != nil {
|
||||
logger.CanalLog.Errorf("Get binlog file name err: %v", err)
|
||||
return nil
|
||||
}
|
||||
posTmp, err := ret.Resultset.GetUintByName(0, "Position")
|
||||
if err != nil {
|
||||
logger.CanalLog.Errorf("Get binlog position err: %v", err)
|
||||
return nil
|
||||
}
|
||||
pos.Pos = uint32(posTmp)
|
||||
logger.CanalLog.Infof("Current Position: %v", pos)
|
||||
rds.RdbSetBinLogPos(pos.Name, pos.Pos)
|
||||
return &pos
|
||||
}
|
||||
160
proxy_go/canal/client.go
Normal file
160
proxy_go/canal/client.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package canal
|
||||
|
||||
import (
|
||||
//"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/withlin/canal-go/client"
|
||||
pbe "github.com/withlin/canal-go/protocol/entry"
|
||||
"proxy/logger"
|
||||
)
|
||||
|
||||
func ConectCanalServer(ipaddr string) {
|
||||
|
||||
// 192.168.199.17 替换成你的canal server的地址
|
||||
// example 替换成-e canal.destinations=example 你自己定义的名字
|
||||
connector := client.NewSimpleCanalConnector(ipaddr, 11111, "", "", "example", 60000, 60*60*1000)
|
||||
err := connector.Connect()
|
||||
if err != nil {
|
||||
logger.CanalLog.Errorln(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// https://github.com/alibaba/canal/wiki/AdminGuide
|
||||
//mysql 数据解析关注的表,Perl正则表达式.
|
||||
//
|
||||
//多个正则之间以逗号(,)分隔,转义符需要双斜杠(\\)
|
||||
//
|
||||
//常见例子:
|
||||
//
|
||||
// 1. 所有表:.* or .*\\..*
|
||||
// 2. canal schema下所有表: canal\\..*
|
||||
// 3. canal下的以canal打头的表:canal\\.canal.*
|
||||
// 4. canal schema下的一张表:canal\\.test1
|
||||
// 5. 多个规则组合使用:canal\\..*,mysql.test1,mysql.test2 (逗号分隔)
|
||||
|
||||
filter := "canal.instance.filter.regex=boss.tb_prd_prd_inst_551,boss.tb_prd_ofr_detail_inst_551,boss.tb_bil_tariff,boss.tb_prd_ofr,boss.tb_bil_evt_pricing_strategy,boss.config_area,boss.tb_bil_pricing_area,boss.ratable_history,boss.tb_bil_holiday_rel,boss.tb_bil_holiday"
|
||||
//err = connector.Subscribe(".*\\..*")
|
||||
err = connector.Subscribe(filter)
|
||||
if err != nil {
|
||||
logger.CanalLog.Errorln(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var counter int= 0
|
||||
for {
|
||||
|
||||
message, err := connector.Get(100, nil, nil)
|
||||
if err != nil {
|
||||
logger.CanalLog.Errorln(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
batchId := message.Id
|
||||
if batchId == -1 || len(message.Entries) <= 0 {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
counter++
|
||||
if counter == 300 {
|
||||
logger.CanalLog.Infoln("===Idle===")
|
||||
counter = 0
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
printEntry(message.Entries)
|
||||
counter = 0
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func updateRedisTable(oneR *RecordChange) {
|
||||
var synType int
|
||||
if oneR.EventType == pbe.EventType_INSERT {
|
||||
synType = 1
|
||||
} else if oneR.EventType == pbe.EventType_DELETE {
|
||||
synType = 3
|
||||
} else {
|
||||
synType = 2
|
||||
}
|
||||
switch oneR.TableName {
|
||||
case TbAcctInfo:
|
||||
mdb.UpdateRedisAcctTable(synType, oneR.ChgAcctInfo.PrdInstId, oneR.ChgAcctInfo.ServiceNbr)
|
||||
case TbOfrDetail:
|
||||
mdb.UpdateRedisAcctTableOfr(synType, oneR.ChgOfrDetail.OfrDetailInstId)
|
||||
case TbTariff:
|
||||
mdb.UpdateRedisTariffTable(synType, oneR.ChgTariff.TariffId)
|
||||
case TbOfrInfo:
|
||||
mdb.UpdateRedisTariffTableByOfr(synType, oneR.ChgOfrInfo.OfrId)
|
||||
case TbPricingStrategy:
|
||||
mdb.UpdateRedisTariffTableByStrategy(synType, oneR.ChgPricingStrategy.EventPricingStrategyId)
|
||||
case TbConfigArea:// update prefix table, while update config_area table
|
||||
mdb.UpdateRedisPrefixTableByConfigArea(synType, oneR.ChgConfigArea.AreaId, oneR.ChgConfigArea.OldAreaCode, oneR.ChgConfigArea.NewAreaCode)
|
||||
case TbPricingArea:
|
||||
mdb.UpdateRedisPrefixTable(synType, oneR.ChgPricingArea.NewStrategyId, oneR.ChgPricingArea.NewAreaId)
|
||||
case TbRr:
|
||||
if rrId > 0 {
|
||||
mdb.UpdateRedisRrTable(synType, rrId, ratableVal, usedVal, beginTime, endTime)
|
||||
} else {
|
||||
if ofrId > 0 {// ofr: CALC_PRIORITY
|
||||
mdb.UpdateRdbRrOfrPriority(ofrId, tariffSeq)
|
||||
} else if strategyId > 0 {// strategy: EVENT_PRIORITY
|
||||
mdb.UpdateRdbRrStrategyPriority(strategyId, tariffSeq)
|
||||
}
|
||||
}
|
||||
case TbHoliday:
|
||||
mdb.UpdateRedisHolidayDisTableByTariffId(synType, TariffId, tariffSeq)
|
||||
case TbBilHoliday:
|
||||
mdb.UpdateRedisHolidayDisTableByHolidayId(synType, holidayId)
|
||||
default:
|
||||
}
|
||||
}*/
|
||||
|
||||
func printEntry(entrys []pbe.Entry) {
|
||||
|
||||
for _, entry := range entrys {
|
||||
if entry.GetEntryType() == pbe.EntryType_TRANSACTIONBEGIN || entry.GetEntryType() == pbe.EntryType_TRANSACTIONEND {
|
||||
continue
|
||||
}
|
||||
rowChange := new(pbe.RowChange)
|
||||
|
||||
err := proto.Unmarshal(entry.GetStoreValue(), rowChange)
|
||||
checkError(err)
|
||||
if rowChange != nil {
|
||||
eventType := rowChange.GetEventType()
|
||||
header := entry.GetHeader()
|
||||
logger.CanalLog.Infof("================> binlog[%s : %d],name[%s,%s], eventType: %s", header.GetLogfileName(), header.GetLogfileOffset(), header.GetSchemaName(), header.GetTableName(), header.GetEventType())
|
||||
|
||||
for _, rowData := range rowChange.GetRowDatas() {
|
||||
/*oneRecordChg := ParseAndFilterChange(eventType, header.GetTableName(), rowData)
|
||||
if oneRecordChg == nil {
|
||||
continue
|
||||
}
|
||||
updateRedisTable(oneRecordChg)*/
|
||||
if eventType == pbe.EventType_DELETE {
|
||||
printColumn(rowData.GetBeforeColumns())
|
||||
} else if eventType == pbe.EventType_INSERT {
|
||||
printColumn(rowData.GetAfterColumns())
|
||||
} else {
|
||||
logger.CanalLog.Infoln("-------> before")
|
||||
printColumn(rowData.GetBeforeColumns())
|
||||
logger.CanalLog.Infoln("-------> after")
|
||||
printColumn(rowData.GetAfterColumns())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printColumn(columns []*pbe.Column) {
|
||||
for _, col := range columns {
|
||||
logger.CanalLog.Infof("%s : %s update= %t", col.GetName(), col.GetValue(), col.GetUpdated())
|
||||
}
|
||||
}
|
||||
|
||||
func checkError(err error) {
|
||||
if err != nil {
|
||||
logger.CanalLog.Errorf("Fatal error: %s", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
28
proxy_go/canal/client_fsm.go
Normal file
28
proxy_go/canal/client_fsm.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package canal
|
||||
|
||||
import (
|
||||
"proxy/logger"
|
||||
"time"
|
||||
)
|
||||
|
||||
var CurState string= "init"
|
||||
func CanalFsm(addr string, username string, password string) {
|
||||
t := time.NewTimer(time.Second * 2)
|
||||
defer t.Stop()
|
||||
for {
|
||||
<-t.C// wait for next tick
|
||||
|
||||
//if config.Config.CanalServer.Standalone || (!config.Config.CanalServer.Standalone && rds.CheckIfRdbMaster() == true) {
|
||||
StartMyCanal(addr, username, password)
|
||||
logger.CanalLog.Warnf("Exit Canal!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
|
||||
t.Reset(time.Second * 3)
|
||||
/*} else {
|
||||
CurState = "idle"
|
||||
if config.Config.CanalServer.Reinit {
|
||||
config.Config.CanalServer.Reinit = false
|
||||
config.SavePcfCfg()
|
||||
}
|
||||
t.Reset(time.Second * 2)
|
||||
}*/
|
||||
}
|
||||
}
|
||||
1102
proxy_go/canal/msg.go
Normal file
1102
proxy_go/canal/msg.go
Normal file
File diff suppressed because it is too large
Load Diff
114
proxy_go/canal/sync_redis.go
Normal file
114
proxy_go/canal/sync_redis.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package canal
|
||||
|
||||
import (
|
||||
"github.com/go-mysql-org/go-mysql/canal"
|
||||
. "proxy/MsgDef"
|
||||
mdb "proxy/Nmysql"
|
||||
"proxy/logger"
|
||||
)
|
||||
|
||||
func updateRedisTable(c *RecordChange) {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
var synType int
|
||||
if c.EventType == canal.InsertAction {
|
||||
synType = 1
|
||||
} else if c.EventType == canal.DeleteAction {
|
||||
synType = 3
|
||||
} else {
|
||||
synType = 2
|
||||
}
|
||||
switch c.TableName {
|
||||
case TbAcctInfo:
|
||||
for _, row := range c.ChgAcctInfo {
|
||||
logger.CanalLog.Infof("entry updateRedisTable: TbAcctInfo, type[%d]", synType)
|
||||
mdb.UpdateRedisAcctTable(synType, &row)
|
||||
}
|
||||
case TbOfrDetail:
|
||||
for _, row := range c.ChgOfrDetail {
|
||||
logger.CanalLog.Infof("entry updateRedisTable: TbOfrDetail, type[%d]", synType)
|
||||
mdb.UpdateRedisAcctTableOfr(synType, &row)
|
||||
}
|
||||
case TbTariff:
|
||||
for _, row := range c.ChgTariff {
|
||||
logger.CanalLog.Infof("entry updateRedisTable: TbTariff, type[%d]", synType)
|
||||
mdb.UpdateRedisTariffTable(synType, &row)
|
||||
}
|
||||
case TbOfrInfo:
|
||||
for _, row := range c.ChgOfrInfo {
|
||||
logger.CanalLog.Infof("entry updateRedisTable: TbOfrInfo, type[%d]", synType)
|
||||
mdb.UpdateRedisTariffTableByOfr(synType, &row)
|
||||
}
|
||||
case TbPricingStrategy:
|
||||
for _, row := range c.ChgPricingStrategy {
|
||||
logger.CanalLog.Infof("entry updateRedisTable: TbPricingStrategy, type[%d]", synType)
|
||||
mdb.UpdateRedisTariffTableByStrategy(synType, &row)
|
||||
}
|
||||
case TbConfigArea:// update prefix table, while update config_area table
|
||||
for _, row := range c.ChgConfigArea {
|
||||
logger.CanalLog.Infof("entry updateRedisTable: TbConfigArea, type[%d]", synType)
|
||||
if synType == 3 {
|
||||
mdb.UpdateRedisPrefixTableByConfigArea(synType, &row)
|
||||
} else {
|
||||
mdb.UpdateRedisPrefixTableByConfigArea(synType, &row)
|
||||
}
|
||||
}
|
||||
case TbPricingArea:
|
||||
for _, row := range c.ChgPricingArea {
|
||||
logger.CanalLog.Infof("entry updateRedisTable: TbPricingArea, type[%d]", synType)
|
||||
if synType == 3 {
|
||||
mdb.UpdateRedisPrefixTable(synType, &row)
|
||||
} else {
|
||||
mdb.UpdateRedisPrefixTable(synType, &row)
|
||||
}
|
||||
}
|
||||
case TbRr:
|
||||
for _, row := range c.ChgRr {
|
||||
logger.CanalLog.Infof("entry updateRedisTable: TbRr, type[%d]", synType)
|
||||
//if row.Id > 0 {
|
||||
if synType == 1 {
|
||||
mdb.UpdateRedisRrTable(synType, &row)
|
||||
} else if synType == 3 {
|
||||
mdb.UpdateRedisRrTable(synType, &row)
|
||||
} else {
|
||||
mdb.UpdateRedisRrTable(synType, &row)
|
||||
}
|
||||
|
||||
/*} else {
|
||||
if ofrId > 0 {// ofr: CALC_PRIORITY
|
||||
mdb.UpdateRdbRrOfrPriority(ofrId, tariffSeq)
|
||||
} else if strategyId > 0 {// strategy: EVENT_PRIORITY
|
||||
mdb.UpdateRdbRrStrategyPriority(strategyId, tariffSeq)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
case TbHoliday:
|
||||
for _, row := range c.ChgHoliday {
|
||||
logger.CanalLog.Infof("entry updateRedisTable: TbHoliday, type[%d]", synType)
|
||||
if synType == 1 {
|
||||
mdb.UpdateRedisHolidayDisTableByTariffId(synType, &row)
|
||||
} else if synType == 3 {
|
||||
mdb.UpdateRedisHolidayDisTableByTariffId(synType, &row)
|
||||
} else {
|
||||
mdb.UpdateRedisHolidayDisTableByTariffId(synType, &row)
|
||||
}
|
||||
}
|
||||
case TbBilHoliday:
|
||||
for _, row := range c.ChgBilHoliday {
|
||||
logger.CanalLog.Infof("entry updateRedisTable: TbBilHoliday, type[%d]", synType)
|
||||
mdb.UpdateRedisHolidayDisTableByHolidayId(synType, &row)
|
||||
}
|
||||
case TbSmsInfo:
|
||||
for _, row := range c.ChgAlertSms {
|
||||
logger.CanalLog.Infof("entry updateRedisTable: TbSmsInfo, type[%d]", synType)
|
||||
mdb.UpdateRedisAlertSms(synType, &row)
|
||||
}
|
||||
case TbSyncMobile:
|
||||
/*for _, row := range c.ChgSyncMobile {
|
||||
logger.CanalLog.Infof("entry updateRedisTable: TbSyncMobile, type[%d]", synType)
|
||||
mdb.UpdateRedisSyncMobile(synType, &row)
|
||||
}*/
|
||||
default:
|
||||
}
|
||||
}
|
||||
38
proxy_go/conf/config.yaml
Normal file
38
proxy_go/conf/config.yaml
Normal file
@@ -0,0 +1,38 @@
|
||||
log:
|
||||
level: info
|
||||
mysqlDb:
|
||||
addr: 192.168.1.211:3306
|
||||
username: boss
|
||||
password: mysqlboss
|
||||
redisDb:
|
||||
netType: unix
|
||||
addr: /var/run/kvdb.sock
|
||||
##sentinelAddrs:
|
||||
## - 192.168.7.90:26379
|
||||
## - 192.168.4.61:26379
|
||||
telnetServer:
|
||||
addr: 192.168.7.90:4100
|
||||
rest:
|
||||
enabled: true
|
||||
httpAddr: 192.168.7.90:8080
|
||||
emsAddr: 192.168.7.92:4999
|
||||
locRzIp: 192.168.7.90
|
||||
locRzPort: 4900
|
||||
ocsRzIp: 192.168.7.90
|
||||
ocsRzPort: 4951
|
||||
enableNotification: true
|
||||
canalServer:
|
||||
enabled: true
|
||||
addr: 192.168.1.211:3306
|
||||
username: canal
|
||||
password: canal
|
||||
reinit: false
|
||||
flushBeforeInit: true
|
||||
standalone: true
|
||||
cronCfg:
|
||||
enabled: true
|
||||
clrExp: '0 20 5 1,11,21 * ?'
|
||||
ntfSms: '*/3 * * * * ?'
|
||||
provision:
|
||||
enabled: true
|
||||
ssEntryIdInCrm: 1049
|
||||
141
proxy_go/config/config.go
Normal file
141
proxy_go/config/config.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"gopkg.in/yaml.v2"
|
||||
"io/ioutil"
|
||||
//"os"
|
||||
//"io/ioutil"
|
||||
|
||||
//"encoding/json"
|
||||
l4g "proxy/logger"//"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type LogConf struct {
|
||||
//Output string `yaml:"output"`// file; console
|
||||
Level string `yaml:"level"`// debug; info; warn; error
|
||||
//Path string `yaml:"path"`
|
||||
//MaxAge int `yaml:"maxAge"`// per Hour
|
||||
//RotationTime int `yaml:"rotationTime"`// per Hour
|
||||
}
|
||||
|
||||
type MysqlDb struct {
|
||||
Addr string `yaml:"addr"`
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
}
|
||||
|
||||
type RedisDb struct {
|
||||
NetType string `yaml:"netType"`
|
||||
Addr string `yaml:"addr"`
|
||||
Password string `yaml:"password,omitempty"`
|
||||
SentinelAddrs []string `yaml:"sentinelAddrs,omitempty"`
|
||||
}
|
||||
|
||||
type TelnetServer struct {
|
||||
Addr string `yaml:"addr"`
|
||||
}
|
||||
|
||||
type RestConf struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
HttpAddr string `yaml:"httpAddr"`
|
||||
//EmsAddr string `yaml:"emsAddr,omitempty"`
|
||||
LocRzIp string `yaml:"locRzIp"`
|
||||
LocRzPort int `yaml:"locRzPort"`
|
||||
OcsRzIp string `yaml:"ocsRzIp"`
|
||||
OcsRzPort int `yaml:"ocsRzPort"`
|
||||
//EnableNotification bool `yaml:"enableNotification"`
|
||||
}
|
||||
|
||||
type CanalServer struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
|
||||
Addr string `yaml:"addr"`
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
|
||||
Reinit bool `yaml:"reinit"`
|
||||
FlushBeforeInit bool `yaml:"flushBeforeInit"`
|
||||
//Standalone bool `yaml:"standalone"`
|
||||
}
|
||||
|
||||
type CronCfg struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
ClrExp string `yaml:"clrExp,omitempty"`
|
||||
NtfSms string `yaml:"ntfSms,omitempty"`
|
||||
}
|
||||
|
||||
type ProvisionCfg struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Interval int `yaml:"interval"`
|
||||
ReadTimout int `yaml:"readTimout"`
|
||||
|
||||
EmsIp string `yaml:"emsIp"`
|
||||
EmsPort int `yaml:"emsPort"`
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
ConnectHss string `yaml:"connectHss,omitempty"`
|
||||
ConnectAuc string `yaml:"connectAuc,omitempty"`
|
||||
ConnectVms string `yaml:"connectVms,omitempty"`
|
||||
DisconnectHss string `yaml:"disconnectHss,omitempty"`
|
||||
DisconnectAuc string `yaml:"disconnectAuc,omitempty"`
|
||||
DisconnectVms string `yaml:"disconnectVms,omitempty"`
|
||||
|
||||
SsEntryIdInCrm int `yaml:"ssEntryIdInCrm,omitempty"`
|
||||
}
|
||||
|
||||
type BundleUsageNotify struct {
|
||||
//Enabled bool `yaml:"enabled"`
|
||||
Voice75Percent bool `yaml:"voice75Percent"`
|
||||
Data75Percent bool `yaml:"data75Percent"`
|
||||
Sms75Percent bool `yaml:"sms75Percent"`
|
||||
}
|
||||
|
||||
type DbConfig struct {
|
||||
Log *LogConf `yaml:"log,omitempty"`
|
||||
MysqlDb MysqlDb `yaml:"mysqlDb"`
|
||||
RedisDb RedisDb `yaml:"redisDb"`
|
||||
TelnetServer TelnetServer `yaml:"telnetServer"`
|
||||
Rest RestConf `json:"rest"`
|
||||
CanalServer CanalServer `yaml:"canalServer"`
|
||||
CronCfg CronCfg `yaml:"cronCfg"`
|
||||
Provision ProvisionCfg `yaml:"provision"`
|
||||
BundleUsageNotify BundleUsageNotify `yaml:"bundleUsageNotify"`
|
||||
}
|
||||
|
||||
var Config = DbConfig{ /*mysql: "192.168.1.151:3306", username: "root", password: "rootaa"*/}
|
||||
|
||||
func ReadConfig() error {
|
||||
if content, err := ioutil.ReadFile("/usr/local/etc/restproxy/config.yaml"); err != nil {
|
||||
return err
|
||||
} else {
|
||||
if yamlErr := yaml.Unmarshal(content, &Config); yamlErr != nil {
|
||||
return yamlErr
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func SavePcfCfg() {
|
||||
if content, err := yaml.Marshal(&Config); err != nil {
|
||||
l4g.CfgLog.Errorf("Marshal failed error %s", err.Error())
|
||||
return
|
||||
} else if fileErr := ioutil.WriteFile("/usr/local/etc/restproxy/config.yaml", content, 0644); fileErr != nil {
|
||||
l4g.CfgLog.Errorf("WriteFile failed error %s", fileErr.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func SetBundleUsageNtf(serviceType int, bEnable bool) {
|
||||
switch serviceType {
|
||||
case 1:
|
||||
Config.BundleUsageNotify.Voice75Percent = bEnable
|
||||
case 2:
|
||||
Config.BundleUsageNotify.Data75Percent = bEnable
|
||||
case 3:
|
||||
Config.BundleUsageNotify.Sms75Percent = bEnable
|
||||
default:
|
||||
}
|
||||
SavePcfCfg()
|
||||
}
|
||||
|
||||
46
proxy_go/cron/cron.go
Normal file
46
proxy_go/cron/cron.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
rds "proxy/Nredis"
|
||||
rest "proxy/Nrestful"
|
||||
|
||||
"github.com/robfig/cron"
|
||||
l4g "proxy/logger"
|
||||
)
|
||||
|
||||
|
||||
func cronClrExpRes() {// 3.1(+[29]/30/31); 5.1/7.1/10.1/12.1(+31);
|
||||
rds.ClrExpRes()
|
||||
}
|
||||
|
||||
func cronNtfSms() {
|
||||
rest.RdbScanAlertSmsRecord()
|
||||
}
|
||||
|
||||
var c *cron.Cron = nil
|
||||
func CronStart(clrExp, ntfSms string) {
|
||||
c = cron.New()
|
||||
|
||||
// add a func to run in cron time
|
||||
//err := c.AddFunc("0 59 * * * ?", cron_rentThisMonth)
|
||||
var err error
|
||||
if clrExp != "" {
|
||||
err = c.AddFunc(clrExp, cronClrExpRes)
|
||||
if err != nil {
|
||||
l4g.AppLog.Error("Add cron clear expire rr and acct error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if ntfSms != "" {
|
||||
err = c.AddFunc(ntfSms, cronNtfSms)
|
||||
if err != nil {
|
||||
l4g.AppLog.Error("Add cron send notify sms error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
c.Start()
|
||||
}
|
||||
|
||||
func CronStop() {
|
||||
c.Stop()
|
||||
}
|
||||
248
proxy_go/go.sum
Normal file
248
proxy_go/go.sum
Normal file
@@ -0,0 +1,248 @@
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/antonfisher/nested-logrus-formatter v1.3.0 h1:8zixYquU1Odk+vzAaAQPAdRh1ZjmUXNQ1T+dUBvlhVo=
|
||||
github.com/antonfisher/nested-logrus-formatter v1.3.0/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cznic/golex v0.0.0-20181122101858-9c343928389c/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc=
|
||||
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
|
||||
github.com/cznic/parser v0.0.0-20160622100904-31edd927e5b1/go.mod h1:2B43mz36vGZNZEwkWi8ayRSSUXLfjL8OkbzwW4NcPMM=
|
||||
github.com/cznic/sortutil v0.0.0-20181122101858-f5f958428db8/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ=
|
||||
github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc=
|
||||
github.com/cznic/y v0.0.0-20170802143616-045f81c6662a/go.mod h1:1rk5VM7oSnA4vjp+hrLQ3HWHa+Y4yPCa3/CsJrcNnvs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/go-mysql-org/go-mysql v1.6.0 h1:19B5fojzZcri/1wj9G/1+ws8RJ3N6rJs2X5c/+kBLuQ=
|
||||
github.com/go-mysql-org/go-mysql v1.6.0/go.mod h1:GX0clmylJLdZEYAojPCDTCvwZxbTBrke93dV55715u0=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-redis/redis/v8 v8.11.0 h1:O1Td0mQ8UFChQ3N9zFQqo6kTU2cJ+/it88gDB+zg0wo=
|
||||
github.com/go-redis/redis/v8 v8.11.0/go.mod h1:DLomh7y2e3ggQXQLd1YgmvIfecPJoFl7WU5SOQ/r06M=
|
||||
github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ircop/tclient v0.0.0-20180908074649-f30684f28b0f h1:p7j1sjVHXwjJpr8fYjzInUGTmK9rYXgcQPiXjpawl/c=
|
||||
github.com/ircop/tclient v0.0.0-20180908074649-f30684f28b0f/go.mod h1:d111ZaRuwWWPIw3+gL48/SD8Pj9P6qBS5scMGWFt6/I=
|
||||
github.com/jmoiron/sqlx v1.3.3 h1:j82X0bf7oQ27XeqxicSZsTU5suPwKElg3oyxNn43iTk=
|
||||
github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.15.0 h1:1V1NfVQR87RtWAgp1lv9JZJ5Jap+XFGKPi00andXGi4=
|
||||
github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ=
|
||||
github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
|
||||
github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8 h1:USx2/E1bX46VG32FIw034Au6seQ2fY9NEILmNh/UlQg=
|
||||
github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8/go.mod h1:B1+S9LNcuMyLH/4HMTViQOJevkGiik3wW2AN9zb2fNQ=
|
||||
github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||
github.com/pingcap/errors v0.11.5-0.20201029093017-5a7df2af2ac7/go.mod h1:G7x87le1poQzLB/TqvTJI2ILrSgobnq4Ut7luOwvfvI=
|
||||
github.com/pingcap/errors v0.11.5-0.20201126102027-b0a155152ca3 h1:LllgC9eGfqzkfubMgjKIDyZYaa609nNWAyNZtpy2B3M=
|
||||
github.com/pingcap/errors v0.11.5-0.20201126102027-b0a155152ca3/go.mod h1:G7x87le1poQzLB/TqvTJI2ILrSgobnq4Ut7luOwvfvI=
|
||||
github.com/pingcap/log v0.0.0-20200511115504-543df19646ad/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8=
|
||||
github.com/pingcap/log v0.0.0-20210317133921-96f4fcab92a4 h1:ERrF0fTuIOnwfGbt71Ji3DKbOEaP189tjym50u8gpC8=
|
||||
github.com/pingcap/log v0.0.0-20210317133921-96f4fcab92a4/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8=
|
||||
github.com/pingcap/parser v0.0.0-20210415081931-48e7f467fd74 h1:FkVEC3Fck3fD16hMObMl/IWs72jR9FmqPn0Bdf728Sk=
|
||||
github.com/pingcap/parser v0.0.0-20210415081931-48e7f467fd74/go.mod h1:xZC8I7bug4GJ5KtHhgAikjTfU4kBv1Sbo3Pf1MZ6lVw=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/reiver/go-oi v1.0.0 h1:nvECWD7LF+vOs8leNGV/ww+F2iZKf3EYjYZ527turzM=
|
||||
github.com/reiver/go-oi v1.0.0/go.mod h1:RrDBct90BAhoDTxB1fenZwfykqeGvhI6LsNfStJoEkI=
|
||||
github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e h1:quuzZLi72kkJjl+f5AQ93FMcadG19WkS7MO6TXFOSas=
|
||||
github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e/go.mod h1:+5vNVvEWwEIx86DB9Ke/+a5wBI464eDRo3eF0LcfpWg=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
|
||||
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec h1:6ncX5ko6B9LntYM0YBRXkiSaZMmLYeZ/NWcmeB43mMY=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 h1:xT+JlYxNGqyT+XcU8iUrN18JYed2TvG9yN5ULG2jATM=
|
||||
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
|
||||
github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07/go.mod h1:yFdBgwXP24JziuRl2NMUahT7nGLNOKi1SIiFxMttVD4=
|
||||
github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed h1:KMgQoLJGCq1IoZpLZE3AIffh9veYWoVlsvA4ib55TMM=
|
||||
github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed/go.mod h1:yFdBgwXP24JziuRl2NMUahT7nGLNOKi1SIiFxMttVD4=
|
||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go v1.2.1 h1:dz+JxTe7GZQdErTo7SREc1jQj/hFP1k7jyIAwODoW+k=
|
||||
github.com/ugorji/go v1.2.1/go.mod h1:cSVypSfTLm2o9fKxXvQgn3rMmkPXovcWor6Qn5tbFmI=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/ugorji/go/codec v1.2.1 h1:/TRfW3XKkvWvmAYyCUaQlhoCDGjcvNR8xVVA/l5p/jQ=
|
||||
github.com/ugorji/go/codec v1.2.1/go.mod h1:s/WxCRi46t8rA+fowL40EnmD7ec0XhR7ZypxeBNdzsM=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/withlin/canal-go v1.1.1 h1:eEcX/184K9tIny6krZi2NGXN111cyINzTPRNTfr83Fg=
|
||||
github.com/withlin/canal-go v1.1.1/go.mod h1:dIyy0yorJ7CfPnVh8sYqkBItyqTQNTxPftE3fBJTkmY=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
|
||||
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
|
||||
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 h1:sYNJzB4J8toYPQTM6pAkcmBRgw9SnQKP9oXCHfgy604=
|
||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
75
proxy_go/logger/logger.go
Normal file
75
proxy_go/logger/logger.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
formatter "github.com/antonfisher/nested-logrus-formatter"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"proxy/util/logger_conf"
|
||||
"proxy/util/logger_util"
|
||||
)
|
||||
|
||||
var (
|
||||
log *logrus.Logger
|
||||
AppLog *logrus.Entry
|
||||
InitLog *logrus.Entry
|
||||
CfgLog *logrus.Entry
|
||||
MysqlLog *logrus.Entry
|
||||
/*Bdtpolicylog *logrus.Entry
|
||||
PolicyAuthorizationlog *logrus.Entry
|
||||
AMpolicylog *logrus.Entry
|
||||
SMpolicylog *logrus.Entry
|
||||
Consumerlog *logrus.Entry
|
||||
UtilLog *logrus.Entry
|
||||
CallbackLog *logrus.Entry
|
||||
OamLog *logrus.Entry
|
||||
CtxLog *logrus.Entry
|
||||
ConsumerLog *logrus.Entry
|
||||
GinLog *logrus.Entry
|
||||
NotifyEventLog *logrus.Entry*/
|
||||
ProvLog *logrus.Entry
|
||||
RedisLog *logrus.Entry
|
||||
RestLog *logrus.Entry
|
||||
CanalLog *logrus.Entry
|
||||
)
|
||||
|
||||
const (
|
||||
FieldRemoteAddr string = "remote_addr"
|
||||
)
|
||||
|
||||
func init() {
|
||||
log = logrus.New()
|
||||
log.SetReportCaller(true)
|
||||
|
||||
log.Formatter = &formatter.Formatter{
|
||||
TimestampFormat: time.RFC3339,
|
||||
TrimMessages: true,
|
||||
NoFieldsSpace: true,
|
||||
HideKeys: true,
|
||||
FieldsOrder: []string{"component", "category", FieldRemoteAddr},
|
||||
}
|
||||
|
||||
free5gcLogHook, err := logger_util.NewFileHook(logger_conf.Free5gcLogFile, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0o666)
|
||||
if err == nil {
|
||||
log.Hooks.Add(free5gcLogHook)
|
||||
}
|
||||
|
||||
AppLog = log.WithFields(logrus.Fields{"component": "proxy", "category": "App"})
|
||||
InitLog = log.WithFields(logrus.Fields{"component": "proxy", "category": "Init"})
|
||||
CfgLog = log.WithFields(logrus.Fields{"component": "proxy", "category": "Cfg"})
|
||||
MysqlLog = log.WithFields(logrus.Fields{"component": "proxy", "category": "Mysql"})
|
||||
RedisLog = log.WithFields(logrus.Fields{"component": "proxy", "category": "Redis"})
|
||||
RestLog = log.WithFields(logrus.Fields{"component": "proxy", "category": "Rest"})
|
||||
CanalLog = log.WithFields(logrus.Fields{"component": "proxy", "category": "Canal"})
|
||||
ProvLog = log.WithFields(logrus.Fields{"component": "proxy", "category": "Prov"})
|
||||
}
|
||||
|
||||
func SetLogLevel(level logrus.Level) {
|
||||
log.SetLevel(level)
|
||||
}
|
||||
|
||||
func SetReportCaller(set bool) {
|
||||
log.SetReportCaller(set)
|
||||
}
|
||||
147
proxy_go/main.go
Normal file
147
proxy_go/main.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"proxy/canal"
|
||||
config2 "proxy/config"
|
||||
"proxy/cron"
|
||||
"proxy/provision"
|
||||
_ "time"
|
||||
|
||||
l4g "github.com/sirupsen/logrus"
|
||||
mysql "proxy/Nmysql"
|
||||
rdb "proxy/Nredis"
|
||||
rest "proxy/Nrestful"
|
||||
tn "proxy/TelnetShellServer"
|
||||
"proxy/logger"
|
||||
)
|
||||
/*
|
||||
func initLog(cfg *rest.LogConf) {
|
||||
if strings.Compare("file", cfg.Output) != 0 {
|
||||
l4g.SetOutput(os.Stdout)
|
||||
} else {
|
||||
path := cfg.Path// "/var/log/pcf.log"
|
||||
// log rotate
|
||||
//`WithLinkName` 为最新的日志建立软连接
|
||||
//`WithRotationTime` 设置日志分割的时间,隔多久分割一次
|
||||
//WithMaxAge 和 WithRotationCount二者只能设置一个
|
||||
// `WithMaxAge` 设置文件清理前的最长保存时间
|
||||
// `WithRotationCount` 设置文件清理前最多保存的个数
|
||||
//
|
||||
// add a new log every 3 hours, with 30 days at most.
|
||||
writer, _ := rotatelogs.New(
|
||||
path+".%Y-%m-%d-%H:%M",
|
||||
rotatelogs.WithLinkName(path),
|
||||
//rotatelogs.WithMaxAge(time.Duration(cfg.MaxAge)*time.Hour),
|
||||
//rotatelogs.WithRotationTime(time.Duration(cfg.RotationTime)*time.Hour),
|
||||
rotatelogs.WithRotationTime(3 * time.Hour),
|
||||
rotatelogs.WithRotationCount(uint(cfg.MaxAge)),
|
||||
)
|
||||
l4g.SetOutput(writer)
|
||||
}
|
||||
|
||||
if strings.Compare("debug", cfg.Level) == 0 {
|
||||
l4g.SetLevel(l4g.DebugLevel)
|
||||
} else if strings.Compare("info", cfg.Level) == 0 {
|
||||
l4g.SetLevel(l4g.InfoLevel)
|
||||
} else if strings.Compare("warn", cfg.Level) == 0 {
|
||||
l4g.SetLevel(l4g.WarnLevel)
|
||||
} else if strings.Compare("error", cfg.Level) == 0 {
|
||||
l4g.SetLevel(l4g.ErrorLevel)
|
||||
}
|
||||
//l4g.SetReportCaller(true)// useless
|
||||
//l4g.SetFormatter(&l4g.TextFormatter{})
|
||||
}*/
|
||||
|
||||
func check(err error, message string) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
initLog.Debugf("%s", message)
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
var initLog *l4g.Entry
|
||||
func init() {
|
||||
initLog = logger.InitLog
|
||||
}
|
||||
|
||||
func setLogLevel(level string) {
|
||||
if level != "" {
|
||||
level, err := l4g.ParseLevel(level)
|
||||
if err != nil {
|
||||
initLog.Warnf("Log level [%s] is invalid, set to [info] level",
|
||||
level)
|
||||
logger.SetLogLevel(l4g.InfoLevel)
|
||||
} else {
|
||||
initLog.Infof("Log level is set to [%s] level", level)
|
||||
logger.SetLogLevel(level)
|
||||
}
|
||||
} else {
|
||||
initLog.Infoln("Log level not set. Default set to [info] level")
|
||||
logger.SetLogLevel(l4g.InfoLevel)
|
||||
}
|
||||
|
||||
logger.SetReportCaller(true)
|
||||
}
|
||||
// *********************************************************************************
|
||||
|
||||
//var cfgFile = flag.String("c", "", "selfcare proxy configure file.")
|
||||
func main() {
|
||||
// check if billing already running
|
||||
/*var output, errout string
|
||||
err, output, errout := rest.Shellout("pidof billing")
|
||||
if err != nil {
|
||||
fmt.Printf("Get pid of billing fail, err: %v\n", err)
|
||||
} else {
|
||||
ss := strings.Fields(output)
|
||||
if len(ss) >= 2 {
|
||||
fmt.Printf("Process billing is already running, pids[%s], exit!\n", output)
|
||||
os.Exit(0)
|
||||
} else {
|
||||
fmt.Printf("First process of billing, stdout[%s], stderr[%s]!\n", output, errout)
|
||||
}
|
||||
}*/
|
||||
|
||||
//flag.Parse()
|
||||
|
||||
// read config
|
||||
config := &config2.Config
|
||||
err := config2.ReadConfig()
|
||||
if err != nil {
|
||||
initLog.Errorf("open config file error: %s", err)
|
||||
return
|
||||
} else {
|
||||
initLog.Infof("config file: %v", config)
|
||||
}
|
||||
|
||||
//initLog(&config.Log)
|
||||
setLogLevel(config.Log.Level)
|
||||
|
||||
initLog.Infof("selfcare proxy start...\r\nconfig:[%v]", config)
|
||||
|
||||
if config.Rest.Enabled {
|
||||
go rest.Connect_ocs(config.Rest.LocRzIp, config.Rest.OcsRzIp, config.Rest.LocRzPort, config.Rest.OcsRzPort)
|
||||
go rest.StartHttpServer(config.Rest.HttpAddr)
|
||||
}
|
||||
|
||||
go tn.Server(config.TelnetServer.Addr)
|
||||
|
||||
// temp disable======================
|
||||
go rdb.NewRedisClient(config.RedisDb.NetType, config.RedisDb.Addr, config.RedisDb.Password, config.RedisDb.SentinelAddrs)
|
||||
|
||||
go mysql.OpenMysql(config.MysqlDb.Username, config.MysqlDb.Password, config.MysqlDb.Addr)
|
||||
if config.CanalServer.Enabled {
|
||||
go canal.CanalFsm(config.CanalServer.Addr, config.CanalServer.Username, config.CanalServer.Password)
|
||||
}
|
||||
//
|
||||
|
||||
if config.CronCfg.Enabled && (config.CronCfg.ClrExp != "" || config.CronCfg.NtfSms != "") {
|
||||
cron.CronStart(config.CronCfg.ClrExp, config.CronCfg.NtfSms)
|
||||
defer cron.CronStop()
|
||||
}
|
||||
|
||||
go provision.ProvisionFsm()
|
||||
|
||||
select {}
|
||||
}
|
||||
242
proxy_go/provision/provision.go
Normal file
242
proxy_go/provision/provision.go
Normal file
@@ -0,0 +1,242 @@
|
||||
package provision
|
||||
|
||||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ircop/tclient"
|
||||
"proxy/MsgDef"
|
||||
"proxy/Nmysql"
|
||||
rds "proxy/Nredis"
|
||||
l4g "proxy/logger"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
. "proxy/config"
|
||||
)
|
||||
|
||||
func ProvisionFsm() {
|
||||
prov := &Config.Provision
|
||||
if !prov.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
interval := prov.Interval
|
||||
if interval <= 0 {
|
||||
interval = 8
|
||||
}
|
||||
ticker := time.NewTicker(time.Second * time.Duration(interval))
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
<-ticker.C // wait for next tick
|
||||
|
||||
CheckNewProvision()
|
||||
ProvisionBySmcli()
|
||||
}
|
||||
}
|
||||
|
||||
func TelnetClient() {
|
||||
client := tclient.New(5, "manager", "manager", "")
|
||||
err := client.Open("192.168.13.117", 4999)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
// you can omit this, or do auth stuff manually by calling `ReadUntil` with login/password prompts
|
||||
/*out, err := client.Login("manager", "manager")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf(out)*/
|
||||
|
||||
out, err := client.Cmd("connect hlr -srvip 192.168.13.118 -passwd 123456")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf(out)
|
||||
|
||||
out, err = client.Cmd("connect auc -srvip 192.168.13.118 -passwd 123456")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf(out)
|
||||
|
||||
out, err = client.Cmd("connect vms -srvip 192.168.4.221 -passwd 123456")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf(out)
|
||||
|
||||
// smcli cmd
|
||||
|
||||
out, err = client.Cmd("disconnect hlr")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf(out)
|
||||
|
||||
out, err = client.Cmd("disconnect vms")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf(out)
|
||||
|
||||
//out, err = client.ReadUntil()
|
||||
}
|
||||
|
||||
func CheckNewProvision() {
|
||||
si, _ := Nmysql.QuerySyncMobileInfo()
|
||||
if si != nil {
|
||||
for _, v := range si {
|
||||
if v.OperType == 2 {// replace imsi
|
||||
oi, _ := Nmysql.QueryOldImsiInfo(v.ServiceNbr)
|
||||
if oi != nil {
|
||||
Nmysql.AddSmcliCmd2Mysql(Nmysql.CT_DISCONNECT_ACCOUNT, Nmysql.NT_HSS, strconv.Itoa(oi.PrdInstId), "delete subscriber -imsi " + v.Imsi)
|
||||
Nmysql.AddSmcliCmd2Mysql(Nmysql.CT_DISCONNECT_ACCOUNT, Nmysql.NT_AUC, strconv.Itoa(oi.PrdInstId),"delete aucSubscriber -imsi " + v.Imsi)
|
||||
Nmysql.UpdateSyncMobileState(oi.PreId, 6)
|
||||
|
||||
crtCmd := fmt.Sprintf("create subscriber -imsi %s -msisdn %s -eps_user_tpl def_eps -impi %s@ims.mnc010.mcc505.3gppnetwork.org",
|
||||
v.Imsi, v.ServiceNbr, v.Imsi)
|
||||
Nmysql.AddSmcliCmd2Mysql(Nmysql.CT_CREATE_SUB, Nmysql.NT_HSS, strconv.Itoa(v.PrdInstId), crtCmd)
|
||||
|
||||
if v.Opc == "" {
|
||||
crtCmd = fmt.Sprintf("create aucSubscriber -imsi %s -ki %s",
|
||||
v.Imsi, v.Ki)
|
||||
} else {
|
||||
crtCmd = fmt.Sprintf("create aucSubscriber -imsi %s -ki %s -opc %s",
|
||||
v.Imsi, v.Ki, v.Opc)
|
||||
}
|
||||
Nmysql.AddSmcliCmd2Mysql(Nmysql.CT_CREATE_SUB, Nmysql.NT_AUC, strconv.Itoa(v.PrdInstId), crtCmd)
|
||||
Nmysql.UpdateSyncMobileState(v.PreId, 2)
|
||||
}
|
||||
} else {
|
||||
crtCmd := fmt.Sprintf("create subscriber -imsi %s -msisdn %s -eps_user_tpl def_eps -impi %s@ims.mnc010.mcc505.3gppnetwork.org",
|
||||
v.Imsi, v.ServiceNbr, v.Imsi)
|
||||
Nmysql.AddSmcliCmd2Mysql(Nmysql.CT_CREATE_SUB, Nmysql.NT_HSS, strconv.Itoa(v.PrdInstId), crtCmd)
|
||||
|
||||
if v.Opc == "" {
|
||||
crtCmd = fmt.Sprintf("create aucSubscriber -imsi %s -ki %s",
|
||||
v.Imsi, v.Ki)
|
||||
} else {
|
||||
crtCmd = fmt.Sprintf("create aucSubscriber -imsi %s -ki %s -opc %s",
|
||||
v.Imsi, v.Ki, v.Opc)
|
||||
}
|
||||
Nmysql.AddSmcliCmd2Mysql(Nmysql.CT_CREATE_SUB, Nmysql.NT_AUC, strconv.Itoa(v.PrdInstId), crtCmd)
|
||||
|
||||
if v.VmsFlag > 0 {
|
||||
crtCmd := fmt.Sprintf("create vmsSubscriber -msisdn %s",
|
||||
v.ServiceNbr)
|
||||
Nmysql.AddSmcliCmd2Mysql(Nmysql.CT_CREATE_SUB, Nmysql.NT_VMS, strconv.Itoa(v.PrdInstId), crtCmd)
|
||||
}
|
||||
|
||||
// add ocs success
|
||||
crtCmd = fmt.Sprintf("create ppsSubscriber -msisdn %s -PRE_ID %d",
|
||||
v.ServiceNbr, v.PreId)
|
||||
Nmysql.AddSmcliCmdSucc2Mysql(Nmysql.CT_CREATE_SUB, Nmysql.NT_OCS, strconv.Itoa(v.PrdInstId), crtCmd)
|
||||
var synMobile = MsgDef.ChgSyncMobile{PreId: v.PreId, OperType: v.OperType, State: 1, ServiceNbr: v.ServiceNbr, CustId: v.CustId, AcctId: v.AcctId,
|
||||
PrdInstId: v.PrdInstId, MobileType: v.MobileType, BirthDate: v.BirthDate, Balance: v.Balance, BalanceExpDate: v.BalanceExpDate, OfrId: v.OfrId,
|
||||
Imsi: v.Imsi, Ki: v.Ki, Opc: v.Opc, VmsFlag: v.VmsFlag, ExpDate: v.ExpDate, CugId: v.CugId}
|
||||
rds.RdbSetCreateAcctRecord(&synMobile)
|
||||
|
||||
Nmysql.UpdateSyncMobileState(v.PreId, 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ProvisionBySmcli() {
|
||||
si, _ := Nmysql.QuerySyncCnCmd()
|
||||
if si != nil {
|
||||
prv := &Config.Provision
|
||||
SmcliClient := tclient.New(prv.ReadTimout, prv.Username, prv.Password, "")
|
||||
err := SmcliClient.Open(prv.EmsIp, prv.EmsPort)
|
||||
if err != nil {
|
||||
l4g.ProvLog.Errorf("Smcli Client Open fail: %v", err)
|
||||
return
|
||||
}
|
||||
defer SmcliClient.Close()
|
||||
|
||||
var out string
|
||||
if prv.ConnectHss != "" {
|
||||
out, err = SmcliClient.Cmd(prv.ConnectHss)
|
||||
if err != nil {
|
||||
l4g.ProvLog.Errorf("Smcli Client Cmd[%s] err: %v", prv.ConnectHss, err)
|
||||
return
|
||||
}
|
||||
l4g.ProvLog.Infof("Smcli Client Cmd[%s] output: %s", prv.ConnectHss, out)
|
||||
if !strings.Contains(out, "success") {// 0000:Command successful
|
||||
l4g.ProvLog.Warnf("Smcli Cmd[%s] not success", prv.ConnectHss)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if prv.ConnectAuc != "" {
|
||||
out, err = SmcliClient.Cmd(prv.ConnectAuc)
|
||||
if err != nil {
|
||||
l4g.ProvLog.Errorf("Smcli Client Cmd[%s] err: %v", prv.ConnectAuc, err)
|
||||
goto ReleaseConnnet
|
||||
}
|
||||
l4g.ProvLog.Infof("Smcli Client Cmd[%s] output: %s", prv.ConnectAuc, out)
|
||||
if !strings.Contains(out, "success") {// 0000:Command successful
|
||||
l4g.ProvLog.Warnf("Smcli Cmd[%s] not success", prv.ConnectAuc)
|
||||
goto ReleaseConnnet
|
||||
}
|
||||
}
|
||||
|
||||
if prv.ConnectVms != "" {
|
||||
out, err = SmcliClient.Cmd(prv.ConnectVms)
|
||||
if err != nil {
|
||||
l4g.ProvLog.Errorf("Smcli Client Cmd[%s] err: %v", prv.ConnectVms, err)
|
||||
goto ReleaseConnHss
|
||||
}
|
||||
l4g.ProvLog.Infof("Smcli Client Cmd[%s] output: %s", prv.ConnectVms, out)
|
||||
if !strings.Contains(out, "success") {// 0000:Command successful
|
||||
l4g.ProvLog.Warnf("Smcli Cmd[%s] not success", prv.ConnectVms)
|
||||
goto ReleaseConnHss
|
||||
}
|
||||
}
|
||||
|
||||
// smcli cmd
|
||||
for _, v := range si {
|
||||
if v.Command != "" {
|
||||
out, err = SmcliClient.Cmd(v.Command)
|
||||
if err != nil {
|
||||
l4g.ProvLog.Errorf("Smcli Client Cmd[%s] err: %v", v.Command, err)
|
||||
Nmysql.UpdateCnCmdResult(v.Id, 2, 2, "timeout")
|
||||
goto ReleaseConnnet
|
||||
}
|
||||
l4g.ProvLog.Infof("Smcli Client Cmd[%s] output: %s", v.Command, out)
|
||||
if !strings.Contains(out, "success") {// 0000:Command successful
|
||||
l4g.ProvLog.Warnf("Smcli Cmd[%s] not success", v.Command)
|
||||
Nmysql.UpdateCnCmdResult(v.Id, 2, 2, out)
|
||||
goto ReleaseConnnet
|
||||
} else {
|
||||
l4g.ProvLog.Infof("Smcli Cmd[%s] success", v.Command)
|
||||
Nmysql.UpdateCnCmdResult(v.Id, 2, 1, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ReleaseConnnet:
|
||||
if prv.DisconnectVms != "" {
|
||||
out, err = SmcliClient.Cmd(prv.DisconnectVms)
|
||||
if err != nil {
|
||||
l4g.ProvLog.Errorf("Smcli Client Cmd[%s] fail: %v", prv.DisconnectVms, err)
|
||||
return
|
||||
}
|
||||
l4g.ProvLog.Infof("Smcli Client Cmd[%s] output: %s", prv.DisconnectVms, out)
|
||||
}
|
||||
|
||||
ReleaseConnHss:
|
||||
if prv.DisconnectHss != "" {
|
||||
out, err = SmcliClient.Cmd(prv.DisconnectHss)
|
||||
if err != nil {
|
||||
l4g.ProvLog.Errorf("Smcli Client Cmd[%s] fail: %v", prv.DisconnectHss, err)
|
||||
return
|
||||
}
|
||||
l4g.ProvLog.Infof("Smcli Client Cmd[%s] output: %s", prv.DisconnectHss, out)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
19
proxy_go/public/go-oi@v1.0.0/LICENSE
Normal file
19
proxy_go/public/go-oi@v1.0.0/LICENSE
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2016 Charles Iliya Krempeaux <charles@reptile.ca> :: http://changelog.ca/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
64
proxy_go/public/go-oi@v1.0.0/README.md
Normal file
64
proxy_go/public/go-oi@v1.0.0/README.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# go-oi
|
||||
|
||||
Package **oi** provides useful tools to be used with the Go programming language's standard "io" package.
|
||||
|
||||
For example, did you know that when you call the `Write` method on something that fits the `io.Writer`
|
||||
interface, that it is possible that not everything was be written?!
|
||||
|
||||
I.e., that a _**short write**_ happened.
|
||||
|
||||
That just doing the following is (in general) **not** enough:
|
||||
```
|
||||
n, err := writer.Write(p)
|
||||
```
|
||||
|
||||
That, for example, you should be checking if `err == io.ErrShortWrite`, and then maybe calling the `Write`
|
||||
method again but only with what didn't get written.
|
||||
|
||||
For a simple example of this (that actually is **not** sufficient to solve this problem, but illustrates
|
||||
the direction you would need to go to solve this problem is):
|
||||
```
|
||||
n, err := w.Write(p)
|
||||
|
||||
if io.ErrShortWrite == err {
|
||||
n2, err2 := w.Write(p[n:])
|
||||
}
|
||||
```
|
||||
|
||||
Note that the second call to the `Write` method passed `p[n:]` (instead of just `p`), to account for the `n` bytes
|
||||
already being written (with the first call to the `Write` method).
|
||||
|
||||
A more "production quality" version of this would likely be in a loop, but such that that the loop had "guards"
|
||||
against looping forever, and also possibly looping for "too long".
|
||||
|
||||
Well package **oi** provides tools that helps you deal with this and other problems. For example, you
|
||||
can handle a _**short write**_ with the following **oi** func:
|
||||
```
|
||||
n, err := oi.LongWrite(writer, p)
|
||||
```
|
||||
|
||||
|
||||
## Documention
|
||||
|
||||
Online documentation, which includes examples, can be found at: http://godoc.org/github.com/reiver/go-oi
|
||||
|
||||
[](https://godoc.org/github.com/reiver/go-oi)
|
||||
|
||||
|
||||
## Example
|
||||
```
|
||||
import (
|
||||
"github.com/reiver/go-oi"
|
||||
)
|
||||
|
||||
// ...
|
||||
|
||||
p := []byte("It is important that this message be written!!!")
|
||||
|
||||
n, err := oi.LongWrite(writer, p)
|
||||
if nil != err {
|
||||
//@TODO: Handle error.
|
||||
return
|
||||
}
|
||||
|
||||
```
|
||||
39
proxy_go/public/go-oi@v1.0.0/doc.go
Normal file
39
proxy_go/public/go-oi@v1.0.0/doc.go
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Package oi provides useful tools to be used with Go's standard "io" package.
|
||||
|
||||
For example, did you know that when you call the Write method on something that fits the io.Writer
|
||||
interface, that it is possible that not everything was be written?!
|
||||
|
||||
I.e., that a 'short write' happened.
|
||||
|
||||
That just doing the following is (in general) not enough:
|
||||
|
||||
n, err := writer.Write(p)
|
||||
|
||||
That, for example, you should be checking if "err == io.ErrShortWrite", and then maybe calling the Write
|
||||
method again but only with what didn't get written.
|
||||
|
||||
For a simple example of this (that actually is not sufficient to solve this problem, but illustrates
|
||||
the direction you would need to go to solve this problem is):
|
||||
|
||||
n, err := w.Write(p)
|
||||
|
||||
if io.ErrShortWrite == err {
|
||||
n2, err2 := w.Write(p[n:])
|
||||
}
|
||||
|
||||
Note that the second call to the Write method passed "p[n:]" (instead of just "p"), to account for the "n" bytes
|
||||
already being written (with the first call to the `Write` method).
|
||||
|
||||
A more "production quality" version of this would likely be in a loop, but such that that the loop had "guards"
|
||||
against looping forever, and also possibly looping for "too long".
|
||||
|
||||
|
||||
Well package oi provides tools that helps you deal with this and other problems. For example, you
|
||||
can handle a 'short write' with the following oi func:
|
||||
```
|
||||
n, err := oi.LongWrite(writer, p)
|
||||
```
|
||||
|
||||
*/
|
||||
package oi
|
||||
39
proxy_go/public/go-oi@v1.0.0/longwrite.go
Normal file
39
proxy_go/public/go-oi@v1.0.0/longwrite.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package oi
|
||||
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
|
||||
// LongWrite tries to write the bytes from 'p' to the writer 'w', such that it deals
|
||||
// with "short writes" where w.Write would return an error of io.ErrShortWrite and
|
||||
// n < len(p).
|
||||
//
|
||||
// Note that LongWrite still could return the error io.ErrShortWrite; but this
|
||||
// would only be after trying to handle the io.ErrShortWrite a number of times, and
|
||||
// then eventually giving up.
|
||||
func LongWrite(w io.Writer, p []byte) (int64, error) {
|
||||
|
||||
numWritten := int64(0)
|
||||
for {
|
||||
//@TODO: Should check to make sure this doesn't get stuck in an infinite loop writting nothing!
|
||||
n, err := w.Write(p)
|
||||
numWritten += int64(n)
|
||||
if nil != err && io.ErrShortWrite != err {
|
||||
return numWritten, err
|
||||
}
|
||||
|
||||
if !(n < len(p)) {
|
||||
break
|
||||
}
|
||||
|
||||
p = p[n:]
|
||||
|
||||
if len(p) < 1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return numWritten, nil
|
||||
}
|
||||
195
proxy_go/public/go-oi@v1.0.0/longwrite_test.go
Normal file
195
proxy_go/public/go-oi@v1.0.0/longwrite_test.go
Normal file
@@ -0,0 +1,195 @@
|
||||
package oi
|
||||
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-oi/test"
|
||||
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func TestLongWrite(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
String string
|
||||
}{
|
||||
{
|
||||
String: "",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
String: "apple",
|
||||
},
|
||||
{
|
||||
String: "banana",
|
||||
},
|
||||
{
|
||||
String: "cherry",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
String: "Hello world!",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
String: "😁😂😃😄😅😆😉😊😋😌😍😏😒😓😔😖😘😚😜😝😞😠😡😢😣😤😥😨😩😪😫😭😰😱😲😳😵😷",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
String: "0123456789",
|
||||
},
|
||||
{
|
||||
String: "٠١٢٣٤٥٦٧٨٩", // Arabic-Indic Digits
|
||||
},
|
||||
{
|
||||
String: "۰۱۲۳۴۵۶۷۸۹", // Extended Arabic-Indic Digits
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
String: "Ⅰ Ⅱ Ⅲ Ⅳ Ⅴ Ⅵ Ⅶ Ⅷ Ⅸ Ⅹ Ⅺ Ⅻ Ⅼ Ⅽ Ⅾ Ⅿ",
|
||||
},
|
||||
{
|
||||
String: "ⅰ ⅱ ⅲ ⅳ ⅴ ⅵ ⅶ ⅷ ⅸ ⅹ ⅺ ⅻ ⅼ ⅽ ⅾ ⅿ",
|
||||
},
|
||||
{
|
||||
String: "ↀ ↁ ↂ Ↄ ↄ ↅ ↆ ↇ ↈ",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
for testNumber, test := range tests {
|
||||
|
||||
p := []byte(test.String)
|
||||
|
||||
var writer oitest.ShortWriter
|
||||
n, err := LongWrite(&writer, p)
|
||||
if nil != err {
|
||||
t.Errorf("For test #%d, did not expect an error, but actually got one: (%T) %q; for %q.", testNumber, err, err.Error(), test.String)
|
||||
continue
|
||||
}
|
||||
if expected, actual := int64(len([]byte(test.String))), n; expected != actual {
|
||||
t.Errorf("For test #%d, expected %d, but actually got %d; for %q.", testNumber, expected, actual, test.String)
|
||||
continue
|
||||
}
|
||||
if expected, actual := test.String, writer.String(); expected != actual {
|
||||
t.Errorf("For test #%d, expected %q, but actually got %q", testNumber, expected, actual)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func TestLongWriteExpectError(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
String string
|
||||
Expected string
|
||||
Writes []int
|
||||
Err error
|
||||
}{
|
||||
{
|
||||
String: "apple",
|
||||
Expected: "appl",
|
||||
Writes: []int{2,2},
|
||||
Err: errors.New("Crabapple!"),
|
||||
},
|
||||
{
|
||||
String: "apple",
|
||||
Expected: "appl",
|
||||
Writes: []int{2,2,0},
|
||||
Err: errors.New("Crabapple!!"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
String: "banana",
|
||||
Expected: "banan",
|
||||
Writes: []int{2,3},
|
||||
Err: errors.New("bananananananana!"),
|
||||
},
|
||||
{
|
||||
String: "banana",
|
||||
Expected: "banan",
|
||||
Writes: []int{2,3,0},
|
||||
Err: errors.New("bananananananananananananana!!!"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
String: "cherry",
|
||||
Expected: "cher",
|
||||
Writes: []int{1,1,1,1},
|
||||
Err: errors.New("C.H.E.R.R.Y."),
|
||||
},
|
||||
{
|
||||
String: "cherry",
|
||||
Expected: "cher",
|
||||
Writes: []int{1,1,1,1,0},
|
||||
Err: errors.New("C_H_E_R_R_Y"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
String: "Hello world!",
|
||||
Expected: "Hello world",
|
||||
Writes: []int{1,2,3,5},
|
||||
Err: errors.New("Welcome!"),
|
||||
},
|
||||
{
|
||||
String: "Hello world!",
|
||||
Expected: "Hello world",
|
||||
Writes: []int{1,2,3,5,0},
|
||||
Err: errors.New("WeLcOmE!!!"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
String: " ",
|
||||
Expected: " ",
|
||||
Writes: []int{1,2,3,5,8,13},
|
||||
Err: errors.New("Space, the final frontier"),
|
||||
},
|
||||
{
|
||||
String: " ",
|
||||
Expected: " ",
|
||||
Writes: []int{1,2,3,5,8,13,0},
|
||||
Err: errors.New("Space, the final frontier"),
|
||||
},
|
||||
}
|
||||
|
||||
for testNumber, test := range tests {
|
||||
|
||||
p := []byte(test.String)
|
||||
|
||||
writer := oitest.NewWritesThenErrorWriter(test.Err, test.Writes...)
|
||||
n, err := LongWrite(writer, p)
|
||||
if nil == err {
|
||||
t.Errorf("For test #%d, expected to get an error, but actually did not get one: %v; for %q.", testNumber, err, test.String)
|
||||
continue
|
||||
}
|
||||
if expected, actual := test.Err, err; expected != actual {
|
||||
t.Errorf("For test #%d, expected to get error (%T) %q, but actually got (%T) %q; for %q.", testNumber, expected, expected.Error(), actual, actual.Error(), test.String)
|
||||
continue
|
||||
}
|
||||
if expected, actual := int64(len(test.Expected)), n; expected != actual {
|
||||
t.Errorf("For test #%d, expected number of bytes written to be %d = len(%q), but actually was %d = len(%q); for %q.", testNumber, expected, test.Expected, actual, writer.String(), test.String)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
28
proxy_go/public/go-oi@v1.0.0/longwritebyte.go
Normal file
28
proxy_go/public/go-oi@v1.0.0/longwritebyte.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package oi
|
||||
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
|
||||
// LongWriteByte trys to write the byte from 'b' to the writer 'w', such that it deals
|
||||
// with "short writes" where w.Write would return an error of io.ErrShortWrite and
|
||||
// n < 1.
|
||||
//
|
||||
// Note that LongWriteByte still could return the error io.ErrShortWrite; but this
|
||||
// would only be after trying to handle the io.ErrShortWrite a number of times, and
|
||||
// then eventually giving up.
|
||||
func LongWriteByte(w io.Writer, b byte) error {
|
||||
var buffer [1]byte
|
||||
p := buffer[:]
|
||||
|
||||
buffer[0] = b
|
||||
|
||||
numWritten, err := LongWrite(w, p)
|
||||
if 1 != numWritten {
|
||||
return io.ErrShortWrite
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
101
proxy_go/public/go-oi@v1.0.0/longwritebyte_test.go
Normal file
101
proxy_go/public/go-oi@v1.0.0/longwritebyte_test.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package oi
|
||||
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-oi/test"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func TestLongWriteByte(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
Byte byte
|
||||
}{}
|
||||
|
||||
for b := byte(' '); b <= byte('/'); b++ {
|
||||
test := struct {
|
||||
Byte byte
|
||||
}{
|
||||
Byte:b,
|
||||
}
|
||||
|
||||
tests = append(tests, test)
|
||||
}
|
||||
|
||||
for b := byte('0'); b <= byte('9'); b++ {
|
||||
test := struct {
|
||||
Byte byte
|
||||
}{
|
||||
Byte:b,
|
||||
}
|
||||
|
||||
tests = append(tests, test)
|
||||
}
|
||||
|
||||
for b := byte(':'); b <= byte('@'); b++ {
|
||||
test := struct {
|
||||
Byte byte
|
||||
}{
|
||||
Byte:b,
|
||||
}
|
||||
|
||||
tests = append(tests, test)
|
||||
}
|
||||
|
||||
for b := byte('A'); b <= byte('Z'); b++ {
|
||||
test := struct {
|
||||
Byte byte
|
||||
}{
|
||||
Byte:b,
|
||||
}
|
||||
|
||||
tests = append(tests, test)
|
||||
}
|
||||
|
||||
for b := byte('['); b <= byte('`'); b++ {
|
||||
test := struct {
|
||||
Byte byte
|
||||
}{
|
||||
Byte:b,
|
||||
}
|
||||
|
||||
tests = append(tests, test)
|
||||
}
|
||||
|
||||
for b := byte('a'); b <= byte('z'); b++ {
|
||||
test := struct {
|
||||
Byte byte
|
||||
}{
|
||||
Byte:b,
|
||||
}
|
||||
|
||||
tests = append(tests, test)
|
||||
}
|
||||
|
||||
for b := byte('{'); b <= byte('~'); b++ {
|
||||
test := struct {
|
||||
Byte byte
|
||||
}{
|
||||
Byte:b,
|
||||
}
|
||||
|
||||
tests = append(tests, test)
|
||||
}
|
||||
|
||||
|
||||
for testNumber, test := range tests {
|
||||
|
||||
var writer oitest.ShortWriter
|
||||
err := LongWriteByte(&writer, test.Byte)
|
||||
if nil != err {
|
||||
t.Errorf("For test #%d, did not expect an error, but actually got one: (%T) %q; for %d (%q).", testNumber, err, err.Error(), test.Byte, string(test.Byte))
|
||||
continue
|
||||
}
|
||||
if expected, actual := string(test.Byte), writer.String(); expected != actual {
|
||||
t.Errorf("For test #%d, expected %q, but actually got %q", testNumber, expected, actual)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
39
proxy_go/public/go-oi@v1.0.0/longwritestring.go
Normal file
39
proxy_go/public/go-oi@v1.0.0/longwritestring.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package oi
|
||||
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
|
||||
// LongWriteString tries to write the bytes from 's' to the writer 'w', such that it deals
|
||||
// with "short writes" where w.Write (or w.WriteString) would return an error of io.ErrShortWrite
|
||||
// and n < len(s).
|
||||
//
|
||||
// Note that LongWriteString still could return the error io.ErrShortWrite; but this
|
||||
// would only be after trying to handle the io.ErrShortWrite a number of times, and
|
||||
// then eventually giving up.
|
||||
func LongWriteString(w io.Writer, s string) (int64, error) {
|
||||
|
||||
numWritten := int64(0)
|
||||
for {
|
||||
//@TODO: Should check to make sure this doesn't get stuck in an infinite loop writting nothing!
|
||||
n, err := io.WriteString(w, s)
|
||||
numWritten += int64(n)
|
||||
if nil != err && io.ErrShortWrite != err {
|
||||
return numWritten, err
|
||||
}
|
||||
|
||||
if !(n < len(s)) {
|
||||
break
|
||||
}
|
||||
|
||||
s = s[n:]
|
||||
|
||||
if len(s) < 1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return numWritten, nil
|
||||
}
|
||||
191
proxy_go/public/go-oi@v1.0.0/longwritestring_test.go
Normal file
191
proxy_go/public/go-oi@v1.0.0/longwritestring_test.go
Normal file
@@ -0,0 +1,191 @@
|
||||
package oi
|
||||
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-oi/test"
|
||||
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func TestLongWriteString(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
String string
|
||||
}{
|
||||
{
|
||||
String: "",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
String: "apple",
|
||||
},
|
||||
{
|
||||
String: "banana",
|
||||
},
|
||||
{
|
||||
String: "cherry",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
String: "Hello world!",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
String: "😁😂😃😄😅😆😉😊😋😌😍😏😒😓😔😖😘😚😜😝😞😠😡😢😣😤😥😨😩😪😫😭😰😱😲😳😵😷",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
String: "0123456789",
|
||||
},
|
||||
{
|
||||
String: "٠١٢٣٤٥٦٧٨٩", // Arabic-Indic Digits
|
||||
},
|
||||
{
|
||||
String: "۰۱۲۳۴۵۶۷۸۹", // Extended Arabic-Indic Digits
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
String: "Ⅰ Ⅱ Ⅲ Ⅳ Ⅴ Ⅵ Ⅶ Ⅷ Ⅸ Ⅹ Ⅺ Ⅻ Ⅼ Ⅽ Ⅾ Ⅿ",
|
||||
},
|
||||
{
|
||||
String: "ⅰ ⅱ ⅲ ⅳ ⅴ ⅵ ⅶ ⅷ ⅸ ⅹ ⅺ ⅻ ⅼ ⅽ ⅾ ⅿ",
|
||||
},
|
||||
{
|
||||
String: "ↀ ↁ ↂ Ↄ ↄ ↅ ↆ ↇ ↈ",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
for testNumber, test := range tests {
|
||||
|
||||
var writer oitest.ShortWriter
|
||||
n, err := LongWriteString(&writer, test.String)
|
||||
if nil != err {
|
||||
t.Errorf("For test #%d, did not expect an error, but actually got one: (%T) %q; for %q.", testNumber, err, err.Error(), test.String)
|
||||
continue
|
||||
}
|
||||
if expected, actual := int64(len([]byte(test.String))), n; expected != actual {
|
||||
t.Errorf("For test #%d, expected %d, but actually got %d; for %q.", testNumber, expected, actual, test.String)
|
||||
continue
|
||||
}
|
||||
if expected, actual := test.String, writer.String(); expected != actual {
|
||||
t.Errorf("For test #%d, expected %q, but actually got %q", testNumber, expected, actual)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func TestLongWriteStringExpectError(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
String string
|
||||
Expected string
|
||||
Writes []int
|
||||
Err error
|
||||
}{
|
||||
{
|
||||
String: "apple",
|
||||
Expected: "appl",
|
||||
Writes: []int{2,2},
|
||||
Err: errors.New("Crabapple!"),
|
||||
},
|
||||
{
|
||||
String: "apple",
|
||||
Expected: "appl",
|
||||
Writes: []int{2,2,0},
|
||||
Err: errors.New("Crabapple!!"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
String: "banana",
|
||||
Expected: "banan",
|
||||
Writes: []int{2,3},
|
||||
Err: errors.New("bananananananana!"),
|
||||
},
|
||||
{
|
||||
String: "banana",
|
||||
Expected: "banan",
|
||||
Writes: []int{2,3,0},
|
||||
Err: errors.New("bananananananananananananana!!!"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
String: "cherry",
|
||||
Expected: "cher",
|
||||
Writes: []int{1,1,1,1},
|
||||
Err: errors.New("C.H.E.R.R.Y."),
|
||||
},
|
||||
{
|
||||
String: "cherry",
|
||||
Expected: "cher",
|
||||
Writes: []int{1,1,1,1,0},
|
||||
Err: errors.New("C_H_E_R_R_Y"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
String: "Hello world!",
|
||||
Expected: "Hello world",
|
||||
Writes: []int{1,2,3,5},
|
||||
Err: errors.New("Welcome!"),
|
||||
},
|
||||
{
|
||||
String: "Hello world!",
|
||||
Expected: "Hello world",
|
||||
Writes: []int{1,2,3,5,0},
|
||||
Err: errors.New("WeLcOmE!!!"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
String: " ",
|
||||
Expected: " ",
|
||||
Writes: []int{1,2,3,5,8,13},
|
||||
Err: errors.New("Space, the final frontier"),
|
||||
},
|
||||
{
|
||||
String: " ",
|
||||
Expected: " ",
|
||||
Writes: []int{1,2,3,5,8,13,0},
|
||||
Err: errors.New("Space, the final frontier"),
|
||||
},
|
||||
}
|
||||
|
||||
for testNumber, test := range tests {
|
||||
|
||||
writer := oitest.NewWritesThenErrorWriter(test.Err, test.Writes...)
|
||||
n, err := LongWriteString(writer, test.String)
|
||||
if nil == err {
|
||||
t.Errorf("For test #%d, expected to get an error, but actually did not get one: %v; for %q.", testNumber, err, test.String)
|
||||
continue
|
||||
}
|
||||
if expected, actual := test.Err, err; expected != actual {
|
||||
t.Errorf("For test #%d, expected to get error (%T) %q, but actually got (%T) %q; for %q.", testNumber, expected, expected.Error(), actual, actual.Error(), test.String)
|
||||
continue
|
||||
}
|
||||
if expected, actual := int64(len(test.Expected)), n; expected != actual {
|
||||
t.Errorf("For test #%d, expected number of bytes written to be %d = len(%q), but actually was %d = len(%q); for %q.", testNumber, expected, test.Expected, actual, writer.String(), test.String)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
5
proxy_go/public/go-oi@v1.0.0/test/doc.go
Normal file
5
proxy_go/public/go-oi@v1.0.0/test/doc.go
Normal file
@@ -0,0 +1,5 @@
|
||||
/*
|
||||
Package oitest provides useful tools to be used for testing implementations of interfaces in Go's standard "io" package.
|
||||
|
||||
*/
|
||||
package oitest
|
||||
12
proxy_go/public/go-oi@v1.0.0/test/randomness.go
Normal file
12
proxy_go/public/go-oi@v1.0.0/test/randomness.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package oitest
|
||||
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
var (
|
||||
randomness = rand.New(rand.NewSource( time.Now().UTC().UnixNano() ))
|
||||
)
|
||||
80
proxy_go/public/go-oi@v1.0.0/test/short_writer.go
Normal file
80
proxy_go/public/go-oi@v1.0.0/test/short_writer.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package oitest
|
||||
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
|
||||
// ShortWriter is useful for testing things that makes use of
|
||||
// or accept an io.Writer.
|
||||
//
|
||||
// In particular, it is useful to see if that thing can handle
|
||||
// an io.Writer that does a "short write".
|
||||
//
|
||||
// A "short write" is the case when a call like:
|
||||
//
|
||||
// n, err := w.Write(p)
|
||||
//
|
||||
// Returns an n < len(p) and err == io.ErrShortWrite.
|
||||
//
|
||||
// A thing that can "handle" this situation might try
|
||||
// writing again, but only what didn't get written.
|
||||
//
|
||||
// For a simple example of this:
|
||||
//
|
||||
// n, err := w.Write(p)
|
||||
//
|
||||
// if io.ErrShortWrite == err {
|
||||
// n2, err2 := w.Write(p[n:])
|
||||
// }
|
||||
//
|
||||
// Note that the second call to the Write method passed
|
||||
// 'p[n:]' (instead of just 'p'), to account for 'n' bytes
|
||||
// already written (with the first call to the Write
|
||||
// method).
|
||||
//
|
||||
// A more "production quality" version of this would likely
|
||||
// be in a loop, but such that that loop had "guards" against
|
||||
// looping forever, and also possibly looping for "too long".
|
||||
type ShortWriter struct {
|
||||
buffer bytes.Buffer
|
||||
}
|
||||
|
||||
|
||||
// Write makes it so ShortWriter fits the io.Writer interface.
|
||||
//
|
||||
// ShortWriter's version of Write will "short write" if len(p) >= 2,
|
||||
// else it will
|
||||
func (w *ShortWriter) Write(p []byte) (int, error) {
|
||||
if len(p) < 1 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
m := 1
|
||||
if limit := len(p)-1; limit > 1 {
|
||||
m += randomness.Intn(len(p)-1)
|
||||
}
|
||||
|
||||
n, err := w.buffer.Write(p[:m])
|
||||
|
||||
err = nil
|
||||
if n < len(p) {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
|
||||
// Returns what was written to the ShortWriter as a []byte.
|
||||
func (w ShortWriter) Bytes() []byte {
|
||||
return w.buffer.Bytes()
|
||||
}
|
||||
|
||||
|
||||
// Returns what was written to the ShortWriter as a string.
|
||||
func (w ShortWriter) String() string {
|
||||
return w.buffer.String()
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package oitest
|
||||
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
|
||||
// WritesThenErrorWriter is useful for testing things that makes
|
||||
// use of or accept an io.Writer.
|
||||
//
|
||||
// In particular, it is useful to see if that thing can handle
|
||||
// an io.Writer that does a "short write".
|
||||
//
|
||||
// A "short write" is the case when a call like:
|
||||
//
|
||||
// n, err := w.Write(p)
|
||||
//
|
||||
// Returns an n < len(p) and err == io.ErrShortWrite.
|
||||
//
|
||||
// A thing that can "handle" this situation might try
|
||||
// writing again, but only what didn't get written.
|
||||
//
|
||||
// For a simple example of this:
|
||||
//
|
||||
// n, err := w.Write(p)
|
||||
//
|
||||
// if io.ErrShortWrite == err {
|
||||
// n2, err2 := w.Write(p[n:])
|
||||
// }
|
||||
//
|
||||
// Note that the second call to the Write method passed
|
||||
// 'p[n:]' (instead of just 'p'), to account for 'n' bytes
|
||||
// already written (with the first call to the Write
|
||||
// method).
|
||||
//
|
||||
// A more "production quality" version of this would likely
|
||||
// be in a loop, but such that that loop had "guards" against
|
||||
// looping forever, and also possibly looping for "too long".
|
||||
type WritesThenErrorWriter struct {
|
||||
buffer bytes.Buffer
|
||||
err error
|
||||
numbersWritten []int
|
||||
writeNumber int
|
||||
}
|
||||
|
||||
|
||||
// NewWritesThenErrorWriter returns a new *WritesThenErrorWriter.
|
||||
func NewWritesThenErrorWriter(err error, numbersWritten ...int) *WritesThenErrorWriter {
|
||||
|
||||
slice := make([]int, len(numbersWritten))
|
||||
copy(slice, numbersWritten)
|
||||
|
||||
writer := WritesThenErrorWriter{
|
||||
err:err,
|
||||
numbersWritten:slice,
|
||||
writeNumber:0,
|
||||
}
|
||||
|
||||
return &writer
|
||||
}
|
||||
|
||||
|
||||
// Write makes it so *WritesThenErrorWriter fits the io.Writer interface.
|
||||
//
|
||||
// *WritesThenErrorWriter's version of Write will "short write" all but
|
||||
// the last call to write, where it will return the specified error (which
|
||||
// could, of course, be nil, if that was specified).
|
||||
func (writer *WritesThenErrorWriter) Write(p []byte) (int, error) {
|
||||
|
||||
m := writer.numbersWritten[writer.writeNumber]
|
||||
|
||||
writer.buffer.Write(p[:m])
|
||||
|
||||
writer.writeNumber++
|
||||
|
||||
if len(writer.numbersWritten) == writer.writeNumber {
|
||||
return m, writer.err
|
||||
}
|
||||
|
||||
return m, io.ErrShortWrite
|
||||
}
|
||||
|
||||
|
||||
// Returns what was written to the ShortWriter as a []byte.
|
||||
func (w WritesThenErrorWriter) Bytes() []byte {
|
||||
return w.buffer.Bytes()
|
||||
}
|
||||
|
||||
|
||||
// Returns what was written to the ShortWriter as a string.
|
||||
func (w WritesThenErrorWriter) String() string {
|
||||
return w.buffer.String()
|
||||
}
|
||||
|
||||
38
proxy_go/public/go-oi@v1.0.0/writenopcloser.go
Normal file
38
proxy_go/public/go-oi@v1.0.0/writenopcloser.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package oi
|
||||
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
|
||||
// WriteNopCloser takes an io.Writer and returns an io.WriteCloser where
|
||||
// calling the Write method on the returned io.WriterCloser calls the
|
||||
// Write method on the io.Writer it received, but whre calling the Close
|
||||
// method on the returned io.WriterCloser does "nothing" (i.e., is a "nop").
|
||||
//
|
||||
// This is useful in cases where an io.WriteCloser is expected, but you
|
||||
// only have an io.Writer (where closing doesn't make sense) and you
|
||||
// need to make your io.Writer fit. (I.e., you need an adaptor.)
|
||||
func WriteNopCloser(w io.Writer) io.WriteCloser {
|
||||
wc := internalWriteNopCloser{
|
||||
writer:w,
|
||||
}
|
||||
|
||||
return &wc
|
||||
}
|
||||
|
||||
|
||||
type internalWriteNopCloser struct {
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
|
||||
func (wc * internalWriteNopCloser) Write(p []byte) (n int, err error) {
|
||||
return wc.writer.Write(p)
|
||||
}
|
||||
|
||||
|
||||
func (wc * internalWriteNopCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2016 Charles Iliya Krempeaux <charles@reptile.ca> :: http://changelog.ca/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,252 @@
|
||||
# go-telnet
|
||||
|
||||
Package **telnet** provides TELNET and TELNETS client and server implementations, for the Go programming language.
|
||||
|
||||
|
||||
The **telnet** package provides an API in a style similar to the "net/http" library that is part of the Go standard library, including support for "middleware".
|
||||
|
||||
|
||||
(TELNETS is *secure TELNET*, with the TELNET protocol over a secured TLS (or SSL) connection.)
|
||||
|
||||
|
||||
## Documention
|
||||
|
||||
Online documentation, which includes examples, can be found at: http://godoc.org/github.com/reiver/go-telnet
|
||||
|
||||
[](https://godoc.org/github.com/reiver/go-telnet)
|
||||
|
||||
|
||||
## Very Simple TELNET Server Example
|
||||
|
||||
A very very simple TELNET server is shown in the following code.
|
||||
|
||||
This particular TELNET server just echos back to the user anything they "submit" to the server.
|
||||
|
||||
(By default, a TELNET client does *not* send anything to the server until the [Enter] key is pressed.
|
||||
"Submit" means typing something and then pressing the [Enter] key.)
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-telnet"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
var handler telnet.Handler = telnet.EchoHandler
|
||||
|
||||
err := telnet.ListenAndServe(":5555", handler)
|
||||
if nil != err {
|
||||
//@TODO: Handle this error better.
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
If you wanted to test out this very very simple TELNET server, if you were on the same computer it was
|
||||
running, you could connect to it using the bash command:
|
||||
```
|
||||
telnet localhost 5555
|
||||
```
|
||||
(Note that we use the same TCP port number -- "5555" -- as we had in our code. That is important, as the
|
||||
value used by your TELNET server and the value used by your TELNET client **must** match.)
|
||||
|
||||
|
||||
## Very Simple (Secure) TELNETS Server Example
|
||||
|
||||
TELNETS is the secure version of TELNET.
|
||||
|
||||
The code to make a TELNETS server is very similar to the code to make a TELNET server.
|
||||
(The difference between we use the `telnet.ListenAndServeTLS` func instead of the
|
||||
`telnet.ListenAndServe` func.)
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-telnet"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
var handler telnet.Handler = telnet.EchoHandler
|
||||
|
||||
err := telnet.ListenAndServeTLS(":5555", "cert.pem", "key.pem", handler)
|
||||
if nil != err {
|
||||
//@TODO: Handle this error better.
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
If you wanted to test out this very very simple TELNETS server, get the `telnets` client program from here:
|
||||
https://github.com/reiver/telnets
|
||||
|
||||
|
||||
## TELNET Client Example:
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-telnet"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var caller telnet.Caller = telnet.StandardCaller
|
||||
|
||||
//@TOOD: replace "example.net:5555" with address you want to connect to.
|
||||
telnet.DialToAndCall("example.net:5555", caller)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## TELNETS Client Example:
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-telnet"
|
||||
|
||||
"crypto/tls"
|
||||
)
|
||||
|
||||
func main() {
|
||||
//@TODO: Configure the TLS connection here, if you need to.
|
||||
tlsConfig := &tls.Config{}
|
||||
|
||||
var caller telnet.Caller = telnet.StandardCaller
|
||||
|
||||
//@TOOD: replace "example.net:5555" with address you want to connect to.
|
||||
telnet.DialToAndCallTLS("example.net:5555", caller, tlsConfig)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## TELNET Shell Server Example
|
||||
|
||||
A more useful TELNET servers can be made using the `"github.com/reiver/go-telnet/telsh"` sub-package.
|
||||
|
||||
For example:
|
||||
```
|
||||
package main
|
||||
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-oi"
|
||||
"github.com/reiver/go-telnet"
|
||||
"github.com/reiver/go-telnet/telsh"
|
||||
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
|
||||
func fiveHandler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
oi.LongWriteString(stdout, "The number FIVE looks like this: 5\r\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func fiveProducer(ctx telnet.Context, name string, args ...string) telsh.Handler{
|
||||
return telsh.PromoteHandlerFunc(fiveHandler)
|
||||
}
|
||||
|
||||
|
||||
|
||||
func danceHandler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
for i:=0; i<20; i++ {
|
||||
oi.LongWriteString(stdout, "\r⠋")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠙")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠹")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠸")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠼")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠴")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠦")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠧")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠇")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠏")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
}
|
||||
oi.LongWriteString(stdout, "\r \r\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func danceProducer(ctx telnet.Context, name string, args ...string) telsh.Handler{
|
||||
|
||||
return telsh.PromoteHandlerFunc(danceHandler)
|
||||
}
|
||||
|
||||
|
||||
func main() {
|
||||
|
||||
shellHandler := telsh.NewShellHandler()
|
||||
|
||||
shellHandler.WelcomeMessage = `
|
||||
__ __ ______ _ _____ ____ __ __ ______
|
||||
\ \ / /| ____|| | / ____| / __ \ | \/ || ____|
|
||||
\ \ /\ / / | |__ | | | | | | | || \ / || |__
|
||||
\ \/ \/ / | __| | | | | | | | || |\/| || __|
|
||||
\ /\ / | |____ | |____ | |____ | |__| || | | || |____
|
||||
\/ \/ |______||______| \_____| \____/ |_| |_||______|
|
||||
|
||||
`
|
||||
|
||||
|
||||
// Register the "five" command.
|
||||
commandName := "five"
|
||||
commandProducer := telsh.ProducerFunc(fiveProducer)
|
||||
|
||||
shellHandler.Register(commandName, commandProducer)
|
||||
|
||||
|
||||
|
||||
// Register the "dance" command.
|
||||
commandName = "dance"
|
||||
commandProducer = telsh.ProducerFunc(danceProducer)
|
||||
|
||||
shellHandler.Register(commandName, commandProducer)
|
||||
|
||||
|
||||
|
||||
shellHandler.Register("dance", telsh.ProducerFunc(danceProducer))
|
||||
|
||||
addr := ":5555"
|
||||
if err := telnet.ListenAndServe(addr, shellHandler); nil != err {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
TELNET servers made using the `"github.com/reiver/go-telnet/telsh"` sub-package will often be more useful
|
||||
as it makes it easier for you to create a *shell* interface.
|
||||
|
||||
|
||||
# More Information
|
||||
|
||||
There is a lot more information about documentation on all this here: http://godoc.org/github.com/reiver/go-telnet
|
||||
|
||||
(You should really read those.)
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package telnet
|
||||
|
||||
|
||||
// A Caller represents the client end of a TELNET (or TELNETS) connection.
|
||||
//
|
||||
// Writing data to the Writer passed as an argument to the CallTELNET method
|
||||
// will send data to the TELNET (or TELNETS) server.
|
||||
//
|
||||
// Reading data from the Reader passed as an argument to the CallTELNET method
|
||||
// will receive data from the TELNET server.
|
||||
//
|
||||
// The Writer's Write method sends "escaped" TELNET (and TELNETS) data.
|
||||
//
|
||||
// The Reader's Read method "un-escapes" TELNET (and TELNETS) data, and filters
|
||||
// out TELNET (and TELNETS) command sequences.
|
||||
type Caller interface {
|
||||
CallTELNET(Context, Writer, Reader)
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package telnet
|
||||
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
)
|
||||
|
||||
|
||||
func DialAndCall(caller Caller) error {
|
||||
conn, err := Dial()
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
|
||||
client := &Client{Caller:caller}
|
||||
|
||||
return client.Call(conn)
|
||||
}
|
||||
|
||||
|
||||
func DialToAndCall(srvAddr string, caller Caller) error {
|
||||
conn, err := DialTo(srvAddr)
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
|
||||
client := &Client{Caller:caller}
|
||||
|
||||
return client.Call(conn)
|
||||
}
|
||||
|
||||
|
||||
func DialAndCallTLS(caller Caller, tlsConfig *tls.Config) error {
|
||||
conn, err := DialTLS(tlsConfig)
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
|
||||
client := &Client{Caller:caller}
|
||||
|
||||
return client.Call(conn)
|
||||
}
|
||||
|
||||
func DialToAndCallTLS(srvAddr string, caller Caller, tlsConfig *tls.Config) error {
|
||||
conn, err := DialToTLS(srvAddr, tlsConfig)
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
|
||||
client := &Client{Caller:caller}
|
||||
|
||||
return client.Call(conn)
|
||||
}
|
||||
|
||||
|
||||
type Client struct {
|
||||
Caller Caller
|
||||
|
||||
Logger Logger
|
||||
}
|
||||
|
||||
|
||||
func (client *Client) Call(conn *Conn) error {
|
||||
|
||||
logger := client.logger()
|
||||
|
||||
|
||||
caller := client.Caller
|
||||
if nil == caller {
|
||||
logger.Debug("Defaulted caller to StandardCaller.")
|
||||
caller = StandardCaller
|
||||
}
|
||||
|
||||
|
||||
var ctx Context = NewContext().InjectLogger(logger)
|
||||
|
||||
var w Writer = conn
|
||||
var r Reader = conn
|
||||
|
||||
caller.CallTELNET(ctx, w, r)
|
||||
conn.Close()
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func (client *Client) logger() Logger {
|
||||
logger := client.Logger
|
||||
if nil == logger {
|
||||
logger = internalDiscardLogger{}
|
||||
}
|
||||
|
||||
return logger
|
||||
}
|
||||
|
||||
|
||||
func (client *Client) SetAuth(username string) {
|
||||
//@TODO: #################################################
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package telnet
|
||||
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
)
|
||||
|
||||
|
||||
type Conn struct {
|
||||
conn interface {
|
||||
Read(b []byte) (n int, err error)
|
||||
Write(b []byte) (n int, err error)
|
||||
Close() error
|
||||
LocalAddr() net.Addr
|
||||
RemoteAddr() net.Addr
|
||||
}
|
||||
dataReader *internalDataReader
|
||||
dataWriter *internalDataWriter
|
||||
}
|
||||
|
||||
|
||||
// Dial makes a (un-secure) TELNET client connection to the system's 'loopback address'
|
||||
// (also known as "localhost" or 127.0.0.1).
|
||||
//
|
||||
// If a secure connection is desired, use `DialTLS` instead.
|
||||
func Dial() (*Conn, error) {
|
||||
return DialTo("")
|
||||
}
|
||||
|
||||
// DialTo makes a (un-secure) TELNET client connection to the the address specified by
|
||||
// 'addr'.
|
||||
//
|
||||
// If a secure connection is desired, use `DialToTLS` instead.
|
||||
func DialTo(addr string) (*Conn, error) {
|
||||
|
||||
const network = "tcp"
|
||||
|
||||
if "" == addr {
|
||||
addr = "127.0.0.1:telnet"
|
||||
}
|
||||
|
||||
conn, err := net.Dial(network, addr)
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dataReader := newDataReader(conn)
|
||||
dataWriter := newDataWriter(conn)
|
||||
|
||||
clientConn := Conn{
|
||||
conn:conn,
|
||||
dataReader:dataReader,
|
||||
dataWriter:dataWriter,
|
||||
}
|
||||
|
||||
return &clientConn, nil
|
||||
}
|
||||
|
||||
|
||||
// DialTLS makes a (secure) TELNETS client connection to the system's 'loopback address'
|
||||
// (also known as "localhost" or 127.0.0.1).
|
||||
func DialTLS(tlsConfig *tls.Config) (*Conn, error) {
|
||||
return DialToTLS("", tlsConfig)
|
||||
}
|
||||
|
||||
// DialToTLS makes a (secure) TELNETS client connection to the the address specified by
|
||||
// 'addr'.
|
||||
func DialToTLS(addr string, tlsConfig *tls.Config) (*Conn, error) {
|
||||
|
||||
const network = "tcp"
|
||||
|
||||
if "" == addr {
|
||||
addr = "127.0.0.1:telnets"
|
||||
}
|
||||
|
||||
conn, err := tls.Dial(network, addr, tlsConfig)
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dataReader := newDataReader(conn)
|
||||
dataWriter := newDataWriter(conn)
|
||||
|
||||
clientConn := Conn{
|
||||
conn:conn,
|
||||
dataReader:dataReader,
|
||||
dataWriter:dataWriter,
|
||||
}
|
||||
|
||||
return &clientConn, nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Close closes the client connection.
|
||||
//
|
||||
// Typical usage might look like:
|
||||
//
|
||||
// telnetsClient, err = telnet.DialToTLS(addr, tlsConfig)
|
||||
// if nil != err {
|
||||
// //@TODO: Handle error.
|
||||
// return err
|
||||
// }
|
||||
// defer telnetsClient.Close()
|
||||
func (clientConn *Conn) Close() error {
|
||||
return clientConn.conn.Close()
|
||||
}
|
||||
|
||||
|
||||
// Read receives `n` bytes sent from the server to the client,
|
||||
// and "returns" into `p`.
|
||||
//
|
||||
// Note that Read can only be used for receiving TELNET (and TELNETS) data from the server.
|
||||
//
|
||||
// TELNET (and TELNETS) command codes cannot be received using this method, as Read deals
|
||||
// with TELNET (and TELNETS) "unescaping", and (when appropriate) filters out TELNET (and TELNETS)
|
||||
// command codes.
|
||||
//
|
||||
// Read makes Client fit the io.Reader interface.
|
||||
func (clientConn *Conn) Read(p []byte) (n int, err error) {
|
||||
return clientConn.dataReader.Read(p)
|
||||
}
|
||||
|
||||
|
||||
// Write sends `n` bytes from 'p' to the server.
|
||||
//
|
||||
// Note that Write can only be used for sending TELNET (and TELNETS) data to the server.
|
||||
//
|
||||
// TELNET (and TELNETS) command codes cannot be sent using this method, as Write deals with
|
||||
// TELNET (and TELNETS) "escaping", and will properly "escape" anything written with it.
|
||||
//
|
||||
// Write makes Conn fit the io.Writer interface.
|
||||
func (clientConn *Conn) Write(p []byte) (n int, err error) {
|
||||
return clientConn.dataWriter.Write(p)
|
||||
}
|
||||
|
||||
|
||||
// LocalAddr returns the local network address.
|
||||
func (clientConn *Conn) LocalAddr() net.Addr {
|
||||
return clientConn.conn.LocalAddr()
|
||||
}
|
||||
|
||||
|
||||
// RemoteAddr returns the remote network address.
|
||||
func (clientConn *Conn) RemoteAddr() net.Addr {
|
||||
return clientConn.conn.RemoteAddr()
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package telnet
|
||||
|
||||
|
||||
type Context interface {
|
||||
Logger() Logger
|
||||
|
||||
InjectLogger(Logger) Context
|
||||
}
|
||||
|
||||
|
||||
type internalContext struct {
|
||||
logger Logger
|
||||
}
|
||||
|
||||
|
||||
func NewContext() Context {
|
||||
ctx := internalContext{}
|
||||
|
||||
return &ctx
|
||||
}
|
||||
|
||||
|
||||
func (ctx *internalContext) Logger() Logger {
|
||||
return ctx.logger
|
||||
}
|
||||
|
||||
func (ctx *internalContext) InjectLogger(logger Logger) Context {
|
||||
ctx.logger = logger
|
||||
|
||||
return ctx
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package telnet
|
||||
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
|
||||
var (
|
||||
errCorrupted = errors.New("Corrupted")
|
||||
)
|
||||
|
||||
|
||||
// An internalDataReader deals with "un-escaping" according to the TELNET protocol.
|
||||
//
|
||||
// In the TELNET protocol byte value 255 is special.
|
||||
//
|
||||
// The TELNET protocol calls byte value 255: "IAC". Which is short for "interpret as command".
|
||||
//
|
||||
// The TELNET protocol also has a distinction between 'data' and 'commands'.
|
||||
//
|
||||
//(DataReader is targetted toward TELNET 'data', not TELNET 'commands'.)
|
||||
//
|
||||
// If a byte with value 255 (=IAC) appears in the data, then it must be escaped.
|
||||
//
|
||||
// Escaping byte value 255 (=IAC) in the data is done by putting 2 of them in a row.
|
||||
//
|
||||
// So, for example:
|
||||
//
|
||||
// []byte{255} -> []byte{255, 255}
|
||||
//
|
||||
// Or, for a more complete example, if we started with the following:
|
||||
//
|
||||
// []byte{1, 55, 2, 155, 3, 255, 4, 40, 255, 30, 20}
|
||||
//
|
||||
// ... TELNET escaping would produce the following:
|
||||
//
|
||||
// []byte{1, 55, 2, 155, 3, 255, 255, 4, 40, 255, 255, 30, 20}
|
||||
//
|
||||
// (Notice that each "255" in the original byte array became 2 "255"s in a row.)
|
||||
//
|
||||
// DataReader deals with "un-escaping". In other words, it un-does what was shown
|
||||
// in the examples.
|
||||
//
|
||||
// So, for example, it does this:
|
||||
//
|
||||
// []byte{255, 255} -> []byte{255}
|
||||
//
|
||||
// And, for example, goes from this:
|
||||
//
|
||||
// []byte{1, 55, 2, 155, 3, 255, 255, 4, 40, 255, 255, 30, 20}
|
||||
//
|
||||
// ... to this:
|
||||
//
|
||||
// []byte{1, 55, 2, 155, 3, 255, 4, 40, 255, 30, 20}
|
||||
type internalDataReader struct {
|
||||
wrapped io.Reader
|
||||
buffered *bufio.Reader
|
||||
}
|
||||
|
||||
|
||||
// newDataReader creates a new DataReader reading from 'r'.
|
||||
func newDataReader(r io.Reader) *internalDataReader {
|
||||
buffered := bufio.NewReader(r)
|
||||
|
||||
reader := internalDataReader{
|
||||
wrapped:r,
|
||||
buffered:buffered,
|
||||
}
|
||||
|
||||
return &reader
|
||||
}
|
||||
|
||||
|
||||
// Read reads the TELNET escaped data from the wrapped io.Reader, and "un-escapes" it into 'data'.
|
||||
func (r *internalDataReader) Read(data []byte) (n int, err error) {
|
||||
|
||||
const IAC = 255
|
||||
|
||||
const SB = 250
|
||||
const SE = 240
|
||||
|
||||
const WILL = 251
|
||||
const WONT = 252
|
||||
const DO = 253
|
||||
const DONT = 254
|
||||
|
||||
p := data
|
||||
|
||||
for len(p) > 0 {
|
||||
var b byte
|
||||
|
||||
b, err = r.buffered.ReadByte()
|
||||
if nil != err {
|
||||
return n, err
|
||||
}
|
||||
|
||||
if IAC == b {
|
||||
var peeked []byte
|
||||
|
||||
peeked, err = r.buffered.Peek(1)
|
||||
if nil != err {
|
||||
return n, err
|
||||
}
|
||||
|
||||
switch peeked[0] {
|
||||
case WILL, WONT, DO, DONT:
|
||||
_, err = r.buffered.Discard(2)
|
||||
if nil != err {
|
||||
return n, err
|
||||
}
|
||||
case IAC:
|
||||
p[0] = IAC
|
||||
n++
|
||||
p = p[1:]
|
||||
|
||||
_, err = r.buffered.Discard(1)
|
||||
if nil != err {
|
||||
return n, err
|
||||
}
|
||||
case SB:
|
||||
for {
|
||||
var b2 byte
|
||||
b2, err = r.buffered.ReadByte()
|
||||
if nil != err {
|
||||
return n, err
|
||||
}
|
||||
|
||||
if IAC == b2 {
|
||||
peeked, err = r.buffered.Peek(1)
|
||||
if nil != err {
|
||||
return n, err
|
||||
}
|
||||
|
||||
if IAC == peeked[0] {
|
||||
_, err = r.buffered.Discard(1)
|
||||
if nil != err {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
|
||||
if SE == peeked[0] {
|
||||
_, err = r.buffered.Discard(1)
|
||||
if nil != err {
|
||||
return n, err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
case SE:
|
||||
_, err = r.buffered.Discard(1)
|
||||
if nil != err {
|
||||
return n, err
|
||||
}
|
||||
default:
|
||||
// If we get in here, this is not following the TELNET protocol.
|
||||
//@TODO: Make a better error.
|
||||
err = errCorrupted
|
||||
return n, err
|
||||
}
|
||||
} else {
|
||||
|
||||
p[0] = b
|
||||
n++
|
||||
p = p[1:]
|
||||
}
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
package telnet
|
||||
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func TestDataReader(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
Bytes []byte
|
||||
Expected []byte
|
||||
}{
|
||||
{
|
||||
Bytes: []byte{},
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("a"),
|
||||
Expected: []byte("a"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("b"),
|
||||
Expected: []byte("b"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("c"),
|
||||
Expected: []byte("c"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple"),
|
||||
Expected: []byte("apple"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("banana"),
|
||||
Expected: []byte("banana"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("cherry"),
|
||||
Expected: []byte("cherry"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple banana cherry"),
|
||||
Expected: []byte("apple banana cherry"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255,255},
|
||||
Expected: []byte{255},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,255,255,255},
|
||||
Expected: []byte{255,255},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,255,255,255,255,255},
|
||||
Expected: []byte{255,255,255},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,255,255,255,255,255,255,255},
|
||||
Expected: []byte{255,255,255,255},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,255,255,255,255,255,255,255,255,255},
|
||||
Expected: []byte{255,255,255,255,255},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple\xff\xffbanana\xff\xffcherry"),
|
||||
Expected: []byte("apple\xffbanana\xffcherry"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("\xff\xffapple\xff\xffbanana\xff\xffcherry\xff\xff"),
|
||||
Expected: []byte("\xffapple\xffbanana\xffcherry\xff"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple\xff\xff\xff\xffbanana\xff\xff\xff\xffcherry"),
|
||||
Expected: []byte("apple\xff\xffbanana\xff\xffcherry"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("\xff\xff\xff\xffapple\xff\xff\xff\xffbanana\xff\xff\xff\xffcherry\xff\xff\xff\xff"),
|
||||
Expected: []byte("\xff\xffapple\xff\xffbanana\xff\xffcherry\xff\xff"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255,251,24}, // IAC WILL TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,252,24}, // IAC WON'T TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,253,24}, // IAC DO TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,254,24}, // IAC DON'T TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255,251,24}, // 'C' IAC WILL TERMINAL-TYPE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,252,24}, // 'C' IAC WON'T TERMINAL-TYPE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,253,24}, // 'C' IAC DO TERMINAL-TYPE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,254,24}, // 'C' IAC DON'T TERMINAL-TYPE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255,251,24, 68}, // IAC WILL TERMINAL-TYPE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,252,24, 68}, // IAC WON'T TERMINAL-TYPE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,253,24, 68}, // IAC DO TERMINAL-TYPE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,254,24, 68}, // IAC DON'T TERMINAL-TYPE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255,251,24, 68}, // 'C' IAC WILL TERMINAL-TYPE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,252,24, 68}, // 'C' IAC WON'T TERMINAL-TYPE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,253,24, 68}, // 'C' IAC DO TERMINAL-TYPE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,254,24, 68}, // 'C' IAC DON'T TERMINAL-TYPE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255, 250, 24, 1, 255, 240}, // IAC SB TERMINAL-TYPE SEND IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 250, 24, 0, 68,69,67,45,86,84,53,50 ,255, 240}, // IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 1, 255, 240}, // 'C' IAC SB TERMINAL-TYPE SEND IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 0, 68,69,67,45,86,84,53,50 ,255, 240}, // 'C' IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255, 250, 24, 1, 255, 240, 68}, // IAC SB TERMINAL-TYPE SEND IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 250, 24, 0, 68,69,67,45,86,84,53,50 ,255, 240, 68}, // IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 1, 255, 240, 68}, // 'C' IAC SB TERMINAL-TYPE SEND IAC SE 'D'
|
||||
Expected: []byte{67, 68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 0, 68,69,67,45,86,84,53,50 ,255, 240, 68}, // 'C' IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE 'D'
|
||||
Expected: []byte{67, 68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255,250, 0,1,2,3,4,5,6,7,8,9,10,11,12,13 ,255,240}, // IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 0,1,2,3,4,5,6,7,8,9,10,11,12,13 ,255,240}, // 'C' IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,250, 0,1,2,3,4,5,6,7,8,9,10,11,12,13 ,255,240, 68}, // IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 0,1,2,3,4,5,6,7,8,9,10,11,12,13 ,255,240, 68}, // 'C' IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
//@TODO: Is this correct? Can IAC appear between thee 'IAC SB' and ''IAC SE'?... and if "yes", do escaping rules apply?
|
||||
{
|
||||
Bytes: []byte{ 255,250, 255,255,240 ,255,240}, // IAC SB 255 255 240 IAC SE = IAC SB IAC IAC SE IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 255,255,240 ,255,240}, // 'C' IAC SB 255 255 240 IAC SE = IAC SB IAC IAC SE IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,250, 255,255,240 ,255,240, 68}, // IAC SB 255 255 240 IAC SE = IAC SB IAC IAC SE IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 255,255,240 ,255,240, 68}, // 'C' IAC SB 255 255 240 IAC SE = IAC SB IAC IAC SE IAC SE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
//@TODO: Is this correct? Can IAC appear between thee 'IAC SB' and ''IAC SE'?... and if "yes", do escaping rules apply?
|
||||
{
|
||||
Bytes: []byte{ 255,250, 71,255,255,240 ,255,240}, // IAC SB 'G' 255 255 240 IAC SE = IAC SB 'G' IAC IAC SE IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 71,255,255,240 ,255,240}, // 'C' IAC SB 'G' 255 255 240 IAC SE = IAC SB 'G' IAC IAC SE IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,250, 71,255,255,240 ,255,240, 68}, // IAC SB 'G' 255 255 240 IAC SE = IAC SB 'G' IAC IAC SE IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 71,255,255,240 ,255,240, 68}, // 'C' IAC SB 'G' 255 255 240 IAC SE = IAC 'G' SB IAC IAC SE IAC SE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
//@TODO: Is this correct? Can IAC appear between thee 'IAC SB' and ''IAC SE'?... and if "yes", do escaping rules apply?
|
||||
{
|
||||
Bytes: []byte{ 255,250, 255,255,240,72 ,255,240}, // IAC SB 255 255 240 'H' IAC SE = IAC SB IAC IAC SE 'H' IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 255,255,240,72 ,255,240}, // 'C' IAC SB 255 255 240 'H' IAC SE = IAC SB IAC IAC SE 'H' IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,250, 255,255,240,72 ,255,240, 68}, // IAC SB 255 255 240 'H' IAC SE = IAC SB IAC IAC SE 'H' IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 255,255,240,72 ,255,240, 68}, // 'C' IAC SB 255 255 240 'H' IAC SE = IAC SB IAC IAC SE 'H' IAC SE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
//@TODO: Is this correct? Can IAC appear between thee 'IAC SB' and ''IAC SE'?... and if "yes", do escaping rules apply?
|
||||
{
|
||||
Bytes: []byte{ 255,250, 71,255,255,240,72 ,255,240}, // IAC SB 'G' 255 255 240 'H' IAC SE = IAC SB 'G' IAC IAC SE 'H' IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 71,255,255,240,72 ,255,240}, // 'C' IAC SB 'G' 255 255 240 'H' IAC SE = IAC SB 'G' IAC IAC SE 'H' IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,250, 71,255,255,240,72 ,255,240, 68}, // IAC SB 'G' 255 255 240 'H' IAC SE = IAC SB 'G' IAC IAC SE 'H' IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 71,255,255,240,72 ,255,240, 68}, // 'C' IAC SB 'G' 255 255 240 'H' IAC SE = IAC 'G' SB IAC IAC SE 'H' IAC SE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
|
||||
//@TODO: Add random tests.
|
||||
|
||||
|
||||
for testNumber, test := range tests {
|
||||
|
||||
subReader := bytes.NewReader(test.Bytes)
|
||||
|
||||
reader := newDataReader(subReader)
|
||||
|
||||
buffer := make([]byte, 2*len(test.Bytes))
|
||||
n, err := reader.Read(buffer)
|
||||
if nil != err && io.EOF != err {
|
||||
t.Errorf("For test #%d, did not expected an error, but actually got one: (%T) %v; for %q -> %q.", testNumber, err, err, string(test.Bytes), string(test.Expected))
|
||||
continue
|
||||
}
|
||||
|
||||
if expected, actual := len(test.Expected), n; expected != actual {
|
||||
t.Errorf("For test #%d, expected %d, but actually got %d (and %q); for %q -> %q.", testNumber, expected, actual, string(buffer[:n]), string(test.Bytes), string(test.Expected))
|
||||
continue
|
||||
}
|
||||
|
||||
if expected, actual := string(test.Expected), string(buffer[:n]); expected != actual {
|
||||
t.Errorf("For test #%d, expected %q, but actually got %q; for %q -> %q.", testNumber, expected, actual, string(test.Bytes), string(test.Expected))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package telnet
|
||||
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-oi"
|
||||
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
|
||||
var iaciac []byte = []byte{255, 255}
|
||||
|
||||
var errOverflow = errors.New("Overflow")
|
||||
var errPartialIACIACWrite = errors.New("Partial IAC IAC write.")
|
||||
|
||||
|
||||
// An internalDataWriter deals with "escaping" according to the TELNET (and TELNETS) protocol.
|
||||
//
|
||||
// In the TELNET (and TELNETS) protocol byte value 255 is special.
|
||||
//
|
||||
// The TELNET (and TELNETS) protocol calls byte value 255: "IAC". Which is short for "interpret as command".
|
||||
//
|
||||
// The TELNET (and TELNETS) protocol also has a distinction between 'data' and 'commands'.
|
||||
//
|
||||
//(DataWriter is targetted toward TELNET (and TELNETS) 'data', not TELNET (and TELNETS) 'commands'.)
|
||||
//
|
||||
// If a byte with value 255 (=IAC) appears in the data, then it must be escaped.
|
||||
//
|
||||
// Escaping byte value 255 (=IAC) in the data is done by putting 2 of them in a row.
|
||||
//
|
||||
// So, for example:
|
||||
//
|
||||
// []byte{255} -> []byte{255, 255}
|
||||
//
|
||||
// Or, for a more complete example, if we started with the following:
|
||||
//
|
||||
// []byte{1, 55, 2, 155, 3, 255, 4, 40, 255, 30, 20}
|
||||
//
|
||||
// ... TELNET escaping would produce the following:
|
||||
//
|
||||
// []byte{1, 55, 2, 155, 3, 255, 255, 4, 40, 255, 255, 30, 20}
|
||||
//
|
||||
// (Notice that each "255" in the original byte array became 2 "255"s in a row.)
|
||||
//
|
||||
// internalDataWriter takes care of all this for you, so you do not have to do it.
|
||||
type internalDataWriter struct {
|
||||
wrapped io.Writer
|
||||
}
|
||||
|
||||
|
||||
// newDataWriter creates a new internalDataWriter writing to 'w'.
|
||||
//
|
||||
// 'w' receives what is written to the *internalDataWriter but escaped according to
|
||||
// the TELNET (and TELNETS) protocol.
|
||||
//
|
||||
// I.e., byte 255 (= IAC) gets encoded as 255, 255.
|
||||
//
|
||||
// For example, if the following it written to the *internalDataWriter's Write method:
|
||||
//
|
||||
// []byte{1, 55, 2, 155, 3, 255, 4, 40, 255, 30, 20}
|
||||
//
|
||||
// ... then (conceptually) the following is written to 'w's Write method:
|
||||
//
|
||||
// []byte{1, 55, 2, 155, 3, 255, 255, 4, 40, 255, 255, 30, 20}
|
||||
//
|
||||
// (Notice that each "255" in the original byte array became 2 "255"s in a row.)
|
||||
//
|
||||
// *internalDataWriter takes care of all this for you, so you do not have to do it.
|
||||
func newDataWriter(w io.Writer) *internalDataWriter {
|
||||
writer := internalDataWriter{
|
||||
wrapped:w,
|
||||
}
|
||||
|
||||
return &writer
|
||||
}
|
||||
|
||||
|
||||
// Write writes the TELNET (and TELNETS) escaped data for of the data in 'data' to the wrapped io.Writer.
|
||||
func (w *internalDataWriter) Write(data []byte) (n int, err error) {
|
||||
var n64 int64
|
||||
|
||||
n64, err = w.write64(data)
|
||||
n = int(n64)
|
||||
if int64(n) != n64 {
|
||||
panic(errOverflow)
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
|
||||
func (w *internalDataWriter) write64(data []byte) (n int64, err error) {
|
||||
|
||||
if len(data) <= 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
const IAC = 255
|
||||
|
||||
var buffer bytes.Buffer
|
||||
for _, datum := range data {
|
||||
|
||||
if IAC == datum {
|
||||
|
||||
if buffer.Len() > 0 {
|
||||
var numWritten int64
|
||||
|
||||
numWritten, err = oi.LongWrite(w.wrapped, buffer.Bytes())
|
||||
n += numWritten
|
||||
if nil != err {
|
||||
return n, err
|
||||
}
|
||||
buffer.Reset()
|
||||
}
|
||||
|
||||
|
||||
var numWritten int64
|
||||
//@TODO: Should we worry about "iaciac" potentially being modified by the .Write()?
|
||||
numWritten, err = oi.LongWrite(w.wrapped, iaciac)
|
||||
if int64(len(iaciac)) != numWritten {
|
||||
//@TODO: Do we really want to panic() here?
|
||||
panic(errPartialIACIACWrite)
|
||||
}
|
||||
n += 1
|
||||
if nil != err {
|
||||
return n, err
|
||||
}
|
||||
} else {
|
||||
buffer.WriteByte(datum) // The returned error is always nil, so we ignore it.
|
||||
}
|
||||
}
|
||||
|
||||
if buffer.Len() > 0 {
|
||||
var numWritten int64
|
||||
numWritten, err = oi.LongWrite(w.wrapped, buffer.Bytes())
|
||||
n += numWritten
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package telnet
|
||||
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func TestDataWriter(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
Bytes []byte
|
||||
Expected []byte
|
||||
}{
|
||||
{
|
||||
Bytes: []byte{},
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple"),
|
||||
Expected: []byte("apple"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("banana"),
|
||||
Expected: []byte("banana"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("cherry"),
|
||||
Expected: []byte("cherry"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple banana cherry"),
|
||||
Expected: []byte("apple banana cherry"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255},
|
||||
Expected: []byte{255,255},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,255},
|
||||
Expected: []byte{255,255,255,255},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,255,255},
|
||||
Expected: []byte{255,255,255,255,255,255},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,255,255,255},
|
||||
Expected: []byte{255,255,255,255,255,255,255,255},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,255,255,255,255},
|
||||
Expected: []byte{255,255,255,255,255,255,255,255,255,255},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple\xffbanana\xffcherry"),
|
||||
Expected: []byte("apple\xff\xffbanana\xff\xffcherry"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("\xffapple\xffbanana\xffcherry\xff"),
|
||||
Expected: []byte("\xff\xffapple\xff\xffbanana\xff\xffcherry\xff\xff"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple\xff\xffbanana\xff\xffcherry"),
|
||||
Expected: []byte("apple\xff\xff\xff\xffbanana\xff\xff\xff\xffcherry"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("\xff\xffapple\xff\xffbanana\xff\xffcherry\xff\xff"),
|
||||
Expected: []byte("\xff\xff\xff\xffapple\xff\xff\xff\xffbanana\xff\xff\xff\xffcherry\xff\xff\xff\xff"),
|
||||
},
|
||||
}
|
||||
|
||||
//@TODO: Add random tests.
|
||||
|
||||
|
||||
for testNumber, test := range tests {
|
||||
|
||||
subWriter := new(bytes.Buffer)
|
||||
|
||||
writer := newDataWriter(subWriter)
|
||||
|
||||
n, err := writer.Write(test.Bytes)
|
||||
if nil != err {
|
||||
t.Errorf("For test #%d, did not expected an error, but actually got one: (%T) %v; for %q -> %q.", testNumber, err, err, string(test.Bytes), string(test.Expected))
|
||||
continue
|
||||
}
|
||||
|
||||
if expected, actual := len(test.Bytes), n; expected != actual {
|
||||
t.Errorf("For test #%d, expected %d, but actually got %d; for %q -> %q.", testNumber, expected, actual, string(test.Bytes), string(test.Expected))
|
||||
continue
|
||||
}
|
||||
|
||||
if expected, actual := string(test.Expected), subWriter.String(); expected != actual {
|
||||
t.Errorf("For test #%d, expected %q, but actually got %q; for %q -> %q.", testNumber, expected, actual, string(test.Bytes), string(test.Expected))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package telnet
|
||||
|
||||
|
||||
type internalDiscardLogger struct{}
|
||||
|
||||
func (internalDiscardLogger) Debug(...interface{}) {}
|
||||
func (internalDiscardLogger) Debugf(string, ...interface{}) {}
|
||||
func (internalDiscardLogger) Debugln(...interface{}) {}
|
||||
|
||||
func (internalDiscardLogger) Error(...interface{}) {}
|
||||
func (internalDiscardLogger) Errorf(string, ...interface{}) {}
|
||||
func (internalDiscardLogger) Errorln(...interface{}) {}
|
||||
|
||||
func (internalDiscardLogger) Trace(...interface{}) {}
|
||||
func (internalDiscardLogger) Tracef(string, ...interface{}) {}
|
||||
func (internalDiscardLogger) Traceln(...interface{}) {}
|
||||
|
||||
func (internalDiscardLogger) Warn(...interface{}) {}
|
||||
func (internalDiscardLogger) Warnf(string, ...interface{}) {}
|
||||
func (internalDiscardLogger) Warnln(...interface{}) {}
|
||||
|
||||
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
Package telnet provides TELNET and TELNETS client and server implementations
|
||||
in a style similar to the "net/http" library that is part of the Go standard library,
|
||||
including support for "middleware"; TELNETS is secure TELNET, with the TELNET protocol
|
||||
over a secured TLS (or SSL) connection.
|
||||
|
||||
|
||||
Example TELNET Server
|
||||
|
||||
ListenAndServe starts a (un-secure) TELNET server with a given address and handler.
|
||||
|
||||
handler := telnet.EchoHandler
|
||||
|
||||
err := telnet.ListenAndServe(":23", handler)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
|
||||
Example TELNETS Server
|
||||
|
||||
ListenAndServeTLS starts a (secure) TELNETS server with a given address and handler,
|
||||
using the specified "cert.pem" and "key.pem" files.
|
||||
|
||||
handler := telnet.EchoHandler
|
||||
|
||||
err := telnet.ListenAndServeTLS(":992", "cert.pem", "key.pem", handler)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
|
||||
Example TELNET Client:
|
||||
|
||||
DialToAndCall creates a (un-secure) TELNET client, which connects to a given address using the specified caller.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-telnet"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var caller telnet.Caller = telnet.StandardCaller
|
||||
|
||||
//@TOOD: replace "example.net:23" with address you want to connect to.
|
||||
telnet.DialToAndCall("example.net:23", caller)
|
||||
}
|
||||
|
||||
|
||||
Example TELNETS Client:
|
||||
|
||||
DialToAndCallTLS creates a (secure) TELNETS client, which connects to a given address using the specified caller.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-telnet"
|
||||
|
||||
"crypto/tls"
|
||||
)
|
||||
|
||||
func main() {
|
||||
//@TODO: Configure the TLS connection here, if you need to.
|
||||
tlsConfig := &tls.Config{}
|
||||
|
||||
var caller telnet.Caller = telnet.StandardCaller
|
||||
|
||||
//@TOOD: replace "example.net:992" with address you want to connect to.
|
||||
telnet.DialToAndCallTLS("example.net:992", caller, tlsConfig)
|
||||
}
|
||||
|
||||
|
||||
TELNET vs TELNETS
|
||||
|
||||
If you are communicating over the open Internet, you should be using (the secure) TELNETS protocol and ListenAndServeTLS.
|
||||
|
||||
If you are communicating just on localhost, then using just (the un-secure) TELNET protocol and telnet.ListenAndServe may be OK.
|
||||
|
||||
If you are not sure which to use, use TELNETS and ListenAndServeTLS.
|
||||
|
||||
|
||||
Example TELNET Shell Server
|
||||
|
||||
The previous 2 exaple servers were very very simple. Specifically, they just echoed back whatever
|
||||
you submitted to it.
|
||||
|
||||
If you typed:
|
||||
|
||||
Apple Banana Cherry\r\n
|
||||
|
||||
... it would send back:
|
||||
|
||||
Apple Banana Cherry\r\n
|
||||
|
||||
(Exactly the same data you sent it.)
|
||||
|
||||
A more useful TELNET server can be made using the "github.com/reiver/go-telnet/telsh" sub-package.
|
||||
|
||||
The `telsh` sub-package provides "middleware" that enables you to create a "shell" interface (also
|
||||
called a "command line interface" or "CLI") which most people would expect when using TELNET OR TELNETS.
|
||||
|
||||
For example:
|
||||
|
||||
|
||||
package main
|
||||
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-oi"
|
||||
"github.com/reiver/go-telnet"
|
||||
"github.com/reiver/go-telnet/telsh"
|
||||
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
func main() {
|
||||
|
||||
shellHandler := telsh.NewShellHandler()
|
||||
|
||||
commandName := "date"
|
||||
shellHandler.Register(commandName, danceProducer)
|
||||
|
||||
commandName = "animate"
|
||||
shellHandler.Register(commandName, animateProducer)
|
||||
|
||||
addr := ":23"
|
||||
if err := telnet.ListenAndServe(addr, shellHandler); nil != err {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
Note that in the example, so far, we have registered 2 commands: `date` and `animate`.
|
||||
|
||||
For this to actually work, we need to have code for the `date` and `animate` commands.
|
||||
|
||||
The actual implemenation for the `date` command could be done like the following:
|
||||
|
||||
func dateHandlerFunc(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
const layout = "Mon Jan 2 15:04:05 -0700 MST 2006"
|
||||
s := time.Now().Format(layout)
|
||||
|
||||
if _, err := oi.LongWriteString(stdout, s); nil != err {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func dateProducerFunc(ctx telnet.Context, name string, args ...string) telsh.Handler{
|
||||
return telsh.PromoteHandlerFunc(dateHandler)
|
||||
}
|
||||
|
||||
|
||||
var dateProducer = ProducerFunc(dateProducerFunc)
|
||||
|
||||
Note that your "real" work is in the `dateHandlerFunc` func.
|
||||
|
||||
And the actual implementation for the `animate` command could be done as follows:
|
||||
|
||||
func animateHandlerFunc(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
|
||||
for i:=0; i<20; i++ {
|
||||
oi.LongWriteString(stdout, "\r⠋")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠙")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠹")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠸")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠼")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠴")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠦")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠧")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠇")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠏")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
}
|
||||
oi.LongWriteString(stdout, "\r \r\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func animateProducerFunc(ctx telnet.Context, name string, args ...string) telsh.Handler{
|
||||
return telsh.PromoteHandlerFunc(animateHandler)
|
||||
}
|
||||
|
||||
|
||||
var animateProducer = ProducerFunc(animateProducerFunc)
|
||||
|
||||
Again, note that your "real" work is in the `animateHandlerFunc` func.
|
||||
|
||||
Generating PEM Files
|
||||
|
||||
If you are using the telnet.ListenAndServeTLS func or the telnet.Server.ListenAndServeTLS method, you will need to
|
||||
supply "cert.pem" and "key.pem" files.
|
||||
|
||||
If you do not already have these files, the Go soure code contains a tool for generating these files for you.
|
||||
|
||||
It can be found at:
|
||||
|
||||
$GOROOT/src/crypto/tls/generate_cert.go
|
||||
|
||||
So, for example, if your `$GOROOT` is the "/usr/local/go" directory, then it would be at:
|
||||
|
||||
/usr/local/go/src/crypto/tls/generate_cert.go
|
||||
|
||||
If you run the command:
|
||||
|
||||
go run $GOROOT/src/crypto/tls/generate_cert.go --help
|
||||
|
||||
... then you get the help information for "generate_cert.go".
|
||||
|
||||
Of course, you would replace or set `$GOROOT` with whatever your path actually is. Again, for example,
|
||||
if your `$GOROOT` is the "/usr/local/go" directory, then it would be:
|
||||
|
||||
go run /usr/local/go/src/crypto/tls/generate_cert.go --help
|
||||
|
||||
To demonstrate the usage of "generate_cert.go", you might run the following to generate certificates
|
||||
that were bound to the hosts `127.0.0.1` and `localhost`:
|
||||
|
||||
go run /usr/local/go/src/crypto/tls/generate_cert.go --ca --host='127.0.0.1,localhost'
|
||||
|
||||
|
||||
If you are not sure where "generate_cert.go" is on your computer, on Linux and Unix based systems, you might
|
||||
be able to find the file with the command:
|
||||
|
||||
locate /src/crypto/tls/generate_cert.go
|
||||
|
||||
(If it finds it, it should output the full path to this file.)
|
||||
|
||||
|
||||
Example TELNET Client
|
||||
|
||||
You can make a simple (un-secure) TELNET client with code like the following:
|
||||
|
||||
package main
|
||||
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-telnet"
|
||||
)
|
||||
|
||||
|
||||
func main() {
|
||||
var caller telnet.Caller = telnet.StandardCaller
|
||||
|
||||
//@TOOD: replace "example.net:5555" with address you want to connect to.
|
||||
telnet.DialToAndCall("example.net:5555", caller)
|
||||
}
|
||||
|
||||
|
||||
Example TELNETS Client
|
||||
|
||||
You can make a simple (secure) TELNETS client with code like the following:
|
||||
|
||||
package main
|
||||
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-telnet"
|
||||
)
|
||||
|
||||
|
||||
func main() {
|
||||
var caller telnet.Caller = telnet.StandardCaller
|
||||
|
||||
//@TOOD: replace "example.net:5555" with address you want to connect to.
|
||||
telnet.DialToAndCallTLS("example.net:5555", caller)
|
||||
}
|
||||
|
||||
|
||||
TELNET Story
|
||||
|
||||
The TELNET protocol is best known for providing a means of connecting to a remote computer, using a (text-based) shell interface, and being able to interact with it, (more or less) as if you were sitting at that computer.
|
||||
|
||||
(Shells are also known as command-line interfaces or CLIs.)
|
||||
|
||||
Although this was the original usage of the TELNET protocol, it can be (and is) used for other purposes as well.
|
||||
|
||||
|
||||
The Era
|
||||
|
||||
The TELNET protocol came from an era in computing when text-based shell interface where the common way of interacting with computers.
|
||||
|
||||
The common interface for computers during this era was a keyboard and a monochromatic (i.e., single color) text-based monitors called "video terminals".
|
||||
|
||||
(The word "video" in that era of computing did not refer to things such as movies. But instead was meant to contrast it with paper. In particular, the teletype machines, which were typewriter like devices that had a keyboard, but instead of having a monitor had paper that was printed onto.)
|
||||
|
||||
|
||||
Early Office Computers
|
||||
|
||||
In that era, in the early days of office computers, it was rare that an individual would have a computer at their desk. (A single computer was much too expensive.)
|
||||
|
||||
Instead, there would be a single central computer that everyone would share. The style of computer used (for the single central shared computer) was called a "mainframe".
|
||||
|
||||
What individuals would have at their desks, instead of their own compuer, would be some type of video terminal.
|
||||
|
||||
The different types of video terminals had named such as:
|
||||
|
||||
• VT52
|
||||
|
||||
• VT100
|
||||
|
||||
• VT220
|
||||
|
||||
• VT240
|
||||
|
||||
("VT" in those named was short for "video terminal".)
|
||||
|
||||
|
||||
Teletype
|
||||
|
||||
To understand this era, we need to go back a bit in time to what came before it: teletypes.
|
||||
|
||||
|
||||
Terminal Codes
|
||||
|
||||
Terminal codes (also sometimes called 'terminal control codes') are used to issue various kinds of commands
|
||||
to the terminal.
|
||||
|
||||
(Note that 'terminal control codes' are a completely separate concept for 'TELNET commands',
|
||||
and the two should NOT be conflated or confused.)
|
||||
|
||||
The most common types of 'terminal codes' are the 'ANSI escape codes'. (Although there are other types too.)
|
||||
|
||||
|
||||
ANSI Escape Codes
|
||||
|
||||
ANSI escape codes (also sometimes called 'ANSI escape sequences') are a common type of 'terminal code' used
|
||||
to do things such as:
|
||||
|
||||
• moving the cursor,
|
||||
|
||||
• erasing the display,
|
||||
|
||||
• erasing the line,
|
||||
|
||||
• setting the graphics mode,
|
||||
|
||||
• setting the foregroup color,
|
||||
|
||||
• setting the background color,
|
||||
|
||||
• setting the screen resolution, and
|
||||
|
||||
• setting keyboard strings.
|
||||
|
||||
|
||||
Setting The Foreground Color With ANSI Escape Codes
|
||||
|
||||
One of the abilities of ANSI escape codes is to set the foreground color.
|
||||
|
||||
Here is a table showing codes for this:
|
||||
|
||||
| ANSI Color | Go string | Go []byte |
|
||||
| ------------ | ---------- | ----------------------------- |
|
||||
| Black | "\x1b[30m" | []byte{27, '[', '3','0', 'm'} |
|
||||
| Red | "\x1b[31m" | []byte{27, '[', '3','1', 'm'} |
|
||||
| Green | "\x1b[32m" | []byte{27, '[', '3','2', 'm'} |
|
||||
| Brown/Yellow | "\x1b[33m" | []byte{27, '[', '3','3', 'm'} |
|
||||
| Blue | "\x1b[34m" | []byte{27, '[', '3','4', 'm'} |
|
||||
| Magenta | "\x1b[35m" | []byte{27, '[', '3','5', 'm'} |
|
||||
| Cyan | "\x1b[36m" | []byte{27, '[', '3','6', 'm'} |
|
||||
| Gray/White | "\x1b[37m" | []byte{27, '[', '3','7', 'm'} |
|
||||
|
||||
(Note that in the `[]byte` that the first `byte` is the number `27` (which
|
||||
is the "escape" character) where the third and fouth characters are the
|
||||
**not** number literals, but instead character literals `'3'` and whatever.)
|
||||
|
||||
|
||||
Setting The Background Color With ANSI Escape Codes
|
||||
|
||||
Another of the abilities of ANSI escape codes is to set the background color.
|
||||
|
||||
| ANSI Color | Go string | Go []byte |
|
||||
| ------------ | ---------- | ----------------------------- |
|
||||
| Black | "\x1b[40m" | []byte{27, '[', '4','0', 'm'} |
|
||||
| Red | "\x1b[41m" | []byte{27, '[', '4','1', 'm'} |
|
||||
| Green | "\x1b[42m" | []byte{27, '[', '4','2', 'm'} |
|
||||
| Brown/Yellow | "\x1b[43m" | []byte{27, '[', '4','3', 'm'} |
|
||||
| Blue | "\x1b[44m" | []byte{27, '[', '4','4', 'm'} |
|
||||
| Magenta | "\x1b[45m" | []byte{27, '[', '4','5', 'm'} |
|
||||
| Cyan | "\x1b[46m" | []byte{27, '[', '4','6', 'm'} |
|
||||
| Gray/White | "\x1b[47m" | []byte{27, '[', '4','7', 'm'} |
|
||||
|
||||
(Note that in the `[]byte` that the first `byte` is the number `27` (which
|
||||
is the "escape" character) where the third and fouth characters are the
|
||||
**not** number literals, but instead character literals `'4'` and whatever.)
|
||||
|
||||
Using ANSI Escape Codes
|
||||
|
||||
In Go code, if I wanted to use an ANSI escape code to use a blue background,
|
||||
a white foreground, and bold, I could do that with the ANSI escape code:
|
||||
|
||||
"\x1b[44;37;1m"
|
||||
|
||||
Note that that start with byte value 27, which we have encoded as hexadecimal
|
||||
as \x1b. Followed by the '[' character.
|
||||
|
||||
Coming after that is the sub-string "44", which is the code that sets our background color to blue.
|
||||
|
||||
We follow that with the ';' character (which separates codes).
|
||||
|
||||
And the after that comes the sub-string "37", which is the code that set our foreground color to white.
|
||||
|
||||
After that, we follow with another ";" character (which, again, separates codes).
|
||||
|
||||
And then we follow it the sub-string "1", which is the code that makes things bold.
|
||||
|
||||
And finally, the ANSI escape sequence is finished off with the 'm' character.
|
||||
|
||||
To show this in a more complete example, our `dateHandlerFunc` from before could incorporate ANSI escape sequences as follows:
|
||||
|
||||
func dateHandlerFunc(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
const layout = "Mon Jan 2 15:04:05 -0700 MST 2006"
|
||||
s := "\x1b[44;37;1m" + time.Now().Format(layout) + "\x1b[0m"
|
||||
|
||||
if _, err := oi.LongWriteString(stdout, s); nil != err {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Note that in that example, in addition to using the ANSI escape sequence "\x1b[44;37;1m"
|
||||
to set the background color to blue, set the foreground color to white, and make it bold,
|
||||
we also used the ANSI escape sequence "\x1b[0m" to reset the background and foreground colors
|
||||
and boldness back to "normal".
|
||||
|
||||
*/
|
||||
package telnet
|
||||
@@ -0,0 +1,33 @@
|
||||
package telnet
|
||||
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-oi"
|
||||
)
|
||||
|
||||
|
||||
// EchoHandler is a simple TELNET server which "echos" back to the client any (non-command)
|
||||
// data back to the TELNET client, it received from the TELNET client.
|
||||
var EchoHandler Handler = internalEchoHandler{}
|
||||
|
||||
|
||||
type internalEchoHandler struct{}
|
||||
|
||||
|
||||
func (handler internalEchoHandler) ServeTELNET(ctx Context, w Writer, r Reader) {
|
||||
|
||||
var buffer [1]byte // Seems like the length of the buffer needs to be small, otherwise will have to wait for buffer to fill up.
|
||||
p := buffer[:]
|
||||
|
||||
for {
|
||||
n, err := r.Read(p)
|
||||
|
||||
if n > 0 {
|
||||
oi.LongWrite(w, p[:n])
|
||||
}
|
||||
|
||||
if nil != err {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
package telnet
|
||||
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func TestEchoHandler(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
Bytes []byte
|
||||
Expected []byte
|
||||
}{
|
||||
{
|
||||
Bytes: []byte{},
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("a"),
|
||||
Expected: []byte("a"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("b"),
|
||||
Expected: []byte("b"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("c"),
|
||||
Expected: []byte("c"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple"),
|
||||
Expected: []byte("apple"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("banana"),
|
||||
Expected: []byte("banana"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("cherry"),
|
||||
Expected: []byte("cherry"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple banana cherry"),
|
||||
Expected: []byte("apple banana cherry"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255, 255},
|
||||
Expected: []byte{255, 255},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, 255},
|
||||
Expected: []byte{255, 255, 255, 255},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, 255, 255, 255},
|
||||
Expected: []byte{255, 255, 255, 255, 255, 255},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, 255, 255, 255, 255, 255},
|
||||
Expected: []byte{255, 255, 255, 255, 255, 255, 255, 255},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
Expected: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple\xff\xffbanana\xff\xffcherry"),
|
||||
Expected: []byte("apple\xff\xffbanana\xff\xffcherry"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("\xff\xffapple\xff\xffbanana\xff\xffcherry\xff\xff"),
|
||||
Expected: []byte("\xff\xffapple\xff\xffbanana\xff\xffcherry\xff\xff"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple\xff\xff\xff\xffbanana\xff\xff\xff\xffcherry"),
|
||||
Expected: []byte("apple\xff\xff\xff\xffbanana\xff\xff\xff\xffcherry"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("\xff\xff\xff\xffapple\xff\xff\xff\xffbanana\xff\xff\xff\xffcherry\xff\xff\xff\xff"),
|
||||
Expected: []byte("\xff\xff\xff\xffapple\xff\xff\xff\xffbanana\xff\xff\xff\xffcherry\xff\xff\xff\xff"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255,251,24}, // IAC WILL TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,252,24}, // IAC WON'T TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,253,24}, // IAC DO TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,254,24}, // IAC DON'T TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255,251,24}, // 'C' IAC WILL TERMINAL-TYPE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,252,24}, // 'C' IAC WON'T TERMINAL-TYPE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,253,24}, // 'C' IAC DO TERMINAL-TYPE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,254,24}, // 'C' IAC DON'T TERMINAL-TYPE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255,251,24, 68}, // IAC WILL TERMINAL-TYPE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,252,24, 68}, // IAC WON'T TERMINAL-TYPE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,253,24, 68}, // IAC DO TERMINAL-TYPE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,254,24, 68}, // IAC DON'T TERMINAL-TYPE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255,251,24, 68}, // 'C' IAC WILL TERMINAL-TYPE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,252,24, 68}, // 'C' IAC WON'T TERMINAL-TYPE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,253,24, 68}, // 'C' IAC DO TERMINAL-TYPE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,254,24, 68}, // 'C' IAC DON'T TERMINAL-TYPE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255, 250, 24, 1, 255, 240}, // IAC SB TERMINAL-TYPE SEND IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 250, 24, 0, 68,69,67,45,86,84,53,50 ,255, 240}, // IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 1, 255, 240}, // 'C' IAC SB TERMINAL-TYPE SEND IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 0, 68,69,67,45,86,84,53,50 ,255, 240}, // 'C' IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255, 250, 24, 1, 255, 240, 68}, // IAC SB TERMINAL-TYPE SEND IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 250, 24, 0, 68,69,67,45,86,84,53,50 ,255, 240, 68}, // IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 1, 255, 240, 68}, // 'C' IAC SB TERMINAL-TYPE SEND IAC SE 'D'
|
||||
Expected: []byte{67, 68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 0, 68,69,67,45,86,84,53,50 ,255, 240, 68}, // 'C' IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE 'D'
|
||||
Expected: []byte{67, 68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255,250, 0,1,2,3,4,5,6,7,8,9,10,11,12,13 ,255,240}, // IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 0,1,2,3,4,5,6,7,8,9,10,11,12,13 ,255,240}, // 'C' IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,250, 0,1,2,3,4,5,6,7,8,9,10,11,12,13 ,255,240, 68}, // IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 0,1,2,3,4,5,6,7,8,9,10,11,12,13 ,255,240, 68}, // 'C' IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
//@TODO: Is this correct? Can IAC appear between thee 'IAC SB' and ''IAC SE'?... and if "yes", do escaping rules apply?
|
||||
{
|
||||
Bytes: []byte{ 255,250, 255,255,240 ,255,240}, // IAC SB 255 255 240 IAC SE = IAC SB IAC IAC SE IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 255,255,240 ,255,240}, // 'C' IAC SB 255 255 240 IAC SE = IAC SB IAC IAC SE IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,250, 255,255,240 ,255,240, 68}, // IAC SB 255 255 240 IAC SE = IAC SB IAC IAC SE IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 255,255,240 ,255,240, 68}, // 'C' IAC SB 255 255 240 IAC SE = IAC SB IAC IAC SE IAC SE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
//@TODO: Is this correct? Can IAC appear between thee 'IAC SB' and ''IAC SE'?... and if "yes", do escaping rules apply?
|
||||
{
|
||||
Bytes: []byte{ 255,250, 71,255,255,240 ,255,240}, // IAC SB 'G' 255 255 240 IAC SE = IAC SB 'G' IAC IAC SE IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 71,255,255,240 ,255,240}, // 'C' IAC SB 'G' 255 255 240 IAC SE = IAC SB 'G' IAC IAC SE IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,250, 71,255,255,240 ,255,240, 68}, // IAC SB 'G' 255 255 240 IAC SE = IAC SB 'G' IAC IAC SE IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 71,255,255,240 ,255,240, 68}, // 'C' IAC SB 'G' 255 255 240 IAC SE = IAC 'G' SB IAC IAC SE IAC SE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
//@TODO: Is this correct? Can IAC appear between thee 'IAC SB' and ''IAC SE'?... and if "yes", do escaping rules apply?
|
||||
{
|
||||
Bytes: []byte{ 255,250, 255,255,240,72 ,255,240}, // IAC SB 255 255 240 'H' IAC SE = IAC SB IAC IAC SE 'H' IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 255,255,240,72 ,255,240}, // 'C' IAC SB 255 255 240 'H' IAC SE = IAC SB IAC IAC SE 'H' IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,250, 255,255,240,72 ,255,240, 68}, // IAC SB 255 255 240 'H' IAC SE = IAC SB IAC IAC SE 'H' IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 255,255,240,72 ,255,240, 68}, // 'C' IAC SB 255 255 240 'H' IAC SE = IAC SB IAC IAC SE 'H' IAC SE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
//@TODO: Is this correct? Can IAC appear between thee 'IAC SB' and ''IAC SE'?... and if "yes", do escaping rules apply?
|
||||
{
|
||||
Bytes: []byte{ 255,250, 71,255,255,240,72 ,255,240}, // IAC SB 'G' 255 255 240 'H' IAC SE = IAC SB 'G' IAC IAC SE 'H' IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 71,255,255,240,72 ,255,240}, // 'C' IAC SB 'G' 255 255 240 'H' IAC SE = IAC SB 'G' IAC IAC SE 'H' IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,250, 71,255,255,240,72 ,255,240, 68}, // IAC SB 'G' 255 255 240 'H' IAC SE = IAC SB 'G' IAC IAC SE 'H' IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 71,255,255,240,72 ,255,240, 68}, // 'C' IAC SB 'G' 255 255 240 'H' IAC SE = IAC 'G' SB IAC IAC SE 'H' IAC SE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
for testNumber, test := range tests {
|
||||
var ctx Context = nil
|
||||
|
||||
var buffer bytes.Buffer
|
||||
|
||||
writer := newDataWriter(&buffer)
|
||||
reader := newDataReader( bytes.NewReader(test.Bytes) )
|
||||
|
||||
EchoHandler.ServeTELNET(ctx, writer, reader)
|
||||
|
||||
if expected, actual := string(test.Expected), buffer.String(); expected != actual {
|
||||
t.Errorf("For test #%d, expected %q, but actually got %q.", testNumber, expected, actual)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package telnet
|
||||
|
||||
|
||||
// A Handler serves a TELNET (or TELNETS) connection.
|
||||
//
|
||||
// Writing data to the Writer passed as an argument to the ServeTELNET method
|
||||
// will send data to the TELNET (or TELNETS) client.
|
||||
//
|
||||
// Reading data from the Reader passed as an argument to the ServeTELNET method
|
||||
// will receive data from the TELNET client.
|
||||
//
|
||||
// The Writer's Write method sends "escaped" TELNET (and TELNETS) data.
|
||||
//
|
||||
// The Reader's Read method "un-escapes" TELNET (and TELNETS) data, and filters
|
||||
// out TELNET (and TELNETS) command sequences.
|
||||
type Handler interface {
|
||||
ServeTELNET(Context, Writer, Reader)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package telnet
|
||||
|
||||
|
||||
type Logger interface{
|
||||
Debug(...interface{})
|
||||
Debugf(string, ...interface{})
|
||||
|
||||
Error(...interface{})
|
||||
Errorf(string, ...interface{})
|
||||
|
||||
Trace(...interface{})
|
||||
Tracef(string, ...interface{})
|
||||
|
||||
Warn(...interface{})
|
||||
Warnf(string, ...interface{})
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package telnet
|
||||
|
||||
|
||||
type Reader interface {
|
||||
Read([]byte) (int, error)
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package telnet
|
||||
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
)
|
||||
|
||||
|
||||
// ListenAndServe listens on the TCP network address `addr` and then spawns a call to the ServeTELNET
|
||||
// method on the `handler` to serve each incoming connection.
|
||||
//
|
||||
// For a very simple example:
|
||||
//
|
||||
// package main
|
||||
//
|
||||
// import (
|
||||
// "github.com/reiver/go-telnet"
|
||||
// )
|
||||
//
|
||||
// func main() {
|
||||
//
|
||||
// //@TODO: In your code, you would probably want to use a different handler.
|
||||
// var handler telnet.Handler = telnet.EchoHandler
|
||||
//
|
||||
// err := telnet.ListenAndServe(":5555", handler)
|
||||
// if nil != err {
|
||||
// //@TODO: Handle this error better.
|
||||
// panic(err)
|
||||
// }
|
||||
// }
|
||||
func ListenAndServe(addr string, handler Handler) error {
|
||||
server := &Server{Addr: addr, Handler: handler}
|
||||
return server.ListenAndServe()
|
||||
}
|
||||
|
||||
|
||||
// Serve accepts an incoming TELNET or TELNETS client connection on the net.Listener `listener`.
|
||||
func Serve(listener net.Listener, handler Handler) error {
|
||||
|
||||
server := &Server{Handler: handler}
|
||||
return server.Serve(listener)
|
||||
}
|
||||
|
||||
|
||||
// A Server defines parameters of a running TELNET server.
|
||||
//
|
||||
// For a simple example:
|
||||
//
|
||||
// package main
|
||||
//
|
||||
// import (
|
||||
// "github.com/reiver/go-telnet"
|
||||
// )
|
||||
//
|
||||
// func main() {
|
||||
//
|
||||
// var handler telnet.Handler = telnet.EchoHandler
|
||||
//
|
||||
// server := &telnet.Server{
|
||||
// Addr:":5555",
|
||||
// Handler:handler,
|
||||
// }
|
||||
//
|
||||
// err := server.ListenAndServe()
|
||||
// if nil != err {
|
||||
// //@TODO: Handle this error better.
|
||||
// panic(err)
|
||||
// }
|
||||
// }
|
||||
type Server struct {
|
||||
Addr string // TCP address to listen on; ":telnet" or ":telnets" if empty (when used with ListenAndServe or ListenAndServeTLS respectively).
|
||||
Handler Handler // handler to invoke; telnet.EchoServer if nil
|
||||
|
||||
TLSConfig *tls.Config // optional TLS configuration; used by ListenAndServeTLS.
|
||||
|
||||
Logger Logger
|
||||
}
|
||||
|
||||
// ListenAndServe listens on the TCP network address 'server.Addr' and then spawns a call to the ServeTELNET
|
||||
// method on the 'server.Handler' to serve each incoming connection.
|
||||
//
|
||||
// For a simple example:
|
||||
//
|
||||
// package main
|
||||
//
|
||||
// import (
|
||||
// "github.com/reiver/go-telnet"
|
||||
// )
|
||||
//
|
||||
// func main() {
|
||||
//
|
||||
// var handler telnet.Handler = telnet.EchoHandler
|
||||
//
|
||||
// server := &telnet.Server{
|
||||
// Addr:":5555",
|
||||
// Handler:handler,
|
||||
// }
|
||||
//
|
||||
// err := server.ListenAndServe()
|
||||
// if nil != err {
|
||||
// //@TODO: Handle this error better.
|
||||
// panic(err)
|
||||
// }
|
||||
// }
|
||||
func (server *Server) ListenAndServe() error {
|
||||
|
||||
addr := server.Addr
|
||||
if "" == addr {
|
||||
addr = ":telnet"
|
||||
}
|
||||
|
||||
|
||||
listener, err := net.Listen("tcp", addr)
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
return server.Serve(listener)
|
||||
}
|
||||
|
||||
|
||||
// Serve accepts an incoming TELNET client connection on the net.Listener `listener`.
|
||||
func (server *Server) Serve(listener net.Listener) error {
|
||||
|
||||
defer listener.Close()
|
||||
|
||||
|
||||
logger := server.logger()
|
||||
|
||||
|
||||
handler := server.Handler
|
||||
if nil == handler {
|
||||
//@TODO: Should this be a "ShellHandler" instead, that gives a shell-like experience by default
|
||||
// If this is changd, then need to change the comment in the "type Server struct" definition.
|
||||
logger.Debug("Defaulted handler to EchoHandler.")
|
||||
handler = EchoHandler
|
||||
}
|
||||
|
||||
|
||||
for {
|
||||
// Wait for a new TELNET client connection.
|
||||
logger.Debugf("Listening at %q.", listener.Addr())
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
//@TODO: Could try to recover from certain kinds of errors. Maybe waiting a while before trying again.
|
||||
return err
|
||||
}
|
||||
logger.Debugf("Received new connection from %q.", conn.RemoteAddr())
|
||||
|
||||
// Handle the new TELNET client connection by spawning
|
||||
// a new goroutine.
|
||||
go server.handle(conn, handler)
|
||||
logger.Debugf("Spawned handler to handle connection from %q.", conn.RemoteAddr())
|
||||
}
|
||||
}
|
||||
|
||||
func (server *Server) handle(c net.Conn, handler Handler) {
|
||||
defer c.Close()
|
||||
|
||||
logger := server.logger()
|
||||
|
||||
|
||||
defer func(){
|
||||
if r := recover(); nil != r {
|
||||
if nil != logger {
|
||||
logger.Errorf("Recovered from: (%T) %v", r, r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
var ctx Context = NewContext().InjectLogger(logger)
|
||||
|
||||
var w Writer = newDataWriter(c)
|
||||
var r Reader = newDataReader(c)
|
||||
|
||||
handler.ServeTELNET(ctx, w, r)
|
||||
c.Close()
|
||||
}
|
||||
|
||||
|
||||
|
||||
func (server *Server) logger() Logger {
|
||||
logger := server.Logger
|
||||
if nil == logger {
|
||||
logger = internalDiscardLogger{}
|
||||
}
|
||||
|
||||
return logger
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package telnet
|
||||
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-oi"
|
||||
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
// StandardCaller is a simple TELNET client which sends to the server any data it gets from os.Stdin
|
||||
// as TELNET (and TELNETS) data, and writes any TELNET (or TELNETS) data it receives from
|
||||
// the server to os.Stdout, and writes any error it has to os.Stderr.
|
||||
var StandardCaller Caller = internalStandardCaller{}
|
||||
|
||||
|
||||
type internalStandardCaller struct{}
|
||||
|
||||
|
||||
func (caller internalStandardCaller) CallTELNET(ctx Context, w Writer, r Reader) {
|
||||
standardCallerCallTELNET(os.Stdin, os.Stdout, os.Stderr, ctx, w, r)
|
||||
}
|
||||
|
||||
|
||||
func standardCallerCallTELNET(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, ctx Context, w Writer, r Reader) {
|
||||
|
||||
go func(writer io.Writer, reader io.Reader) {
|
||||
|
||||
var buffer [1]byte // Seems like the length of the buffer needs to be small, otherwise will have to wait for buffer to fill up.
|
||||
p := buffer[:]
|
||||
|
||||
for {
|
||||
// Read 1 byte.
|
||||
n, err := reader.Read(p)
|
||||
if n <= 0 && nil == err {
|
||||
continue
|
||||
} else if n <= 0 && nil != err {
|
||||
break
|
||||
}
|
||||
|
||||
oi.LongWrite(writer, p)
|
||||
}
|
||||
}(stdout, r)
|
||||
|
||||
|
||||
|
||||
var buffer bytes.Buffer
|
||||
var p []byte
|
||||
|
||||
var crlfBuffer [2]byte = [2]byte{'\r','\n'}
|
||||
crlf := crlfBuffer[:]
|
||||
|
||||
scanner := bufio.NewScanner(stdin)
|
||||
scanner.Split(scannerSplitFunc)
|
||||
|
||||
for scanner.Scan() {
|
||||
buffer.Write(scanner.Bytes())
|
||||
buffer.Write(crlf)
|
||||
|
||||
p = buffer.Bytes()
|
||||
|
||||
n, err := oi.LongWrite(w, p)
|
||||
if nil != err {
|
||||
break
|
||||
}
|
||||
if expected, actual := int64(len(p)), n; expected != actual {
|
||||
err := fmt.Errorf("Transmission problem: tried sending %d bytes, but actually only sent %d bytes.", expected, actual)
|
||||
fmt.Fprint(stderr, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
buffer.Reset()
|
||||
}
|
||||
|
||||
// Wait a bit to receive data from the server (that we would send to io.Stdout).
|
||||
time.Sleep(3 * time.Millisecond)
|
||||
}
|
||||
|
||||
|
||||
func scannerSplitFunc(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||
if atEOF {
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
return bufio.ScanLines(data, atEOF)
|
||||
}
|
||||
@@ -0,0 +1,974 @@
|
||||
package telnet
|
||||
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-oi"
|
||||
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func TestStandardCallerFromClientToServer(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
Bytes []byte
|
||||
Expected []byte
|
||||
}{
|
||||
{
|
||||
Bytes: []byte{},
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("a"),
|
||||
Expected: []byte(""),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("b"),
|
||||
Expected: []byte(""),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("c"),
|
||||
Expected: []byte(""),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("a\n"),
|
||||
Expected: []byte("a\r\n"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("b\n"),
|
||||
Expected: []byte("b\r\n"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("c\n"),
|
||||
Expected: []byte("c\r\n"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("a\nb\nc"),
|
||||
Expected: []byte("a\r\nb\r\n"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("a\nb\nc\n"),
|
||||
Expected: []byte("a\r\nb\r\nc\r\n"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple"),
|
||||
Expected: []byte(""),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("banana"),
|
||||
Expected: []byte(""),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("cherry"),
|
||||
Expected: []byte(""),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple\n"),
|
||||
Expected: []byte("apple\r\n"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("banana\n"),
|
||||
Expected: []byte("banana\r\n"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("cherry\n"),
|
||||
Expected: []byte("cherry\r\n"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple\nbanana\ncherry"),
|
||||
Expected: []byte("apple\r\nbanana\r\n"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple\nbanana\ncherry\n"),
|
||||
Expected: []byte("apple\r\nbanana\r\ncherry\r\n"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple banana cherry"),
|
||||
Expected: []byte(""),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple banana cherry\n"),
|
||||
Expected: []byte("apple banana cherry\r\n"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255},
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255},
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255},
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, 255},
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, 255, 255},
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, 255, 255, 255},
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, 255, 255, 255, 255},
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, 255, 255, 255, 255, 255},
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255, '\n'},
|
||||
Expected: []byte{255, 255, '\r', '\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, '\n'},
|
||||
Expected: []byte{255, 255, 255, 255, '\r', '\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, '\n'},
|
||||
Expected: []byte{255, 255, 255, 255, 255, 255, '\r', '\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, 255, '\n'},
|
||||
Expected: []byte{255, 255, 255, 255, 255, 255, 255, 255, '\r', '\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, 255, 255, '\n'},
|
||||
Expected: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, '\r', '\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, 255, 255, 255, '\n'},
|
||||
Expected: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, '\r', '\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, 255, 255, 255, 255, '\n'},
|
||||
Expected: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, '\r', '\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, 255, 255, 255, 255, 255, '\n'},
|
||||
Expected: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, '\r', '\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, '\n'},
|
||||
Expected: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, '\r', '\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, '\n'},
|
||||
Expected: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, '\r', '\n'},
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple\xff\xffbanana\xff\xffcherry"),
|
||||
Expected: []byte(""),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("\xff\xffapple\xff\xffbanana\xff\xffcherry\xff\xff"),
|
||||
Expected: []byte(""),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple\xffbanana\xffcherry\n"),
|
||||
Expected: []byte("apple\xff\xffbanana\xff\xffcherry\r\n"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("\xffapple\xffbanana\xffcherry\xff\n"),
|
||||
Expected: []byte("\xff\xffapple\xff\xffbanana\xff\xffcherry\xff\xff\r\n"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple\xff\xff\xff\xffbanana\xff\xff\xff\xffcherry"),
|
||||
Expected: []byte(""),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("\xff\xff\xff\xffapple\xff\xff\xff\xffbanana\xff\xff\xff\xffcherry\xff\xff\xff\xff"),
|
||||
Expected: []byte(""),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple\xff\xffbanana\xff\xffcherry\n"),
|
||||
Expected: []byte("apple\xff\xff\xff\xffbanana\xff\xff\xff\xffcherry\r\n"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("\xff\xffapple\xff\xffbanana\xff\xffcherry\xff\xff\n"),
|
||||
Expected: []byte("\xff\xff\xff\xffapple\xff\xff\xff\xffbanana\xff\xff\xff\xffcherry\xff\xff\xff\xff\r\n"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255,251,24}, // IAC WILL TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,252,24}, // IAC WON'T TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,253,24}, // IAC DO TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,254,24}, // IAC DON'T TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{ 255,251,24, '\n'}, // IAC WILL TERMINAL-TYPE '\n'
|
||||
Expected: []byte{255,255,251,24,'\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,252,24, '\n'}, // IAC WON'T TERMINAL-TYPE '\n'
|
||||
Expected: []byte{255,255,252,24,'\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,253,24, '\n'}, // IAC DO TERMINAL-TYPE '\n'
|
||||
Expected: []byte{255,255,253,24,'\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,254,24, '\n'}, // IAC DON'T TERMINAL-TYPE '\n'
|
||||
Expected: []byte{255,255,254,24,'\r','\n'},
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255,251,24}, // 'C' IAC WILL TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,252,24}, // 'C' IAC WON'T TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,253,24}, // 'C' IAC DO TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,254,24}, // 'C' IAC DON'T TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255,251,24, '\n'}, // 'C' IAC WILL TERMINAL-TYPE '\n'
|
||||
Expected: []byte{67, 255,255,251,24, '\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,252,24, '\n'}, // 'C' IAC WON'T TERMINAL-TYPE '\n'
|
||||
Expected: []byte{67, 255,255,252,24, '\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,253,24, '\n'}, // 'C' IAC DO TERMINAL-TYPE '\n'
|
||||
Expected: []byte{67, 255,255,253,24, '\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,254,24, '\n'}, // 'C' IAC DON'T TERMINAL-TYPE '\n'
|
||||
Expected: []byte{67, 255,255,254,24, '\r','\n'},
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255,251,24, 68}, // IAC WILL TERMINAL-TYPE 'D'
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,252,24, 68}, // IAC WON'T TERMINAL-TYPE 'D'
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,253,24, 68}, // IAC DO TERMINAL-TYPE 'D'
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,254,24, 68}, // IAC DON'T TERMINAL-TYPE 'D'
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{ 255,251,24, 68, '\n'}, // IAC WILL TERMINAL-TYPE 'D' '\n'
|
||||
Expected: []byte{255,255,251,24, 68, '\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,252,24, 68, '\n'}, // IAC WON'T TERMINAL-TYPE 'D' '\n'
|
||||
Expected: []byte{255,255,252,24, 68, '\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,253,24, 68, '\n'}, // IAC DO TERMINAL-TYPE 'D' '\n'
|
||||
Expected: []byte{255,255,253,24, 68, '\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,254,24, 68, '\n'}, // IAC DON'T TERMINAL-TYPE 'D' '\n'
|
||||
Expected: []byte{255,255,254,24, 68, '\r','\n'},
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255,251,24, 68}, // 'C' IAC WILL TERMINAL-TYPE 'D'
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,252,24, 68}, // 'C' IAC WON'T TERMINAL-TYPE 'D'
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,253,24, 68}, // 'C' IAC DO TERMINAL-TYPE 'D'
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,254,24, 68}, // 'C' IAC DON'T TERMINAL-TYPE 'D'
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255,251,24, 68, '\n'}, // 'C' IAC WILL TERMINAL-TYPE 'D' '\n'
|
||||
Expected: []byte{67, 255,255,251,24, 68, '\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,252,24, 68, '\n'}, // 'C' IAC WON'T TERMINAL-TYPE 'D' '\n'
|
||||
Expected: []byte{67, 255,255,252,24, 68, '\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,253,24, 68, '\n'}, // 'C' IAC DO TERMINAL-TYPE 'D' '\n'
|
||||
Expected: []byte{67, 255,255,253,24, 68, '\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,254,24, 68, '\n'}, // 'C' IAC DON'T TERMINAL-TYPE 'D' '\n'
|
||||
Expected: []byte{67, 255,255,254,24, 68, '\r','\n'},
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255, 250, 24, 1, 255, 240}, // IAC SB TERMINAL-TYPE SEND IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 250, 24, 0, 68,69,67,45,86,84,53,50 ,255, 240}, // IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{ 255, 250, 24, 1, 255, 240, '\n'}, // IAC SB TERMINAL-TYPE SEND IAC SE '\n'
|
||||
Expected: []byte{255, 255, 250, 24, 1, 255, 255, 240, '\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255, 250, 24, 0, 68,69,67,45,86,84,53,50, 255, 240, '\n'}, // IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE '\n'
|
||||
Expected: []byte{255, 255, 250, 24, 0, 68,69,67,45,86,84,53,50, 255, 255, 240, '\r','\n'},
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 1, 255, 240}, // 'C' IAC SB TERMINAL-TYPE SEND IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 0, 68,69,67,45,86,84,53,50 ,255, 240}, // 'C' IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 1, 255, 240, '\n'}, // 'C' IAC SB TERMINAL-TYPE SEND IAC SE '\n'
|
||||
Expected: []byte{67, 255, 255, 250, 24, 1, 255, 255, 240, '\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 0, 68,69,67,45,86,84,53,50, 255, 240, '\n'}, // 'C' IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE '\n'
|
||||
Expected: []byte{67, 255, 255, 250, 24, 0, 68,69,67,45,86,84,53,50, 255, 255, 240, '\r','\n'},
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255, 250, 24, 1, 255, 240, 68}, // IAC SB TERMINAL-TYPE SEND IAC SE 'D'
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 250, 24, 0, 68,69,67,45,86,84,53,50 ,255, 240, 68}, // IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE 'D'
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{ 255, 250, 24, 1, 255, 240, 68, '\n'}, // IAC SB TERMINAL-TYPE SEND IAC SE 'D' '\n'
|
||||
Expected: []byte{255, 255, 250, 24, 1, 255, 255, 240, 68, '\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255, 250, 24, 0, 68,69,67,45,86,84,53,50, 255, 240, 68, '\n'}, // IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE 'D' '\n'
|
||||
Expected: []byte{255, 255, 250, 24, 0, 68,69,67,45,86,84,53,50, 255, 255, 240, 68, '\r','\n'},
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 1, 255, 240, 68}, // 'C' IAC SB TERMINAL-TYPE SEND IAC SE 'D'
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 0, 68,69,67,45,86,84,53,50 ,255, 240, 68}, // 'C' IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE 'D'
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 1, 255, 240, 68, '\n'}, // 'C' IAC SB TERMINAL-TYPE SEND IAC SE 'D' '\n'
|
||||
Expected: []byte{67, 255, 255, 250, 24, 1, 255, 255, 240, 68, '\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 0, 68,69,67,45,86,84,53,50, 255, 240, 68, '\n'}, // 'C' IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE 'D' '\n'
|
||||
Expected: []byte{67, 255, 255, 250, 24, 0, 68,69,67,45,86,84,53,50, 255, 255, 240, 68, '\r','\n'},
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{ 255,250, 0,1,2,3,4,5,6,7,8,9, 10,11,12,13 ,255,240}, // IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE
|
||||
Expected: []byte{255,255,250, 0,1,2,3,4,5,6,7,8,9,'\r',10},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 0,1,2,3,4,5,6,7,8,9, 10, 11,12,13 ,255,240}, // 'C' IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE
|
||||
Expected: []byte{67, 255,255,250, 0,1,2,3,4,5,6,7,8,9,'\r',10},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,250, 0,1,2,3,4,5,6,7,8,9, 10,11,12,13 ,255,240, 68}, // IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE 'D'
|
||||
Expected: []byte{255,255,250, 0,1,2,3,4,5,6,7,8,9,'\r',10},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 0,1,2,3,4,5,6,7,8,9, 10,11,12,13 ,255,240, 68}, // 'C' IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE 'D'
|
||||
Expected: []byte{67, 255,255,250, 0,1,2,3,4,5,6,7,8,9,'\r',10},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{ 255,250, 0,1,2,3,4,5,6,7,8,9, 10,11,12,13, 255,240, '\n'}, // IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE '\n'
|
||||
Expected: []byte{255,255,250, 0,1,2,3,4,5,6,7,8,9,'\r',10,11,12,13, 255,255,240, '\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 0,1,2,3,4,5,6,7,8,9, 10,11,12, 13, 255,240, '\n'}, // 'C' IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE '\n'
|
||||
Expected: []byte{67, 255,255,250, 0,1,2,3,4,5,6,7,8,9,'\r',10,11,12,13, 255,255,240, '\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,250, 0,1,2,3,4,5,6,7,8,9, 10,11,12,13, 255,240, 68, '\n'}, // IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE 'D' '\n'
|
||||
Expected: []byte{255,255,250, 0,1,2,3,4,5,6,7,8,9,'\r',10,11,12,13, 255,255,240, 68, '\r','\n'},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 0,1,2,3,4,5,6,7,8,9, 10,11,12,13, 255,240, 68, '\n'}, // 'C' IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE 'D' '\n'
|
||||
Expected: []byte{67, 255,255,250, 0,1,2,3,4,5,6,7,8,9,'\r',10,11,12,13, 255,255,240, 68, '\r','\n'},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
for testNumber, test := range tests {
|
||||
var stdinBuffer bytes.Buffer
|
||||
var stdoutBuffer bytes.Buffer
|
||||
var stderrBuffer bytes.Buffer
|
||||
|
||||
stdinBuffer.Write(test.Bytes) // <----------------- The important difference between the 2 loops.
|
||||
|
||||
stdin := ioutil.NopCloser(&stdinBuffer)
|
||||
stdout := oi.WriteNopCloser(&stdoutBuffer)
|
||||
stderr := oi.WriteNopCloser(&stderrBuffer)
|
||||
|
||||
var ctx Context = nil
|
||||
|
||||
var dataWriterBuffer bytes.Buffer
|
||||
dataWriter := newDataWriter(&dataWriterBuffer)
|
||||
|
||||
dataReader := newDataReader( bytes.NewReader([]byte{}) ) // <----------------- The important difference between the 2 loops.
|
||||
|
||||
standardCallerCallTELNET(stdin, stdout, stderr, ctx, dataWriter, dataReader)
|
||||
|
||||
|
||||
if expected, actual := string(test.Expected), dataWriterBuffer.String(); expected != actual {
|
||||
t.Errorf("For test #%d, expected %q, but actually got %q; for %q.", testNumber, expected, actual, test.Bytes)
|
||||
continue
|
||||
}
|
||||
|
||||
if expected, actual := "", stdoutBuffer.String(); expected != actual {
|
||||
t.Errorf("For test #%d, expected %q, but actually got %q.", testNumber, expected, actual)
|
||||
continue
|
||||
}
|
||||
|
||||
if expected, actual := "", stderrBuffer.String(); expected != actual {
|
||||
t.Errorf("For test #%d, expected %q, but actually got %q.", testNumber, expected, actual)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
func TestStandardCallerFromServerToClient(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
Bytes []byte
|
||||
Expected []byte
|
||||
}{
|
||||
{
|
||||
Bytes: []byte{},
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("a"),
|
||||
Expected: []byte("a"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("b"),
|
||||
Expected: []byte("b"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("c"),
|
||||
Expected: []byte("c"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple"),
|
||||
Expected: []byte("apple"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("banana"),
|
||||
Expected: []byte("banana"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("cherry"),
|
||||
Expected: []byte("cherry"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple banana cherry"),
|
||||
Expected: []byte("apple banana cherry"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255,255},
|
||||
Expected: []byte{255},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,255,255,255},
|
||||
Expected: []byte{255,255},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,255,255,255,255,255},
|
||||
Expected: []byte{255,255,255},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,255,255,255,255,255,255,255},
|
||||
Expected: []byte{255,255,255,255},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,255,255,255,255,255,255,255,255,255},
|
||||
Expected: []byte{255,255,255,255,255},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple\xff\xffbanana\xff\xffcherry"),
|
||||
Expected: []byte("apple\xffbanana\xffcherry"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("\xff\xffapple\xff\xffbanana\xff\xffcherry\xff\xff"),
|
||||
Expected: []byte("\xffapple\xffbanana\xffcherry\xff"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte("apple\xff\xff\xff\xffbanana\xff\xff\xff\xffcherry"),
|
||||
Expected: []byte("apple\xff\xffbanana\xff\xffcherry"),
|
||||
},
|
||||
{
|
||||
Bytes: []byte("\xff\xff\xff\xffapple\xff\xff\xff\xffbanana\xff\xff\xff\xffcherry\xff\xff\xff\xff"),
|
||||
Expected: []byte("\xff\xffapple\xff\xffbanana\xff\xffcherry\xff\xff"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255,251,24}, // IAC WILL TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,252,24}, // IAC WON'T TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,253,24}, // IAC DO TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,254,24}, // IAC DON'T TERMINAL-TYPE
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255,251,24}, // 'C' IAC WILL TERMINAL-TYPE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,252,24}, // 'C' IAC WON'T TERMINAL-TYPE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,253,24}, // 'C' IAC DO TERMINAL-TYPE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,254,24}, // 'C' IAC DON'T TERMINAL-TYPE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255,251,24, 68}, // IAC WILL TERMINAL-TYPE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,252,24, 68}, // IAC WON'T TERMINAL-TYPE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,253,24, 68}, // IAC DO TERMINAL-TYPE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,254,24, 68}, // IAC DON'T TERMINAL-TYPE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255,251,24, 68}, // 'C' IAC WILL TERMINAL-TYPE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,252,24, 68}, // 'C' IAC WON'T TERMINAL-TYPE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,253,24, 68}, // 'C' IAC DO TERMINAL-TYPE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,254,24, 68}, // 'C' IAC DON'T TERMINAL-TYPE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255, 250, 24, 1, 255, 240}, // IAC SB TERMINAL-TYPE SEND IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 250, 24, 0, 68,69,67,45,86,84,53,50 ,255, 240}, // IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 1, 255, 240}, // 'C' IAC SB TERMINAL-TYPE SEND IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 0, 68,69,67,45,86,84,53,50 ,255, 240}, // 'C' IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255, 250, 24, 1, 255, 240, 68}, // IAC SB TERMINAL-TYPE SEND IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255, 250, 24, 0, 68,69,67,45,86,84,53,50 ,255, 240, 68}, // IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 1, 255, 240, 68}, // 'C' IAC SB TERMINAL-TYPE SEND IAC SE 'D'
|
||||
Expected: []byte{67, 68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255, 250, 24, 0, 68,69,67,45,86,84,53,50 ,255, 240, 68}, // 'C' IAC SB TERMINAL-TYPE IS "DEC-VT52" IAC SE 'D'
|
||||
Expected: []byte{67, 68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Bytes: []byte{255,250, 0,1,2,3,4,5,6,7,8,9,10,11,12,13 ,255,240}, // IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 0,1,2,3,4,5,6,7,8,9,10,11,12,13 ,255,240}, // 'C' IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{255,250, 0,1,2,3,4,5,6,7,8,9,10,11,12,13 ,255,240, 68}, // IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 0,1,2,3,4,5,6,7,8,9,10,11,12,13 ,255,240, 68}, // 'C' IAC SB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 IAC SE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
//@TODO: Is this correct? Can IAC appear between thee 'IAC SB' and ''IAC SE'?... and if "yes", do escaping rules apply?
|
||||
{
|
||||
Bytes: []byte{ 255,250, 255,255,240 ,255,240}, // IAC SB 255 255 240 IAC SE = IAC SB IAC IAC SE IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 255,255,240 ,255,240}, // 'C' IAC SB 255 255 240 IAC SE = IAC SB IAC IAC SE IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,250, 255,255,240 ,255,240, 68}, // IAC SB 255 255 240 IAC SE = IAC SB IAC IAC SE IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 255,255,240 ,255,240, 68}, // 'C' IAC SB 255 255 240 IAC SE = IAC SB IAC IAC SE IAC SE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
//@TODO: Is this correct? Can IAC appear between thee 'IAC SB' and ''IAC SE'?... and if "yes", do escaping rules apply?
|
||||
{
|
||||
Bytes: []byte{ 255,250, 71,255,255,240 ,255,240}, // IAC SB 'G' 255 255 240 IAC SE = IAC SB 'G' IAC IAC SE IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 71,255,255,240 ,255,240}, // 'C' IAC SB 'G' 255 255 240 IAC SE = IAC SB 'G' IAC IAC SE IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,250, 71,255,255,240 ,255,240, 68}, // IAC SB 'G' 255 255 240 IAC SE = IAC SB 'G' IAC IAC SE IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 71,255,255,240 ,255,240, 68}, // 'C' IAC SB 'G' 255 255 240 IAC SE = IAC 'G' SB IAC IAC SE IAC SE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
//@TODO: Is this correct? Can IAC appear between thee 'IAC SB' and ''IAC SE'?... and if "yes", do escaping rules apply?
|
||||
{
|
||||
Bytes: []byte{ 255,250, 255,255,240,72 ,255,240}, // IAC SB 255 255 240 'H' IAC SE = IAC SB IAC IAC SE 'H' IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 255,255,240,72 ,255,240}, // 'C' IAC SB 255 255 240 'H' IAC SE = IAC SB IAC IAC SE 'H' IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,250, 255,255,240,72 ,255,240, 68}, // IAC SB 255 255 240 'H' IAC SE = IAC SB IAC IAC SE 'H' IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 255,255,240,72 ,255,240, 68}, // 'C' IAC SB 255 255 240 'H' IAC SE = IAC SB IAC IAC SE 'H' IAC SE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
|
||||
|
||||
|
||||
//@TODO: Is this correct? Can IAC appear between thee 'IAC SB' and ''IAC SE'?... and if "yes", do escaping rules apply?
|
||||
{
|
||||
Bytes: []byte{ 255,250, 71,255,255,240,72 ,255,240}, // IAC SB 'G' 255 255 240 'H' IAC SE = IAC SB 'G' IAC IAC SE 'H' IAC SE
|
||||
Expected: []byte{},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 71,255,255,240,72 ,255,240}, // 'C' IAC SB 'G' 255 255 240 'H' IAC SE = IAC SB 'G' IAC IAC SE 'H' IAC SE
|
||||
Expected: []byte{67},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{ 255,250, 71,255,255,240,72 ,255,240, 68}, // IAC SB 'G' 255 255 240 'H' IAC SE = IAC SB 'G' IAC IAC SE 'H' IAC SE 'D'
|
||||
Expected: []byte{68},
|
||||
},
|
||||
{
|
||||
Bytes: []byte{67, 255,250, 71,255,255,240,72 ,255,240, 68}, // 'C' IAC SB 'G' 255 255 240 'H' IAC SE = IAC 'G' SB IAC IAC SE 'H' IAC SE 'D'
|
||||
Expected: []byte{67,68},
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
for testNumber, test := range tests {
|
||||
var stdinBuffer bytes.Buffer
|
||||
var stdoutBuffer bytes.Buffer
|
||||
var stderrBuffer bytes.Buffer
|
||||
|
||||
stdin := ioutil.NopCloser(&stdinBuffer)
|
||||
stdout := oi.WriteNopCloser(&stdoutBuffer)
|
||||
stderr := oi.WriteNopCloser(&stderrBuffer)
|
||||
|
||||
var ctx Context = nil
|
||||
|
||||
var dataWriterBuffer bytes.Buffer
|
||||
dataWriter := newDataWriter(&dataWriterBuffer)
|
||||
|
||||
dataReader := newDataReader( bytes.NewReader(test.Bytes) ) // <----------------- The important difference between the 2 loops.
|
||||
|
||||
standardCallerCallTELNET(stdin, stdout, stderr, ctx, dataWriter, dataReader)
|
||||
|
||||
|
||||
if expected, actual := "", dataWriterBuffer.String(); expected != actual {
|
||||
t.Errorf("For test #%d, expected %q, but actually got %q; for %q.", testNumber, expected, actual, test.Bytes)
|
||||
continue
|
||||
}
|
||||
|
||||
if expected, actual := string(test.Expected), stdoutBuffer.String(); expected != actual {
|
||||
t.Errorf("For test #%d, expected %q, but actually got %q.", testNumber, expected, actual)
|
||||
continue
|
||||
}
|
||||
|
||||
if expected, actual := "", stderrBuffer.String(); expected != actual {
|
||||
t.Errorf("For test #%d, expected %q, but actually got %q.", testNumber, expected, actual)
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package telsh
|
||||
|
||||
|
||||
type internalDiscardLogger struct{}
|
||||
|
||||
func (internalDiscardLogger) Debug(...interface{}) {}
|
||||
func (internalDiscardLogger) Debugf(string, ...interface{}) {}
|
||||
func (internalDiscardLogger) Debugln(...interface{}) {}
|
||||
|
||||
func (internalDiscardLogger) Error(...interface{}) {}
|
||||
func (internalDiscardLogger) Errorf(string, ...interface{}) {}
|
||||
func (internalDiscardLogger) Errorln(...interface{}) {}
|
||||
|
||||
func (internalDiscardLogger) Trace(...interface{}) {}
|
||||
func (internalDiscardLogger) Tracef(string, ...interface{}) {}
|
||||
func (internalDiscardLogger) Traceln(...interface{}) {}
|
||||
|
||||
func (internalDiscardLogger) Warn(...interface{}) {}
|
||||
func (internalDiscardLogger) Warnf(string, ...interface{}) {}
|
||||
func (internalDiscardLogger) Warnln(...interface{}) {}
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
Package telsh provides "middleware" (for the telnet package) that can be used to implement a TELNET or TELNETS server
|
||||
that provides a "shell" interface (also known as a "command-line interface" or "CLI").
|
||||
|
||||
Shell interfaces you may be familiar with include: "bash", "csh", "sh", "zsk", etc.
|
||||
|
||||
|
||||
TELNET Server
|
||||
|
||||
Here is an example usage:
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-oi"
|
||||
"github.com/reiver/go-telnet"
|
||||
"github.com/reiver/go-telnet/telsh"
|
||||
|
||||
"io"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
telnetHandler := telsh.NewShellHandler()
|
||||
|
||||
if err := telnetHandler.RegisterElse(
|
||||
telsh.ProducerFunc(
|
||||
func(ctx telnet.Context, name string, args ...string) telsh.Handler {
|
||||
return telsh.PromoteHandlerFunc(
|
||||
func(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
oi.LongWrite(stdout, []byte{'w','a','t','?', '\r','\n'})
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
},
|
||||
),
|
||||
); nil != err {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := telnetHandler.Register("help",
|
||||
telsh.ProducerFunc(
|
||||
func(ctx telnet.Context, name string, args ...string) telsh.Handler {
|
||||
return telsh.PromoteHandlerFunc(
|
||||
func(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
oi.LongWrite(stdout, []byte{'r','t','f','m','!', '\r','\n'})
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
},
|
||||
),
|
||||
); nil != err {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err := telnet.ListenAndServe(":5555", telnetHandler)
|
||||
if nil != err {
|
||||
//@TODO: Handle this error better.
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
Here is a more "unpacked" example:
|
||||
|
||||
package main
|
||||
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-oi"
|
||||
"github.com/reiver/go-telnet"
|
||||
"github.com/reiver/go-telnet/telsh"
|
||||
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
var (
|
||||
shellHandler = telsh.NewShellHandler()
|
||||
)
|
||||
|
||||
|
||||
func init() {
|
||||
|
||||
shellHandler.Register("dance", telsh.ProducerFunc(producer))
|
||||
|
||||
|
||||
shellHandler.WelcomeMessage = `
|
||||
__ __ ______ _ _____ ____ __ __ ______
|
||||
\ \ / /| ____|| | / ____| / __ \ | \/ || ____|
|
||||
\ \ /\ / / | |__ | | | | | | | || \ / || |__
|
||||
\ \/ \/ / | __| | | | | | | | || |\/| || __|
|
||||
\ /\ / | |____ | |____ | |____ | |__| || | | || |____
|
||||
\/ \/ |______||______| \_____| \____/ |_| |_||______|
|
||||
|
||||
`
|
||||
}
|
||||
|
||||
|
||||
func producer(ctx telnet.Context, name string, args ...string) telsh.Handler{
|
||||
return telsh.PromoteHandlerFunc(handler)
|
||||
}
|
||||
|
||||
|
||||
func handler(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
for i:=0; i<20; i++ {
|
||||
oi.LongWriteString(stdout, "\r⠋")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠙")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠹")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠸")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠼")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠴")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠦")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠧")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠇")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
|
||||
oi.LongWriteString(stdout, "\r⠏")
|
||||
time.Sleep(50*time.Millisecond)
|
||||
}
|
||||
oi.LongWriteString(stdout, "\r \r\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func main() {
|
||||
|
||||
addr := ":5555"
|
||||
if err := telnet.ListenAndServe(addr, shellHandler); nil != err {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
package telsh
|
||||
@@ -0,0 +1,140 @@
|
||||
package telsh
|
||||
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
|
||||
// Hander is an abstraction that represents a "running" shell "command".
|
||||
//
|
||||
// Contrast this with a Producer, which is is an abstraction that
|
||||
// represents a shell "command".
|
||||
//
|
||||
// To use a metaphor, the differences between a Producer and a Handler,
|
||||
// is like the difference between a program executable and actually running
|
||||
// the program executable.
|
||||
//
|
||||
// Conceptually, anything that implements the Hander, and then has its Producer
|
||||
// registered with ShellHandler.Register() will be available as a command.
|
||||
//
|
||||
// Note that Handler was intentionally made to be compatible with
|
||||
// "os/exec", which is part of the Go standard library.
|
||||
type Handler interface {
|
||||
Run() error
|
||||
|
||||
StdinPipe() (io.WriteCloser, error)
|
||||
StdoutPipe() (io.ReadCloser, error)
|
||||
StderrPipe() (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
|
||||
// HandlerFunc is useful to write inline Producers, and provides an alternative to
|
||||
// creating something that implements Handler directly.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// shellHandler := telsh.NewShellHandler()
|
||||
//
|
||||
// shellHandler.Register("five", telsh.ProducerFunc(
|
||||
//
|
||||
// func(ctx telnet.Context, name string, args ...string) telsh.Handler{
|
||||
//
|
||||
// return telsh.PromoteHandlerFunc(
|
||||
//
|
||||
// func(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string) error {
|
||||
// oi.LongWrite(stdout, []byte{'5', '\r', '\n'})
|
||||
//
|
||||
// return nil
|
||||
// },
|
||||
// )
|
||||
// },
|
||||
// ))
|
||||
//
|
||||
// Note that PromoteHandlerFunc is used to turn a HandlerFunc into a Handler.
|
||||
type HandlerFunc func(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser, args ...string)error
|
||||
|
||||
|
||||
type internalPromotedHandlerFunc struct {
|
||||
err error
|
||||
fn HandlerFunc
|
||||
stdin io.ReadCloser
|
||||
stdout io.WriteCloser
|
||||
stderr io.WriteCloser
|
||||
|
||||
stdinPipe io.WriteCloser
|
||||
stdoutPipe io.ReadCloser
|
||||
stderrPipe io.ReadCloser
|
||||
|
||||
args []string
|
||||
}
|
||||
|
||||
|
||||
// PromoteHandlerFunc turns a HandlerFunc into a Handler.
|
||||
func PromoteHandlerFunc(fn HandlerFunc, args ...string) Handler {
|
||||
stdin, stdinPipe := io.Pipe()
|
||||
stdoutPipe, stdout := io.Pipe()
|
||||
stderrPipe, stderr := io.Pipe()
|
||||
|
||||
argsCopy := make([]string, len(args))
|
||||
for i, datum := range args {
|
||||
argsCopy[i] = datum
|
||||
}
|
||||
|
||||
handler := internalPromotedHandlerFunc{
|
||||
err:nil,
|
||||
|
||||
fn:fn,
|
||||
|
||||
stdin:stdin,
|
||||
stdout:stdout,
|
||||
stderr:stderr,
|
||||
|
||||
stdinPipe:stdinPipe,
|
||||
stdoutPipe:stdoutPipe,
|
||||
stderrPipe:stderrPipe,
|
||||
|
||||
args:argsCopy,
|
||||
}
|
||||
|
||||
return &handler
|
||||
}
|
||||
|
||||
|
||||
func (handler *internalPromotedHandlerFunc) Run() error {
|
||||
if nil != handler.err {
|
||||
return handler.err
|
||||
}
|
||||
|
||||
handler.err = handler.fn(handler.stdin, handler.stdout, handler.stderr, handler.args...)
|
||||
|
||||
handler.stdin.Close()
|
||||
handler.stdout.Close()
|
||||
handler.stderr.Close()
|
||||
|
||||
return handler.err
|
||||
}
|
||||
|
||||
func (handler *internalPromotedHandlerFunc) StdinPipe() (io.WriteCloser, error) {
|
||||
if nil != handler.err {
|
||||
return nil, handler.err
|
||||
}
|
||||
|
||||
return handler.stdinPipe, nil
|
||||
}
|
||||
|
||||
func (handler *internalPromotedHandlerFunc) StdoutPipe() (io.ReadCloser, error) {
|
||||
if nil != handler.err {
|
||||
return nil, handler.err
|
||||
}
|
||||
|
||||
return handler.stdoutPipe, nil
|
||||
}
|
||||
|
||||
func (handler *internalPromotedHandlerFunc) StderrPipe() (io.ReadCloser, error) {
|
||||
if nil != handler.err {
|
||||
return nil, handler.err
|
||||
}
|
||||
|
||||
return handler.stderrPipe, nil
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package telsh
|
||||
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-oi"
|
||||
"github.com/reiver/go-telnet"
|
||||
|
||||
"io"
|
||||
"sort"
|
||||
)
|
||||
|
||||
|
||||
type internalHelpProducer struct {
|
||||
shellHandler *ShellHandler
|
||||
}
|
||||
|
||||
|
||||
func Help(shellHandler *ShellHandler) Producer {
|
||||
producer := internalHelpProducer{
|
||||
shellHandler:shellHandler,
|
||||
}
|
||||
|
||||
return &producer
|
||||
}
|
||||
|
||||
|
||||
func (producer *internalHelpProducer) Produce(telnet.Context, string, ...string) Handler {
|
||||
return newHelpHandler(producer)
|
||||
}
|
||||
|
||||
|
||||
type internalHelpHandler struct {
|
||||
helpProducer *internalHelpProducer
|
||||
|
||||
err error
|
||||
|
||||
stdin io.ReadCloser
|
||||
stdout io.WriteCloser
|
||||
stderr io.WriteCloser
|
||||
|
||||
stdinPipe io.WriteCloser
|
||||
stdoutPipe io.ReadCloser
|
||||
stderrPipe io.ReadCloser
|
||||
}
|
||||
|
||||
|
||||
func newHelpHandler(helpProducer *internalHelpProducer) *internalHelpHandler {
|
||||
stdin, stdinPipe := io.Pipe()
|
||||
stdoutPipe, stdout := io.Pipe()
|
||||
stderrPipe, stderr := io.Pipe()
|
||||
|
||||
handler := internalHelpHandler{
|
||||
helpProducer:helpProducer,
|
||||
|
||||
err:nil,
|
||||
|
||||
stdin:stdin,
|
||||
stdout:stdout,
|
||||
stderr:stderr,
|
||||
|
||||
stdinPipe:stdinPipe,
|
||||
stdoutPipe:stdoutPipe,
|
||||
stderrPipe:stderrPipe,
|
||||
}
|
||||
|
||||
return &handler
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
func (handler *internalHelpHandler) Run() error {
|
||||
if nil != handler.err {
|
||||
return handler.err
|
||||
}
|
||||
|
||||
//@TODO: Should this be reaching inside of ShellHandler? Maybe there should be ShellHandler public methods instead.
|
||||
keys := make([]string, 1+len(handler.helpProducer.shellHandler.producers))
|
||||
i:=0
|
||||
for key,_ := range handler.helpProducer.shellHandler.producers {
|
||||
keys[i] = key
|
||||
i++
|
||||
}
|
||||
keys[i] = handler.helpProducer.shellHandler.ExitCommandName
|
||||
sort.Strings(keys)
|
||||
for _, key := range keys {
|
||||
oi.LongWriteString(handler.stdout, key)
|
||||
oi.LongWriteString(handler.stdout, "\r\n")
|
||||
}
|
||||
|
||||
handler.stdin.Close()
|
||||
handler.stdout.Close()
|
||||
handler.stderr.Close()
|
||||
|
||||
return handler.err
|
||||
}
|
||||
|
||||
func (handler *internalHelpHandler) StdinPipe() (io.WriteCloser, error) {
|
||||
if nil != handler.err {
|
||||
return nil, handler.err
|
||||
}
|
||||
|
||||
return handler.stdinPipe, nil
|
||||
}
|
||||
|
||||
func (handler *internalHelpHandler) StdoutPipe() (io.ReadCloser, error) {
|
||||
if nil != handler.err {
|
||||
return nil, handler.err
|
||||
}
|
||||
|
||||
return handler.stdoutPipe, nil
|
||||
}
|
||||
|
||||
func (handler *internalHelpHandler) StderrPipe() (io.ReadCloser, error) {
|
||||
if nil != handler.err {
|
||||
return nil, handler.err
|
||||
}
|
||||
|
||||
return handler.stderrPipe, nil
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package telsh
|
||||
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-telnet"
|
||||
)
|
||||
|
||||
|
||||
// A Producer provides a Produce method which creates a Handler.
|
||||
//
|
||||
// Producer is an abstraction that represents a shell "command".
|
||||
//
|
||||
// Contrast this with a Handler, which is is an abstraction that
|
||||
// represents a "running" shell "command".
|
||||
//
|
||||
// To use a metaphor, the differences between a Producer and a Handler,
|
||||
// is like the difference between a program executable and actually running
|
||||
// the program executable.
|
||||
type Producer interface {
|
||||
Produce(telnet.Context, string, ...string) Handler
|
||||
}
|
||||
|
||||
|
||||
// ProducerFunc is an adaptor, that can be used to turn a func with the
|
||||
// signature:
|
||||
//
|
||||
// func(telnet.Context, string, ...string) Handler
|
||||
//
|
||||
// Into a Producer
|
||||
type ProducerFunc func(telnet.Context, string, ...string) Handler
|
||||
|
||||
|
||||
// Produce makes ProducerFunc fit the Producer interface.
|
||||
func (fn ProducerFunc) Produce(ctx telnet.Context, name string, args ...string) Handler {
|
||||
return fn(ctx, name, args...)
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
package telsh
|
||||
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-oi"
|
||||
"github.com/reiver/go-telnet"
|
||||
|
||||
"bytes"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
||||
const (
|
||||
defaultExitCommandName = "exit"
|
||||
defaultPrompt = "§ "
|
||||
defaultWelcomeMessage = "\r\nWelcome!\r\n"
|
||||
defaultExitMessage = "\r\nGoodbye!\r\n"
|
||||
)
|
||||
|
||||
|
||||
type ShellHandler struct {
|
||||
muxtex sync.RWMutex
|
||||
producers map[string]Producer
|
||||
elseProducer Producer
|
||||
|
||||
ExitCommandName string
|
||||
Prompt string
|
||||
WelcomeMessage string
|
||||
ExitMessage string
|
||||
}
|
||||
|
||||
|
||||
func NewShellHandler() *ShellHandler {
|
||||
producers := map[string]Producer{}
|
||||
|
||||
telnetHandler := ShellHandler{
|
||||
producers:producers,
|
||||
|
||||
Prompt: defaultPrompt,
|
||||
ExitCommandName: defaultExitCommandName,
|
||||
WelcomeMessage: defaultWelcomeMessage,
|
||||
ExitMessage: defaultExitMessage,
|
||||
}
|
||||
|
||||
return &telnetHandler
|
||||
}
|
||||
|
||||
|
||||
func (telnetHandler *ShellHandler) Register(name string, producer Producer) error {
|
||||
|
||||
telnetHandler.muxtex.Lock()
|
||||
telnetHandler.producers[name] = producer
|
||||
telnetHandler.muxtex.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (telnetHandler *ShellHandler) MustRegister(name string, producer Producer) *ShellHandler {
|
||||
if err := telnetHandler.Register(name, producer); nil != err {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return telnetHandler
|
||||
}
|
||||
|
||||
|
||||
func (telnetHandler *ShellHandler) RegisterHandlerFunc(name string, handlerFunc HandlerFunc) error {
|
||||
|
||||
produce := func(ctx telnet.Context, name string, args ...string) Handler {
|
||||
return PromoteHandlerFunc(handlerFunc, args...)
|
||||
}
|
||||
|
||||
producer := ProducerFunc(produce)
|
||||
|
||||
return telnetHandler.Register(name, producer)
|
||||
}
|
||||
|
||||
func (telnetHandler *ShellHandler) MustRegisterHandlerFunc(name string, handlerFunc HandlerFunc) *ShellHandler {
|
||||
if err := telnetHandler.RegisterHandlerFunc(name, handlerFunc); nil != err {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return telnetHandler
|
||||
}
|
||||
|
||||
|
||||
func (telnetHandler *ShellHandler) RegisterElse(producer Producer) error {
|
||||
|
||||
telnetHandler.muxtex.Lock()
|
||||
telnetHandler.elseProducer = producer
|
||||
telnetHandler.muxtex.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (telnetHandler *ShellHandler) MustRegisterElse(producer Producer) *ShellHandler {
|
||||
if err := telnetHandler.RegisterElse(producer); nil != err {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return telnetHandler
|
||||
}
|
||||
|
||||
|
||||
func (telnetHandler *ShellHandler) ServeTELNET(ctx telnet.Context, writer telnet.Writer, reader telnet.Reader) {
|
||||
|
||||
logger := ctx.Logger()
|
||||
if nil == logger {
|
||||
logger = internalDiscardLogger{}
|
||||
}
|
||||
|
||||
|
||||
colonSpaceCommandNotFoundEL := []byte(": command not found\r\n")
|
||||
|
||||
|
||||
var prompt bytes.Buffer
|
||||
var exitCommandName string
|
||||
var welcomeMessage string
|
||||
var exitMessage string
|
||||
|
||||
prompt.WriteString(telnetHandler.Prompt)
|
||||
|
||||
promptBytes := prompt.Bytes()
|
||||
|
||||
exitCommandName = telnetHandler.ExitCommandName
|
||||
welcomeMessage = telnetHandler.WelcomeMessage
|
||||
exitMessage = telnetHandler.ExitMessage
|
||||
|
||||
|
||||
if _, err := oi.LongWriteString(writer, welcomeMessage); nil != err {
|
||||
logger.Errorf("Problem long writing welcome message: %v", err)
|
||||
return
|
||||
}
|
||||
logger.Debugf("Wrote welcome message: %q.", welcomeMessage)
|
||||
if _, err := oi.LongWrite(writer, promptBytes); nil != err {
|
||||
logger.Errorf("Problem long writing prompt: %v", err)
|
||||
return
|
||||
}
|
||||
logger.Debugf("Wrote prompt: %q.", promptBytes)
|
||||
|
||||
|
||||
var buffer [1]byte // Seems like the length of the buffer needs to be small, otherwise will have to wait for buffer to fill up.
|
||||
p := buffer[:]
|
||||
|
||||
var line bytes.Buffer
|
||||
|
||||
for {
|
||||
// Read 1 byte.
|
||||
n, err := reader.Read(p)
|
||||
if n <= 0 && nil == err {
|
||||
continue
|
||||
} else if n <= 0 && nil != err {
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
line.WriteByte(p[0])
|
||||
//logger.Tracef("Received: %q (%d).", p[0], p[0])
|
||||
|
||||
|
||||
if '\n' == p[0] {
|
||||
lineString := line.String()
|
||||
|
||||
if "\r\n" == lineString {
|
||||
line.Reset()
|
||||
if _, err := oi.LongWrite(writer, promptBytes); nil != err {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
//@TODO: support piping.
|
||||
fields := strings.Fields(lineString)
|
||||
logger.Debugf("Have %d tokens.", len(fields))
|
||||
logger.Tracef("Tokens: %v", fields)
|
||||
if len(fields) <= 0 {
|
||||
line.Reset()
|
||||
if _, err := oi.LongWrite(writer, promptBytes); nil != err {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
field0 := fields[0]
|
||||
|
||||
if exitCommandName == field0 {
|
||||
oi.LongWriteString(writer, exitMessage)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
var producer Producer
|
||||
|
||||
telnetHandler.muxtex.RLock()
|
||||
var ok bool
|
||||
producer, ok = telnetHandler.producers[field0]
|
||||
telnetHandler.muxtex.RUnlock()
|
||||
|
||||
if !ok {
|
||||
telnetHandler.muxtex.RLock()
|
||||
producer = telnetHandler.elseProducer
|
||||
telnetHandler.muxtex.RUnlock()
|
||||
}
|
||||
|
||||
if nil == producer {
|
||||
//@TODO: Don't convert that to []byte! think this creates "garbage" (for collector).
|
||||
oi.LongWrite(writer, []byte(field0))
|
||||
oi.LongWrite(writer, colonSpaceCommandNotFoundEL)
|
||||
line.Reset()
|
||||
if _, err := oi.LongWrite(writer, promptBytes); nil != err {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
handler := producer.Produce(ctx, field0, fields[1:]...)
|
||||
if nil == handler {
|
||||
oi.LongWrite(writer, []byte(field0))
|
||||
//@TODO: Need to use a different error message.
|
||||
oi.LongWrite(writer, colonSpaceCommandNotFoundEL)
|
||||
line.Reset()
|
||||
oi.LongWrite(writer, promptBytes)
|
||||
continue
|
||||
}
|
||||
|
||||
//@TODO: Wire up the stdin, stdout, stderr of the handler.
|
||||
|
||||
if stdoutPipe, err := handler.StdoutPipe(); nil != err {
|
||||
//@TODO:
|
||||
} else if nil == stdoutPipe {
|
||||
//@TODO:
|
||||
} else {
|
||||
connect(ctx, writer, stdoutPipe)
|
||||
}
|
||||
|
||||
|
||||
if stderrPipe, err := handler.StderrPipe(); nil != err {
|
||||
//@TODO:
|
||||
} else if nil == stderrPipe {
|
||||
//@TODO:
|
||||
} else {
|
||||
connect(ctx, writer, stderrPipe)
|
||||
}
|
||||
|
||||
|
||||
if err := handler.Run(); nil != err {
|
||||
//@TODO:
|
||||
}
|
||||
line.Reset()
|
||||
if _, err := oi.LongWrite(writer, promptBytes); nil != err {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//@TODO: Are there any special errors we should be dealing with separately?
|
||||
if nil != err {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
oi.LongWriteString(writer, exitMessage)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
func connect(ctx telnet.Context, writer io.Writer, reader io.Reader) {
|
||||
|
||||
logger := ctx.Logger()
|
||||
|
||||
go func(logger telnet.Logger){
|
||||
|
||||
var buffer [1]byte // Seems like the length of the buffer needs to be small, otherwise will have to wait for buffer to fill up.
|
||||
p := buffer[:]
|
||||
|
||||
for {
|
||||
// Read 1 byte.
|
||||
n, err := reader.Read(p)
|
||||
if n <= 0 && nil == err {
|
||||
continue
|
||||
} else if n <= 0 && nil != err {
|
||||
break
|
||||
}
|
||||
|
||||
//logger.Tracef("Sending: %q.", p)
|
||||
//@TODO: Should we be checking for errors?
|
||||
oi.LongWrite(writer, p)
|
||||
//logger.Tracef("Sent: %q.", p)
|
||||
}
|
||||
}(logger)
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package telsh
|
||||
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-telnet"
|
||||
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func TestServeTELNETCommandNotFound(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
ClientSends string
|
||||
Expected string
|
||||
}{
|
||||
{
|
||||
ClientSends: "\r\n",
|
||||
Expected: "",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
ClientSends: "apple\r\n",
|
||||
Expected: "apple: command not found\r\n",
|
||||
},
|
||||
{
|
||||
ClientSends: "banana\r\n",
|
||||
Expected: "banana: command not found\r\n",
|
||||
},
|
||||
{
|
||||
ClientSends: "cherry\r\n",
|
||||
Expected: "cherry: command not found\r\n",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
ClientSends: "\t\r\n",
|
||||
Expected: "",
|
||||
},
|
||||
{
|
||||
ClientSends: "\t\t\r\n",
|
||||
Expected: "",
|
||||
},
|
||||
{
|
||||
ClientSends: "\t\t\t\r\n",
|
||||
Expected: "",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
ClientSends: " \r\n",
|
||||
Expected: "",
|
||||
},
|
||||
{
|
||||
ClientSends: " \r\n",
|
||||
Expected: "",
|
||||
},
|
||||
{
|
||||
ClientSends: " \r\n",
|
||||
Expected: "",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
ClientSends: " \t\r\n",
|
||||
Expected: "",
|
||||
},
|
||||
{
|
||||
ClientSends: "\t \r\n",
|
||||
Expected: "",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
ClientSends: "ls -alF\r\n",
|
||||
Expected: "ls: command not found\r\n",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
for testNumber, test := range tests {
|
||||
|
||||
shellHandler := NewShellHandler()
|
||||
if nil == shellHandler {
|
||||
t.Errorf("For test #%d, did not expect to get nil, but actually got it: %v; for client sent: %q", testNumber, shellHandler, test.ClientSends)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
ctx := telnet.NewContext()
|
||||
|
||||
var buffer bytes.Buffer
|
||||
|
||||
shellHandler.ServeTELNET(ctx, &buffer, strings.NewReader(test.ClientSends))
|
||||
|
||||
if expected, actual := shellHandler.WelcomeMessage+shellHandler.Prompt+test.Expected+shellHandler.Prompt+shellHandler.ExitMessage, buffer.String(); expected != actual {
|
||||
t.Errorf("For test #%d, expect %q, but actually got %q; for client sent: %q", testNumber, expected, actual, test.ClientSends)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package telnet
|
||||
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
)
|
||||
|
||||
|
||||
// ListenAndServeTLS acts identically to ListenAndServe, except that it
|
||||
// uses the TELNET protocol over TLS.
|
||||
//
|
||||
// From a TELNET protocol point-of-view, it allows for 'secured telnet', also known as TELNETS,
|
||||
// which by default listens to port 992.
|
||||
//
|
||||
// Of course, this port can be overridden using the 'addr' argument.
|
||||
//
|
||||
// For a very simple example:
|
||||
//
|
||||
// package main
|
||||
//
|
||||
// import (
|
||||
// "github.com/reiver/go-telnet"
|
||||
// )
|
||||
//
|
||||
// func main() {
|
||||
//
|
||||
// //@TODO: In your code, you would probably want to use a different handler.
|
||||
// var handler telnet.Handler = telnet.EchoHandler
|
||||
//
|
||||
// err := telnet.ListenAndServeTLS(":5555", "cert.pem", "key.pem", handler)
|
||||
// if nil != err {
|
||||
// //@TODO: Handle this error better.
|
||||
// panic(err)
|
||||
// }
|
||||
// }
|
||||
func ListenAndServeTLS(addr string, certFile string, keyFile string, handler Handler) error {
|
||||
server := &Server{Addr: addr, Handler: handler}
|
||||
return server.ListenAndServeTLS(certFile, keyFile)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ListenAndServeTLS acts identically to ListenAndServe, except that it
|
||||
// uses the TELNET protocol over TLS.
|
||||
//
|
||||
// From a TELNET protocol point-of-view, it allows for 'secured telnet', also known as TELNETS,
|
||||
// which by default listens to port 992.
|
||||
func (server *Server) ListenAndServeTLS(certFile string, keyFile string) error {
|
||||
|
||||
addr := server.Addr
|
||||
if "" == addr {
|
||||
addr = ":telnets"
|
||||
}
|
||||
|
||||
|
||||
listener, err := net.Listen("tcp", addr)
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
// Apparently have to make a copy of the TLS config this way, rather than by
|
||||
// simple assignment, to prevent some unexported fields from being copied over.
|
||||
//
|
||||
// It would be nice if tls.Config had a method that would do this "safely".
|
||||
// (I.e., what happens if in the future more exported fields are added to
|
||||
// tls.Config?)
|
||||
var tlsConfig *tls.Config = nil
|
||||
if nil == server.TLSConfig {
|
||||
tlsConfig = &tls.Config{}
|
||||
} else {
|
||||
tlsConfig = &tls.Config{
|
||||
Rand: server.TLSConfig.Rand,
|
||||
Time: server.TLSConfig.Time,
|
||||
Certificates: server.TLSConfig.Certificates,
|
||||
NameToCertificate: server.TLSConfig.NameToCertificate,
|
||||
GetCertificate: server.TLSConfig.GetCertificate,
|
||||
RootCAs: server.TLSConfig.RootCAs,
|
||||
NextProtos: server.TLSConfig.NextProtos,
|
||||
ServerName: server.TLSConfig.ServerName,
|
||||
ClientAuth: server.TLSConfig.ClientAuth,
|
||||
ClientCAs: server.TLSConfig.ClientCAs,
|
||||
InsecureSkipVerify: server.TLSConfig.InsecureSkipVerify,
|
||||
CipherSuites: server.TLSConfig.CipherSuites,
|
||||
PreferServerCipherSuites: server.TLSConfig.PreferServerCipherSuites,
|
||||
SessionTicketsDisabled: server.TLSConfig.SessionTicketsDisabled,
|
||||
SessionTicketKey: server.TLSConfig.SessionTicketKey,
|
||||
ClientSessionCache: server.TLSConfig.ClientSessionCache,
|
||||
MinVersion: server.TLSConfig.MinVersion,
|
||||
MaxVersion: server.TLSConfig.MaxVersion,
|
||||
CurvePreferences: server.TLSConfig.CurvePreferences,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tlsConfigHasCertificate := len(tlsConfig.Certificates) > 0 || nil != tlsConfig.GetCertificate
|
||||
if "" == certFile || "" == keyFile || !tlsConfigHasCertificate {
|
||||
tlsConfig.Certificates = make([]tls.Certificate, 1)
|
||||
|
||||
var err error
|
||||
tlsConfig.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tlsListener := tls.NewListener(listener, tlsConfig)
|
||||
|
||||
|
||||
return server.Serve(tlsListener)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package telnet
|
||||
|
||||
|
||||
type Writer interface {
|
||||
Write([]byte) (int, error)
|
||||
}
|
||||
14
proxy_go/public/logger_conf@v1.0.0/.gitignore
vendored
Normal file
14
proxy_go/public/logger_conf@v1.0.0/.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# Toolchain
|
||||
# Goland project folder
|
||||
.idea/
|
||||
# Visual Studio Code
|
||||
.vscode/
|
||||
# emacs/vim
|
||||
GPATH
|
||||
GRTAGS
|
||||
GTAGS
|
||||
TAGS
|
||||
tags
|
||||
cscope.*
|
||||
# mac
|
||||
.DS_Store
|
||||
289
proxy_go/public/logger_conf@v1.0.0/.golangci.yml
Normal file
289
proxy_go/public/logger_conf@v1.0.0/.golangci.yml
Normal file
@@ -0,0 +1,289 @@
|
||||
# This file contains all available configuration options
|
||||
# with their default values.
|
||||
# options for analysis running
|
||||
run:
|
||||
# default concurrency is a available CPU number
|
||||
concurrency: 4
|
||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||
timeout: 1m
|
||||
# exit code when at least one issue was found, default is 1
|
||||
issues-exit-code: 1
|
||||
# include test files or not, default is true
|
||||
tests: false
|
||||
# list of build tags, all linters use it. Default is empty list.
|
||||
build-tags:
|
||||
# which dirs to skip: issues from them won't be reported;
|
||||
# can use regexp here: generated.*, regexp is applied on full path;
|
||||
# default value is empty list, but default dirs are skipped independently
|
||||
# from this option's value (see skip-dirs-use-default).
|
||||
# "/" will be replaced by current OS file path separator to properly work
|
||||
# on Windows.
|
||||
skip-dirs:
|
||||
# default is true. Enables skipping of directories:
|
||||
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
||||
skip-dirs-use-default: true
|
||||
# which files to skip: they will be analyzed, but issues from them
|
||||
# won't be reported. Default value is empty list, but there is
|
||||
# no need to include all autogenerated files, we confidently recognize
|
||||
# autogenerated files. If it's not please let us know.
|
||||
# "/" will be replaced by current OS file path separator to properly work
|
||||
# on Windows.
|
||||
skip-files:
|
||||
- "api_.*\\.go$"
|
||||
- "model_.*\\.go$"
|
||||
- "routers.go"
|
||||
- "client.go"
|
||||
- "configuration.go"
|
||||
- "nas.go"
|
||||
# by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules":
|
||||
# If invoked with -mod=readonly, the go command is disallowed from the implicit
|
||||
# automatic updating of go.mod described above. Instead, it fails when any changes
|
||||
# to go.mod are needed. This setting is most useful to check that go.mod does
|
||||
# not need updates, such as in a continuous integration and testing system.
|
||||
# If invoked with -mod=vendor, the go command assumes that the vendor
|
||||
# directory holds the correct copies of dependencies and ignores
|
||||
# the dependency descriptions in go.mod.
|
||||
#modules-download-mode: readonly|release|vendor
|
||||
# Allow multiple parallel golangci-lint instances running.
|
||||
# If false (default) - golangci-lint acquires file lock on start.
|
||||
allow-parallel-runners: true
|
||||
# output configuration options
|
||||
output:
|
||||
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
|
||||
format: colored-line-number
|
||||
# print lines of code with issue, default is true
|
||||
print-issued-lines: true
|
||||
# print linter name in the end of issue text, default is true
|
||||
print-linter-name: true
|
||||
# make issues output unique by line, default is true
|
||||
uniq-by-line: true
|
||||
# all available settings of specific linters
|
||||
linters-settings:
|
||||
errcheck:
|
||||
# report about not checking of errors in type assertions: `a := b.(MyStruct)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-type-assertions: false
|
||||
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-blank: true
|
||||
# [deprecated] comma-separated list of pairs of the form pkg:regex
|
||||
# the regex is used to ignore names within pkg. (default "fmt:.*").
|
||||
# see https://github.com/kisielk/errcheck#the-deprecated-method for details
|
||||
#ignore: fmt:.*,io/ioutil:^Read.*
|
||||
# path to a file containing a list of functions to exclude from checking
|
||||
# see https://github.com/kisielk/errcheck#excluding-functions for details
|
||||
#exclude: /path/to/file.txt
|
||||
funlen:
|
||||
lines: 60
|
||||
statements: 40
|
||||
gocognit:
|
||||
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||
min-complexity: 10
|
||||
nestif:
|
||||
# minimal complexity of if statements to report, 5 by default
|
||||
min-complexity: 4
|
||||
goconst:
|
||||
# minimal length of string constant, 3 by default
|
||||
min-len: 3
|
||||
# minimal occurrences count to trigger, 3 by default
|
||||
min-occurrences: 3
|
||||
gocritic:
|
||||
# Which checks should be enabled; can't be combined with 'disabled-checks';
|
||||
# See https://go-critic.github.io/overview#checks-overview
|
||||
# To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run`
|
||||
# By default list of stable checks is used.
|
||||
enabled-checks:
|
||||
#- rangeValCopy
|
||||
# Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty
|
||||
disabled-checks:
|
||||
- regexpMust
|
||||
# Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.
|
||||
# Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
|
||||
enabled-tags:
|
||||
- performance
|
||||
disabled-tags:
|
||||
- experimental
|
||||
settings: # settings passed to gocritic
|
||||
captLocal: # must be valid enabled check name
|
||||
paramsOnly: true
|
||||
rangeValCopy:
|
||||
sizeThreshold: 32
|
||||
gocyclo:
|
||||
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||
min-complexity: 10
|
||||
godox:
|
||||
# report any comments starting with keywords, this is useful for TODO or FIXME comments that
|
||||
# might be left in the code accidentally and should be resolved before merging
|
||||
keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting
|
||||
#- TODO
|
||||
- FIXME
|
||||
- BUG
|
||||
#- NOTE
|
||||
#- OPTIMIZE # marks code that should be optimized before merging
|
||||
#- HACK # marks hack-arounds that should be removed before merging
|
||||
- XXX # Fatal! Important problem
|
||||
gofmt:
|
||||
# simplify code: gofmt with `-s` option, true by default
|
||||
simplify: true
|
||||
goimports:
|
||||
# put imports beginning with prefix after 3rd-party packages;
|
||||
# it's a comma-separated list of prefixes
|
||||
local-prefixes: github.com/org/project
|
||||
golint:
|
||||
# minimal confidence for issues, default is 0.8
|
||||
min-confidence: 0.8
|
||||
gomnd:
|
||||
settings:
|
||||
mnd:
|
||||
# the list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.
|
||||
checks: argument,case,condition,operation,return,assign
|
||||
gomodguard:
|
||||
allowed:
|
||||
modules: # List of allowed modules
|
||||
# - gopkg.in/yaml.v2
|
||||
domains: # List of allowed module domains
|
||||
# - golang.org
|
||||
blocked:
|
||||
modules: # List of blocked modules
|
||||
# - github.com/uudashr/go-module: # Blocked module
|
||||
# recommendations: # Recommended modules that should be used instead (Optional)
|
||||
# - golang.org/x/mod
|
||||
# reason: "`mod` is the official go.mod parser library." # Reason why the recommended module should be used (Optional)
|
||||
versions: # List of blocked module version constraints
|
||||
# - github.com/mitchellh/go-homedir: # Blocked module with version constraint
|
||||
# version: "< 1.1.0" # Version constraint, see https://github.com/Masterminds/semver#basic-comparisons
|
||||
# reason: "testing if blocked version constraint works." # Reason why the version constraint exists. (Optional)
|
||||
govet:
|
||||
# report about shadowed variables
|
||||
check-shadowing: true
|
||||
# settings per analyzer
|
||||
settings:
|
||||
printf: # analyzer name, run `go tool vet help` to see all analyzers
|
||||
funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
|
||||
# enable or disable analyzers by name
|
||||
enable:
|
||||
- atomicalign
|
||||
enable-all: false
|
||||
disable:
|
||||
- shadow
|
||||
disable-all: false
|
||||
depguard:
|
||||
list-type: blacklist
|
||||
include-go-root: false
|
||||
packages:
|
||||
- github.com/sirupsen/logrus
|
||||
packages-with-error-message:
|
||||
# specify an error message to output when a blacklisted package is used
|
||||
- github.com/sirupsen/logrus: "logging is allowed only by logutils.Log"
|
||||
lll:
|
||||
# max line length, lines longer will be reported. Default is 120.
|
||||
# '\t' is counted as 1 character by default, and can be changed with the tab-width option
|
||||
line-length: 120
|
||||
# tab width in spaces. Default to 1.
|
||||
tab-width: 1
|
||||
maligned:
|
||||
# print struct with more effective memory layout or not, false by default
|
||||
suggest-new: true
|
||||
nakedret:
|
||||
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
|
||||
max-func-lines: 30
|
||||
testpackage:
|
||||
# regexp pattern to skip files
|
||||
skip-regexp: (export|internal)_test\.go
|
||||
unused:
|
||||
# treat code as a program (not a library) and report unused exported identifiers; default is false.
|
||||
# XXX: if you enable this setting, unused will report a lot of false-positives in text editors:
|
||||
# if it's called for subdir of a project it can't find funcs usages. All text editor integrations
|
||||
# with golangci-lint call it on a directory with the changed file.
|
||||
check-exported: false
|
||||
whitespace:
|
||||
multi-if: false # Enforces newlines (or comments) after every multi-line if statement
|
||||
multi-func: false # Enforces newlines (or comments) after every multi-line function signature
|
||||
custom:
|
||||
# Each custom linter should have a unique name.
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- gofmt
|
||||
- govet
|
||||
- errcheck
|
||||
- staticcheck
|
||||
- unused
|
||||
- gosimple
|
||||
- structcheck
|
||||
- varcheck
|
||||
- ineffassign
|
||||
- deadcode
|
||||
- typecheck
|
||||
# Additional
|
||||
- lll
|
||||
- godox
|
||||
# - gomnd
|
||||
#- maligned
|
||||
#- nestif
|
||||
#- goconst
|
||||
#- gocognit
|
||||
- nakedret
|
||||
#disable-all: false
|
||||
fast: true
|
||||
issues:
|
||||
# List of regexps of issue texts to exclude, empty list by default.
|
||||
# But independently from this option we use default exclude patterns,
|
||||
# it can be disabled by `exclude-use-default: false`. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`
|
||||
exclude:
|
||||
# Excluding configuration per-path, per-linter, per-text and per-source
|
||||
exclude-rules:
|
||||
# Exclude some linters from running on tests files.
|
||||
# Independently from option `exclude` we use default exclude patterns,
|
||||
# it can be disabled by this option. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`.
|
||||
# Default value for this option is true.
|
||||
exclude-use-default: false
|
||||
# The default value is false. If set to true exclude and exclude-rules
|
||||
# regular expressions become case sensitive.
|
||||
exclude-case-sensitive: false
|
||||
# The list of ids of default excludes to include or disable. By default it's empty.
|
||||
include:
|
||||
#- EXC0002 # disable excluding of issues about comments from golint
|
||||
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
|
||||
#max-issues-per-linter: 0
|
||||
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
|
||||
#max-same-issues: 0
|
||||
# Show only new issues: if there are unstaged changes or untracked files,
|
||||
# only those changes are analyzed, else only changes in HEAD~ are analyzed.
|
||||
# It's a super-useful option for integration of golangci-lint into existing
|
||||
# large codebase. It's not practical to fix all existing issues at the moment
|
||||
# of integration: much better don't allow issues in new code.
|
||||
# Default is false.
|
||||
new: false
|
||||
# Show only new issues created after git revision `REV`
|
||||
new-from-rev: ""
|
||||
# Show only new issues created in git patch with set file path.
|
||||
#new-from-patch: path/to/patch/file
|
||||
severity:
|
||||
# Default value is empty string.
|
||||
# Set the default severity for issues. If severity rules are defined and the issues
|
||||
# do not match or no severity is provided to the rule this will be the default
|
||||
# severity applied. Severities should match the supported severity names of the
|
||||
# selected out format.
|
||||
# - Code climate: https://docs.codeclimate.com/docs/issues#issue-severity
|
||||
# - Checkstyle: https://checkstyle.sourceforge.io/property_types.html#severity
|
||||
# - Github: https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message
|
||||
default-severity: error
|
||||
# The default value is false.
|
||||
# If set to true severity-rules regular expressions become case sensitive.
|
||||
case-sensitive: false
|
||||
# Default value is empty list.
|
||||
# When a list of severity rules are provided, severity information will be added to lint
|
||||
# issues. Severity rules have the same filtering capability as exclude rules except you
|
||||
# are allowed to specify one matcher per severity rule.
|
||||
# Only affects out formats that support setting severity information.
|
||||
rules:
|
||||
- linters:
|
||||
- gomnd
|
||||
severity: ignore
|
||||
202
proxy_go/public/logger_conf@v1.0.0/LICENSE.txt
Normal file
202
proxy_go/public/logger_conf@v1.0.0/LICENSE.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2019 Communication Service/Software Laboratory, National Chiao Tung University
|
||||
|
||||
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.
|
||||
60
proxy_go/public/logger_conf@v1.0.0/conf.go
Normal file
60
proxy_go/public/logger_conf@v1.0.0/conf.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package logger_conf
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
//var Free5gcLogDir string = path_util.Free5gcPath("free5gc/log") + "/"
|
||||
var Free5gcLogDir string = "/var/log/"
|
||||
var LibLogDir string = Free5gcLogDir + "lib/"
|
||||
var NfLogDir string = Free5gcLogDir + "nf/"
|
||||
|
||||
var Free5gcLogFile string = Free5gcLogDir + "proxy_go.log"
|
||||
|
||||
func init() {
|
||||
if err := os.MkdirAll(LibLogDir, 0775); err != nil {
|
||||
log.Printf("Mkdir %s failed: %+v", LibLogDir, err)
|
||||
}
|
||||
if err := os.MkdirAll(NfLogDir, 0775); err != nil {
|
||||
log.Printf("Mkdir %s failed: %+v", NfLogDir, err)
|
||||
}
|
||||
|
||||
// Create log file or if it already exist, check if user can access it
|
||||
f, fileOpenErr := os.OpenFile(Free5gcLogFile, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
|
||||
if fileOpenErr != nil {
|
||||
// user cannot access it.
|
||||
log.Printf("Cannot Open %s\n", Free5gcLogFile)
|
||||
} else {
|
||||
// user can access it
|
||||
if err := f.Close(); err != nil {
|
||||
log.Printf("File %s cannot been closed\n", Free5gcLogFile)
|
||||
}
|
||||
}
|
||||
|
||||
sudoUID, errUID := strconv.Atoi(os.Getenv("SUDO_UID"))
|
||||
sudoGID, errGID := strconv.Atoi(os.Getenv("SUDO_GID"))
|
||||
|
||||
if errUID == nil && errGID == nil {
|
||||
// if using sudo to run the program, errUID will be nil and sudoUID will get the uid who run sudo
|
||||
// else errUID will not be nil and sudoUID will be nil
|
||||
// If user using sudo to run the program and create log file, log will own by root,
|
||||
// here we change own to user so user can view and reuse the file
|
||||
if err := os.Chown(Free5gcLogDir, sudoUID, sudoGID); err != nil {
|
||||
log.Printf("Dir %s chown to %d:%d error: %v\n", Free5gcLogDir, sudoUID, sudoGID, err)
|
||||
}
|
||||
if err := os.Chown(LibLogDir, sudoUID, sudoGID); err != nil {
|
||||
log.Printf("Dir %s chown to %d:%d error: %v\n", LibLogDir, sudoUID, sudoGID, err)
|
||||
}
|
||||
if err := os.Chown(NfLogDir, sudoUID, sudoGID); err != nil {
|
||||
log.Printf("Dir %s chown to %d:%d error: %v\n", NfLogDir, sudoUID, sudoGID, err)
|
||||
}
|
||||
|
||||
if fileOpenErr == nil {
|
||||
if err := os.Chown(Free5gcLogFile, sudoUID, sudoGID); err != nil {
|
||||
log.Printf("File %s chown to %d:%d error: %v\n", Free5gcLogFile, sudoUID, sudoGID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
proxy_go/public/logger_conf@v1.0.0/go.sum
Normal file
22
proxy_go/public/logger_conf@v1.0.0/go.sum
Normal file
@@ -0,0 +1,22 @@
|
||||
github.com/free5gc/path_util v0.0.0-20201212235135-42ce24f2b9c9 h1:GzLy+VsNFUruyrgM5GECl6QEq2/43rZQxvrjEFWuXQI=
|
||||
github.com/free5gc/path_util v0.0.0-20201212235135-42ce24f2b9c9/go.mod h1:8KT1mRTxzh0STAR4juLuvsCo3y1kTUHQ9X+OIeagU4k=
|
||||
github.com/antonfisher/nested-logrus-formatter v1.3.0 h1:8zixYquU1Odk+vzAaAQPAdRh1ZjmUXNQ1T+dUBvlhVo=
|
||||
github.com/antonfisher/nested-logrus-formatter v1.3.0/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201211090839-8ad439b19e0f h1:QdHQnPce6K4XQewki9WNbG5KOROuDzqO3NaYjI1cXJ0=
|
||||
golang.org/x/sys v0.0.0-20201211090839-8ad439b19e0f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
15
proxy_go/public/logger_util@v1.0.0/.gitignore
vendored
Normal file
15
proxy_go/public/logger_util@v1.0.0/.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# Toolchain
|
||||
# Goland project folder
|
||||
.idea/
|
||||
# Visual Studio Code
|
||||
.vscode/
|
||||
# emacs/vim
|
||||
GPATH
|
||||
GRTAGS
|
||||
GTAGS
|
||||
TAGS
|
||||
tags
|
||||
cscope.*
|
||||
# mac
|
||||
.DS_Store
|
||||
|
||||
289
proxy_go/public/logger_util@v1.0.0/.golangci.yml
Normal file
289
proxy_go/public/logger_util@v1.0.0/.golangci.yml
Normal file
@@ -0,0 +1,289 @@
|
||||
# This file contains all available configuration options
|
||||
# with their default values.
|
||||
# options for analysis running
|
||||
run:
|
||||
# default concurrency is a available CPU number
|
||||
concurrency: 4
|
||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||
timeout: 1m
|
||||
# exit code when at least one issue was found, default is 1
|
||||
issues-exit-code: 1
|
||||
# include test files or not, default is true
|
||||
tests: false
|
||||
# list of build tags, all linters use it. Default is empty list.
|
||||
build-tags:
|
||||
# which dirs to skip: issues from them won't be reported;
|
||||
# can use regexp here: generated.*, regexp is applied on full path;
|
||||
# default value is empty list, but default dirs are skipped independently
|
||||
# from this option's value (see skip-dirs-use-default).
|
||||
# "/" will be replaced by current OS file path separator to properly work
|
||||
# on Windows.
|
||||
skip-dirs:
|
||||
# default is true. Enables skipping of directories:
|
||||
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
||||
skip-dirs-use-default: true
|
||||
# which files to skip: they will be analyzed, but issues from them
|
||||
# won't be reported. Default value is empty list, but there is
|
||||
# no need to include all autogenerated files, we confidently recognize
|
||||
# autogenerated files. If it's not please let us know.
|
||||
# "/" will be replaced by current OS file path separator to properly work
|
||||
# on Windows.
|
||||
skip-files:
|
||||
- "api_.*\\.go$"
|
||||
- "model_.*\\.go$"
|
||||
- "routers.go"
|
||||
- "client.go"
|
||||
- "configuration.go"
|
||||
- "nas.go"
|
||||
# by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules":
|
||||
# If invoked with -mod=readonly, the go command is disallowed from the implicit
|
||||
# automatic updating of go.mod described above. Instead, it fails when any changes
|
||||
# to go.mod are needed. This setting is most useful to check that go.mod does
|
||||
# not need updates, such as in a continuous integration and testing system.
|
||||
# If invoked with -mod=vendor, the go command assumes that the vendor
|
||||
# directory holds the correct copies of dependencies and ignores
|
||||
# the dependency descriptions in go.mod.
|
||||
#modules-download-mode: readonly|release|vendor
|
||||
# Allow multiple parallel golangci-lint instances running.
|
||||
# If false (default) - golangci-lint acquires file lock on start.
|
||||
allow-parallel-runners: true
|
||||
# output configuration options
|
||||
output:
|
||||
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
|
||||
format: colored-line-number
|
||||
# print lines of code with issue, default is true
|
||||
print-issued-lines: true
|
||||
# print linter name in the end of issue text, default is true
|
||||
print-linter-name: true
|
||||
# make issues output unique by line, default is true
|
||||
uniq-by-line: true
|
||||
# all available settings of specific linters
|
||||
linters-settings:
|
||||
errcheck:
|
||||
# report about not checking of errors in type assertions: `a := b.(MyStruct)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-type-assertions: false
|
||||
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-blank: true
|
||||
# [deprecated] comma-separated list of pairs of the form pkg:regex
|
||||
# the regex is used to ignore names within pkg. (default "fmt:.*").
|
||||
# see https://github.com/kisielk/errcheck#the-deprecated-method for details
|
||||
#ignore: fmt:.*,io/ioutil:^Read.*
|
||||
# path to a file containing a list of functions to exclude from checking
|
||||
# see https://github.com/kisielk/errcheck#excluding-functions for details
|
||||
#exclude: /path/to/file.txt
|
||||
funlen:
|
||||
lines: 60
|
||||
statements: 40
|
||||
gocognit:
|
||||
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||
min-complexity: 10
|
||||
nestif:
|
||||
# minimal complexity of if statements to report, 5 by default
|
||||
min-complexity: 4
|
||||
goconst:
|
||||
# minimal length of string constant, 3 by default
|
||||
min-len: 3
|
||||
# minimal occurrences count to trigger, 3 by default
|
||||
min-occurrences: 3
|
||||
gocritic:
|
||||
# Which checks should be enabled; can't be combined with 'disabled-checks';
|
||||
# See https://go-critic.github.io/overview#checks-overview
|
||||
# To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run`
|
||||
# By default list of stable checks is used.
|
||||
enabled-checks:
|
||||
#- rangeValCopy
|
||||
# Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty
|
||||
disabled-checks:
|
||||
- regexpMust
|
||||
# Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.
|
||||
# Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
|
||||
enabled-tags:
|
||||
- performance
|
||||
disabled-tags:
|
||||
- experimental
|
||||
settings: # settings passed to gocritic
|
||||
captLocal: # must be valid enabled check name
|
||||
paramsOnly: true
|
||||
rangeValCopy:
|
||||
sizeThreshold: 32
|
||||
gocyclo:
|
||||
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||
min-complexity: 10
|
||||
godox:
|
||||
# report any comments starting with keywords, this is useful for TODO or FIXME comments that
|
||||
# might be left in the code accidentally and should be resolved before merging
|
||||
keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting
|
||||
#- TODO
|
||||
- FIXME
|
||||
- BUG
|
||||
#- NOTE
|
||||
#- OPTIMIZE # marks code that should be optimized before merging
|
||||
#- HACK # marks hack-arounds that should be removed before merging
|
||||
- XXX # Fatal! Important problem
|
||||
gofmt:
|
||||
# simplify code: gofmt with `-s` option, true by default
|
||||
simplify: true
|
||||
goimports:
|
||||
# put imports beginning with prefix after 3rd-party packages;
|
||||
# it's a comma-separated list of prefixes
|
||||
local-prefixes: github.com/org/project
|
||||
golint:
|
||||
# minimal confidence for issues, default is 0.8
|
||||
min-confidence: 0.8
|
||||
gomnd:
|
||||
settings:
|
||||
mnd:
|
||||
# the list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.
|
||||
checks: argument,case,condition,operation,return,assign
|
||||
gomodguard:
|
||||
allowed:
|
||||
modules: # List of allowed modules
|
||||
# - gopkg.in/yaml.v2
|
||||
domains: # List of allowed module domains
|
||||
# - golang.org
|
||||
blocked:
|
||||
modules: # List of blocked modules
|
||||
# - github.com/uudashr/go-module: # Blocked module
|
||||
# recommendations: # Recommended modules that should be used instead (Optional)
|
||||
# - golang.org/x/mod
|
||||
# reason: "`mod` is the official go.mod parser library." # Reason why the recommended module should be used (Optional)
|
||||
versions: # List of blocked module version constraints
|
||||
# - github.com/mitchellh/go-homedir: # Blocked module with version constraint
|
||||
# version: "< 1.1.0" # Version constraint, see https://github.com/Masterminds/semver#basic-comparisons
|
||||
# reason: "testing if blocked version constraint works." # Reason why the version constraint exists. (Optional)
|
||||
govet:
|
||||
# report about shadowed variables
|
||||
check-shadowing: true
|
||||
# settings per analyzer
|
||||
settings:
|
||||
printf: # analyzer name, run `go tool vet help` to see all analyzers
|
||||
funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
|
||||
# enable or disable analyzers by name
|
||||
enable:
|
||||
- atomicalign
|
||||
enable-all: false
|
||||
disable:
|
||||
- shadow
|
||||
disable-all: false
|
||||
depguard:
|
||||
list-type: blacklist
|
||||
include-go-root: false
|
||||
packages:
|
||||
- github.com/sirupsen/logrus
|
||||
packages-with-error-message:
|
||||
# specify an error message to output when a blacklisted package is used
|
||||
- github.com/sirupsen/logrus: "logging is allowed only by logutils.Log"
|
||||
lll:
|
||||
# max line length, lines longer will be reported. Default is 120.
|
||||
# '\t' is counted as 1 character by default, and can be changed with the tab-width option
|
||||
line-length: 120
|
||||
# tab width in spaces. Default to 1.
|
||||
tab-width: 1
|
||||
maligned:
|
||||
# print struct with more effective memory layout or not, false by default
|
||||
suggest-new: true
|
||||
nakedret:
|
||||
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
|
||||
max-func-lines: 30
|
||||
testpackage:
|
||||
# regexp pattern to skip files
|
||||
skip-regexp: (export|internal)_test\.go
|
||||
unused:
|
||||
# treat code as a program (not a library) and report unused exported identifiers; default is false.
|
||||
# XXX: if you enable this setting, unused will report a lot of false-positives in text editors:
|
||||
# if it's called for subdir of a project it can't find funcs usages. All text editor integrations
|
||||
# with golangci-lint call it on a directory with the changed file.
|
||||
check-exported: false
|
||||
whitespace:
|
||||
multi-if: false # Enforces newlines (or comments) after every multi-line if statement
|
||||
multi-func: false # Enforces newlines (or comments) after every multi-line function signature
|
||||
custom:
|
||||
# Each custom linter should have a unique name.
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- gofmt
|
||||
- govet
|
||||
- errcheck
|
||||
- staticcheck
|
||||
- unused
|
||||
- gosimple
|
||||
- structcheck
|
||||
- varcheck
|
||||
- ineffassign
|
||||
- deadcode
|
||||
- typecheck
|
||||
# Additional
|
||||
- lll
|
||||
- godox
|
||||
# - gomnd
|
||||
#- maligned
|
||||
#- nestif
|
||||
#- goconst
|
||||
#- gocognit
|
||||
- nakedret
|
||||
#disable-all: false
|
||||
fast: true
|
||||
issues:
|
||||
# List of regexps of issue texts to exclude, empty list by default.
|
||||
# But independently from this option we use default exclude patterns,
|
||||
# it can be disabled by `exclude-use-default: false`. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`
|
||||
exclude:
|
||||
# Excluding configuration per-path, per-linter, per-text and per-source
|
||||
exclude-rules:
|
||||
# Exclude some linters from running on tests files.
|
||||
# Independently from option `exclude` we use default exclude patterns,
|
||||
# it can be disabled by this option. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`.
|
||||
# Default value for this option is true.
|
||||
exclude-use-default: false
|
||||
# The default value is false. If set to true exclude and exclude-rules
|
||||
# regular expressions become case sensitive.
|
||||
exclude-case-sensitive: false
|
||||
# The list of ids of default excludes to include or disable. By default it's empty.
|
||||
include:
|
||||
#- EXC0002 # disable excluding of issues about comments from golint
|
||||
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
|
||||
#max-issues-per-linter: 0
|
||||
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
|
||||
#max-same-issues: 0
|
||||
# Show only new issues: if there are unstaged changes or untracked files,
|
||||
# only those changes are analyzed, else only changes in HEAD~ are analyzed.
|
||||
# It's a super-useful option for integration of golangci-lint into existing
|
||||
# large codebase. It's not practical to fix all existing issues at the moment
|
||||
# of integration: much better don't allow issues in new code.
|
||||
# Default is false.
|
||||
new: false
|
||||
# Show only new issues created after git revision `REV`
|
||||
new-from-rev: ""
|
||||
# Show only new issues created in git patch with set file path.
|
||||
#new-from-patch: path/to/patch/file
|
||||
severity:
|
||||
# Default value is empty string.
|
||||
# Set the default severity for issues. If severity rules are defined and the issues
|
||||
# do not match or no severity is provided to the rule this will be the default
|
||||
# severity applied. Severities should match the supported severity names of the
|
||||
# selected out format.
|
||||
# - Code climate: https://docs.codeclimate.com/docs/issues#issue-severity
|
||||
# - Checkstyle: https://checkstyle.sourceforge.io/property_types.html#severity
|
||||
# - Github: https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message
|
||||
default-severity: error
|
||||
# The default value is false.
|
||||
# If set to true severity-rules regular expressions become case sensitive.
|
||||
case-sensitive: false
|
||||
# Default value is empty list.
|
||||
# When a list of severity rules are provided, severity information will be added to lint
|
||||
# issues. Severity rules have the same filtering capability as exclude rules except you
|
||||
# are allowed to specify one matcher per severity rule.
|
||||
# Only affects out formats that support setting severity information.
|
||||
rules:
|
||||
- linters:
|
||||
- gomnd
|
||||
severity: ignore
|
||||
9
proxy_go/public/logger_util@v1.0.0/CHANGELOG.md
Normal file
9
proxy_go/public/logger_util@v1.0.0/CHANGELOG.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Change Log
|
||||
---
|
||||
2020-03-xx-xx
|
||||
---
|
||||
- Implemented enchacements:
|
||||
|
||||
- Fixed bugs:
|
||||
|
||||
- Closed issues:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user