├── vendor ├── github.com │ ├── go-web │ │ ├── httplog │ │ │ ├── doc.go │ │ │ ├── README.md │ │ │ ├── AUTHORS │ │ │ ├── buff.go │ │ │ ├── example_test.go │ │ │ ├── recorder_test.go │ │ │ ├── request_test.go │ │ │ ├── default_test.go │ │ │ ├── request.go │ │ │ ├── error.go │ │ │ ├── apache_test.go │ │ │ ├── default.go │ │ │ ├── LICENSE │ │ │ ├── error_test.go │ │ │ ├── apache.go │ │ │ └── recorder.go │ │ └── httpmux │ │ │ ├── AUTHORS │ │ │ ├── example_test.go │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ └── httpmux_test.go │ ├── julienschmidt │ │ └── httprouter │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── path_test.go │ │ │ └── path.go │ └── fiorix │ │ └── go-smpp │ │ ├── smpp │ │ ├── pdu │ │ │ ├── doc.go │ │ │ ├── pdufield │ │ │ │ ├── doc.go │ │ │ │ ├── tlvbody.go │ │ │ │ ├── body_test.go │ │ │ │ ├── body.go │ │ │ │ ├── map.go │ │ │ │ ├── map_test.go │ │ │ │ ├── list_test.go │ │ │ │ ├── types_test.go │ │ │ │ ├── list.go │ │ │ │ └── types.go │ │ │ ├── pdutext │ │ │ │ ├── raw.go │ │ │ │ ├── doc.go │ │ │ │ ├── latin1.go │ │ │ │ ├── ucs2.go │ │ │ │ ├── raw_test.go │ │ │ │ ├── latin1_test.go │ │ │ │ ├── ucs2_test.go │ │ │ │ ├── codec.go │ │ │ │ └── codec_test.go │ │ │ ├── body.go │ │ │ ├── header_test.go │ │ │ ├── types_test.go │ │ │ ├── codec.go │ │ │ └── header.go │ │ ├── doc.go │ │ ├── smpptest │ │ │ ├── doc.go │ │ │ ├── conn.go │ │ │ ├── server_test.go │ │ │ └── server.go │ │ ├── conn_test.go │ │ ├── receiver_test.go │ │ ├── transceiver.go │ │ ├── transceiver_test.go │ │ ├── receiver.go │ │ ├── example_test.go │ │ ├── conn.go │ │ ├── transmitter_test.go │ │ └── client.go │ │ └── LICENSE └── golang.org │ └── x │ ├── net │ ├── websocket │ │ ├── examplehandler_test.go │ │ ├── exampledial_test.go │ │ ├── client.go │ │ └── server.go │ ├── context │ │ ├── withtimeout_test.go │ │ ├── go17.go │ │ └── context.go │ ├── PATENTS │ └── LICENSE │ └── text │ ├── transform │ └── examples_test.go │ ├── encoding │ ├── example_test.go │ ├── internal │ │ ├── internal.go │ │ └── identifier │ │ │ ├── identifier.go │ │ │ └── gen.go │ ├── unicode │ │ └── override.go │ ├── encoding.go │ └── charmap │ │ └── charmap.go │ ├── PATENTS │ └── LICENSE ├── Godeps ├── Readme └── Godeps.json ├── .travis.yml ├── Dockerfile ├── apiserver ├── doc.go ├── auth_test.go ├── auth.go ├── pool_test.go ├── form_test.go ├── cors.go ├── form.go ├── httpws_test.go ├── main_test.go ├── pool.go ├── cors_test.go ├── rpc_test.go ├── rpc.go └── http.go ├── AUTHORS ├── k8s └── smsapi.yaml ├── LICENSE ├── README.md ├── main.go └── index.html /vendor/github.com/go-web/httplog/doc.go: -------------------------------------------------------------------------------- 1 | // Package httplog provides logging for http requests. 2 | package httplog 3 | -------------------------------------------------------------------------------- /vendor/github.com/julienschmidt/httprouter/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: go 3 | go: 4 | - 1.1 5 | - 1.2 6 | - 1.3 7 | - 1.4 8 | - tip 9 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.6 4 | env: 5 | - GO15VENDOREXPERIMENT=1 6 | install: 7 | - go get -d -v golang.org/x/tools/cmd/cover 8 | script: 9 | - go test -v -cover ./apiserver 10 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httplog/README.md: -------------------------------------------------------------------------------- 1 | # httplog 2 | 3 | [![GoDoc](https://godoc.org/github.com/go-web/httplog?status.svg)](http://godoc.org/github.com/go-web/httplog) 4 | 5 | Package httplog provides log facilitites for http servers written in Go. 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.6 2 | 3 | ADD . /go/src/github.com/fiorix/sms-api-server 4 | COPY index.html /pub/index.html 5 | RUN GO15VENDOREXPERIMENT=1 go install github.com/fiorix/sms-api-server 6 | 7 | EXPOSE 8080 8 | ENTRYPOINT ["/go/bin/sms-api-server", "-public", "/pub"] 9 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Package pdu provide codecs for binary PDU data. 6 | package pdu 7 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Package smpp is an implementation of the SMPP 3.4 protocol. 6 | package smpp 7 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/smpptest/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Package smpptest provides an SMPP test server. 6 | package smpptest 7 | -------------------------------------------------------------------------------- /apiserver/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 sms-api-server authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Package apiserver provides an HTTP handler for sending and receiving 6 | // SMS via an SMSC. 7 | package apiserver 8 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdufield/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Package pdufield provides PDU field codecs and utilities. 6 | package pdufield 7 | -------------------------------------------------------------------------------- /apiserver/auth_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 sms-api-server authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package apiserver 6 | 7 | import "testing" 8 | 9 | func TestAuth(t *testing.T) { 10 | auth(nil) 11 | } 12 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of sms-api-server authors for copyright purposes. 2 | # This file is distinct from the CONTRIBUTORS file. 3 | # 4 | # Names should be added to this file as 5 | # Name or Organization 6 | # 7 | # The email address is not required for organizations. 8 | # 9 | # Please keep the list sorted. 10 | 11 | Alexandre Fiori 12 | -------------------------------------------------------------------------------- /apiserver/auth.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 sms-api-server authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package apiserver 6 | 7 | import "net/http" 8 | 9 | // auth provides an authentication handler. Hook up your own stuff here. 10 | func auth(f http.HandlerFunc) http.HandlerFunc { 11 | return f 12 | } 13 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httplog/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of httplog authors for copyright purposes. 2 | # This file is distinct from the CONTRIBUTORS file. 3 | # 4 | # Names should be added to this file as 5 | # Name or Organization 6 | # 7 | # The email address is not required for organizations. 8 | # 9 | # Please keep the list sorted. 10 | 11 | Alexandre Fiori 12 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httpmux/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of httpmux authors for copyright purposes. 2 | # This file is distinct from the CONTRIBUTORS file. 3 | # 4 | # Names should be added to this file as 5 | # Name or Organization 6 | # 7 | # The email address is not required for organizations. 8 | # 9 | # Please keep the list sorted. 10 | 11 | Alexandre Fiori 12 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httplog/buff.go: -------------------------------------------------------------------------------- 1 | package httplog 2 | 3 | import ( 4 | "bytes" 5 | "sync" 6 | ) 7 | 8 | var logBuffer = &sync.Pool{} 9 | 10 | func getBuffer() *bytes.Buffer { 11 | if b, ok := logBuffer.Get().(*bytes.Buffer); ok { 12 | b.Reset() 13 | return b 14 | } 15 | return &bytes.Buffer{} 16 | } 17 | 18 | func putBuffer(b *bytes.Buffer) { 19 | if b.Len() <= 1<<10 { 20 | logBuffer.Put(b) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httplog/example_test.go: -------------------------------------------------------------------------------- 1 | package httplog_test 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "os" 7 | 8 | "github.com/go-web/httplog" 9 | "github.com/go-web/httpmux" 10 | ) 11 | 12 | func ExampleLog() { 13 | logger := log.New(os.Stdout, "[go-web] ", log.LstdFlags) 14 | mux := httpmux.New() 15 | mux.UseFunc(httplog.DefaultFormat(logger)) 16 | mux.GET("/", func(w http.ResponseWriter, r *http.Request) { 17 | httplog.Errorf(r, "Todos são manos, eeei. Todos são hu-manos.") 18 | }) 19 | http.ListenAndServe(":8080", mux) 20 | } 21 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdutext/raw.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdutext 6 | 7 | // Raw text codec, no encoding. 8 | type Raw []byte 9 | 10 | // Type implements the Codec interface. 11 | func (s Raw) Type() DataCoding { 12 | return 0x00 13 | } 14 | 15 | // Encode raw text. 16 | func (s Raw) Encode() []byte { 17 | return s 18 | } 19 | 20 | // Decode raw text. 21 | func (s Raw) Decode() []byte { 22 | return s 23 | } 24 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httplog/recorder_test.go: -------------------------------------------------------------------------------- 1 | package httplog 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | "net/http/httptest" 7 | "testing" 8 | ) 9 | 10 | func TestResponseRecorder(t *testing.T) { 11 | w := NewResponseWriter(&httptest.ResponseRecorder{}) 12 | w.Header().Set("X-test", "foobar") 13 | w.WriteHeader(http.StatusNotFound) 14 | io.WriteString(w, "hello, world") 15 | switch { 16 | case w.Code() != http.StatusNotFound: 17 | t.Fatalf("Unexpected status code. Want %d, have %d", 18 | http.StatusNotFound, w.Code()) 19 | case w.Bytes() != 12: 20 | t.Fatalf("Unexpected # of bytes. Want 12, have %d", w.Bytes()) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdutext/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Package pdutext provides text conversion for PDU fields. 6 | // 7 | // See 2.2.2 from http://opensmpp.org/specs/smppv34_gsmumts_ig_v10.pdf 8 | // for details. 9 | // 10 | // pdutext supports Latin1 (0x03) and UCS2 (0x08). 11 | // 12 | // Latin1 encoding is Windows-1252 (CP1252) for now, not ISO-8859-1. 13 | // http://www.i18nqa.com/debug/table-iso8859-1-vs-windows-1252.html 14 | // 15 | // UCS2 is UTF-16-BE. Here be dragons. 16 | // 17 | // TODO(fiorix): Fix this. 18 | package pdutext 19 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/websocket/examplehandler_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket_test 6 | 7 | import ( 8 | "io" 9 | "net/http" 10 | 11 | "golang.org/x/net/websocket" 12 | ) 13 | 14 | // Echo the data received on the WebSocket. 15 | func EchoServer(ws *websocket.Conn) { 16 | io.Copy(ws, ws) 17 | } 18 | 19 | // This example demonstrates a trivial echo server. 20 | func ExampleHandler() { 21 | http.Handle("/echo", websocket.Handler(EchoServer)) 22 | err := http.ListenAndServe(":12345", nil) 23 | if err != nil { 24 | panic("ListenAndServe: " + err.Error()) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httplog/request_test.go: -------------------------------------------------------------------------------- 1 | package httplog 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "net/url" 7 | "testing" 8 | ) 9 | 10 | func TestUseXForwardedFor(t *testing.T) { 11 | want := "4.2.2.2:1905" 12 | var have string 13 | f := func(w http.ResponseWriter, r *http.Request) { 14 | have = r.RemoteAddr 15 | } 16 | r := &http.Request{ 17 | Method: "GET", 18 | URL: &url.URL{Path: "/"}, 19 | RemoteAddr: "[::1]:1905", 20 | Header: http.Header{ 21 | "X-Forwarded-For": {"4.2.2.2"}, 22 | }, 23 | } 24 | w := &httptest.ResponseRecorder{} 25 | UseXForwardedFor(f).ServeHTTP(w, r) 26 | if have != want { 27 | t.Fatalf("Unexpected value for RemoteAddr. Want %q, have %q", 28 | want, have) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /k8s/smsapi.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: smsapi 5 | spec: 6 | ports: 7 | - port: 80 8 | targetPort: 8080 9 | selector: 10 | name: smsapi 11 | type: LoadBalancer 12 | --- 13 | apiVersion: v1 14 | kind: ReplicationController 15 | metadata: 16 | name: smsapi 17 | spec: 18 | replicas: 1 19 | template: 20 | metadata: 21 | labels: 22 | name: smsapi 23 | spec: 24 | containers: 25 | - name: smsapi 26 | image: fiorix/sms-api-server 27 | ports: 28 | - containerPort: 8080 29 | env: 30 | - name: SMPP_USER 31 | value: foobar 32 | - name: SMPP_PASSWD 33 | value: c-krit 34 | args: 35 | - --tls 36 | - --smpp=smpp-provider-hostname:port 37 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/context/withtimeout_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package context_test 6 | 7 | import ( 8 | "fmt" 9 | "time" 10 | 11 | "golang.org/x/net/context" 12 | ) 13 | 14 | func ExampleWithTimeout() { 15 | // Pass a context with a timeout to tell a blocking function that it 16 | // should abandon its work after the timeout elapses. 17 | ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond) 18 | select { 19 | case <-time.After(200 * time.Millisecond): 20 | fmt.Println("overslept") 21 | case <-ctx.Done(): 22 | fmt.Println(ctx.Err()) // prints "context deadline exceeded" 23 | } 24 | // Output: 25 | // context deadline exceeded 26 | } 27 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/websocket/exampledial_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | 11 | "golang.org/x/net/websocket" 12 | ) 13 | 14 | // This example demonstrates a trivial client. 15 | func ExampleDial() { 16 | origin := "http://localhost/" 17 | url := "ws://localhost:12345/ws" 18 | ws, err := websocket.Dial(url, "", origin) 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | if _, err := ws.Write([]byte("hello, world!\n")); err != nil { 23 | log.Fatal(err) 24 | } 25 | var msg = make([]byte, 512) 26 | var n int 27 | if n, err = ws.Read(msg); err != nil { 28 | log.Fatal(err) 29 | } 30 | fmt.Printf("Received: %s.\n", msg[:n]) 31 | } 32 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httplog/default_test.go: -------------------------------------------------------------------------------- 1 | package httplog 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "log" 7 | "net/http" 8 | "net/http/httptest" 9 | "net/url" 10 | "strings" 11 | "testing" 12 | 13 | "github.com/go-web/httpmux" 14 | ) 15 | 16 | func TestDefaultFormat(t *testing.T) { 17 | var b bytes.Buffer 18 | l := log.New(&b, "", 0) 19 | mux := httpmux.New() 20 | mux.UseFunc(DefaultFormat(l)) 21 | mux.GET("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 22 | w.WriteHeader(http.StatusNotFound) 23 | io.WriteString(w, "foobar") 24 | })) 25 | r := &http.Request{ 26 | Proto: "HTTP/1.1", 27 | Method: "GET", 28 | URL: &url.URL{Path: "/"}, 29 | } 30 | w := &httptest.ResponseRecorder{} 31 | mux.ServeHTTP(w, r) 32 | // TODO: Test the format. 33 | if !strings.HasPrefix(b.String(), "HTTP/1.1 404") { 34 | t.Fatalf("Unexpected data. Want HTTP/1.1 404, have %q", b.String()) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/conn_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package smpp 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/fiorix/go-smpp/smpp/pdu" 11 | "github.com/fiorix/go-smpp/smpp/pdu/pdufield" 12 | "github.com/fiorix/go-smpp/smpp/smpptest" 13 | ) 14 | 15 | func TestConn(t *testing.T) { 16 | s := smpptest.NewServer() 17 | defer s.Close() 18 | c, err := Dial(s.Addr(), nil) 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | defer c.Close() 23 | p := pdu.NewBindTransmitter() 24 | f := p.Fields() 25 | f.Set(pdufield.SystemID, smpptest.DefaultUser) 26 | f.Set(pdufield.Password, smpptest.DefaultPasswd) 27 | f.Set(pdufield.InterfaceVersion, 0x34) 28 | if err = c.Write(p); err != nil { 29 | t.Fatal(err) 30 | } 31 | if _, err = c.Read(); err != nil { 32 | t.Fatal(err) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdutext/latin1.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdutext 6 | 7 | import ( 8 | "golang.org/x/text/encoding/charmap" 9 | "golang.org/x/text/transform" 10 | ) 11 | 12 | // Latin1 text codec. 13 | type Latin1 []byte 14 | 15 | // Type implements the Codec interface. 16 | func (s Latin1) Type() DataCoding { 17 | return Latin1Type 18 | } 19 | 20 | // Encode to Latin1. 21 | func (s Latin1) Encode() []byte { 22 | e := charmap.Windows1252.NewEncoder() 23 | es, _, err := transform.Bytes(e, s) 24 | if err != nil { 25 | return s 26 | } 27 | return es 28 | } 29 | 30 | // Decode from Latin1. 31 | func (s Latin1) Decode() []byte { 32 | e := charmap.Windows1252.NewDecoder() 33 | es, _, err := transform.Bytes(e, s) 34 | if err != nil { 35 | return s 36 | } 37 | return es 38 | } 39 | -------------------------------------------------------------------------------- /apiserver/pool_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 sms-api-server authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package apiserver 6 | 7 | import ( 8 | "testing" 9 | "time" 10 | 11 | "github.com/fiorix/go-smpp/smpp/pdu" 12 | "github.com/fiorix/go-smpp/smpp/pdu/pdufield" 13 | ) 14 | 15 | func TestDeliveryStore(t *testing.T) { 16 | want := "delivery receipt" 17 | pool := newPool() 18 | id, dr := pool.Register() 19 | defer pool.Unregister(id) 20 | p := pdu.NewDeliverSM() 21 | f := p.Fields() 22 | f.Set(pdufield.SourceAddr, "bart") 23 | f.Set(pdufield.DestinationAddr, "lisa") 24 | f.Set(pdufield.ShortMessage, want) 25 | pool.Handler(p) 26 | select { 27 | case r := <-dr: 28 | if r.Text != want { 29 | t.Fatalf("unexpected message: want %q, have %q", 30 | want, r.Text) 31 | } 32 | case <-time.After(2 * time.Second): 33 | t.Fatal("timeout waiting for delivery receipt") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /vendor/golang.org/x/text/transform/examples_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package transform_test 6 | 7 | import ( 8 | "fmt" 9 | "unicode" 10 | 11 | "golang.org/x/text/transform" 12 | "golang.org/x/text/unicode/norm" 13 | ) 14 | 15 | func ExampleRemoveFunc() { 16 | input := []byte(`tschüß; до свидания`) 17 | 18 | b := make([]byte, len(input)) 19 | 20 | t := transform.RemoveFunc(unicode.IsSpace) 21 | n, _, _ := t.Transform(b, input, true) 22 | fmt.Println(string(b[:n])) 23 | 24 | t = transform.RemoveFunc(func(r rune) bool { 25 | return !unicode.Is(unicode.Latin, r) 26 | }) 27 | n, _, _ = t.Transform(b, input, true) 28 | fmt.Println(string(b[:n])) 29 | 30 | n, _, _ = t.Transform(b, norm.NFD.Bytes(input), true) 31 | fmt.Println(string(b[:n])) 32 | 33 | // Output: 34 | // tschüß;досвидания 35 | // tschüß 36 | // tschuß 37 | } 38 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdufield/tlvbody.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdufield 6 | 7 | import ( 8 | "encoding/binary" 9 | "io" 10 | ) 11 | 12 | // TODO(fiorix): Implement TLV parameters. 13 | 14 | // TLVType is the Tag Length Value. 15 | type TLVType uint16 16 | 17 | // TLVBody represents data of a TLV field. 18 | type TLVBody struct { 19 | Tag TLVType 20 | Len uint16 21 | data []byte 22 | } 23 | 24 | // Bytes return raw TLV binary data. 25 | func (tlv *TLVBody) Bytes() []byte { 26 | return tlv.data 27 | } 28 | 29 | // SerializeTo serializes TLV data to its binary form. 30 | func (tlv *TLVBody) SerializeTo(w io.Writer) error { 31 | b := make([]byte, 4+len(tlv.data)) 32 | binary.BigEndian.PutUint16(b[0:2], uint16(tlv.Tag)) 33 | binary.BigEndian.PutUint16(b[2:4], tlv.Len) 34 | copy(b[4:], tlv.data) 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdutext/ucs2.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdutext 6 | 7 | import ( 8 | "golang.org/x/text/encoding/unicode" 9 | "golang.org/x/text/transform" 10 | ) 11 | 12 | // UCS2 text codec. 13 | type UCS2 []byte 14 | 15 | // Type implements the Codec interface. 16 | func (s UCS2) Type() DataCoding { 17 | return UCS2Type 18 | } 19 | 20 | // Encode to UCS2. 21 | func (s UCS2) Encode() []byte { 22 | e := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM) 23 | es, _, err := transform.Bytes(e.NewEncoder(), s) 24 | if err != nil { 25 | return s 26 | } 27 | return es 28 | } 29 | 30 | // Decode from UCS2. 31 | func (s UCS2) Decode() []byte { 32 | e := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM) 33 | es, _, err := transform.Bytes(e.NewDecoder(), s) 34 | if err != nil { 35 | return s 36 | } 37 | return es 38 | } 39 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdutext/raw_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdutext 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | ) 11 | 12 | func TestRawEncoder(t *testing.T) { 13 | text := []byte("Olá mundão") 14 | want := text 15 | s := Raw(text) 16 | if s.Type() != 0x00 { 17 | t.Fatalf("Unexpected data type; want 0x00, have %d", s.Type()) 18 | } 19 | have := s.Encode() 20 | if !bytes.Equal(want, have) { 21 | t.Fatalf("Unexpected text; want %q, have %q", want, have) 22 | } 23 | } 24 | 25 | func TestRawDecoder(t *testing.T) { 26 | text := []byte("Olá mundão") 27 | want := text 28 | s := Raw(text) 29 | if s.Type() != 0x00 { 30 | t.Fatalf("Unexpected data type; want 0x00, have %d", s.Type()) 31 | } 32 | have := s.Decode() 33 | if !bytes.Equal(want, have) { 34 | t.Fatalf("Unexpected text; want %q, have %q", want, have) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httplog/request.go: -------------------------------------------------------------------------------- 1 | package httplog 2 | 3 | import ( 4 | "net" 5 | "net/http" 6 | "strings" 7 | ) 8 | 9 | // UseXForwardedFor parses the first value from the X-Forwarded-For 10 | // header and updates the request RemoteAddr field with it, then 11 | // call the next handler and reverts the value back at the end. 12 | func UseXForwardedFor(next http.HandlerFunc) http.HandlerFunc { 13 | return func(w http.ResponseWriter, r *http.Request) { 14 | ip := parseXFF(r.Header.Get("X-Forwarded-For")) 15 | if ip != "" { 16 | _, port, err := net.SplitHostPort(r.RemoteAddr) 17 | if err == nil { 18 | addr := r.RemoteAddr 19 | r.RemoteAddr = net.JoinHostPort(ip, port) 20 | defer func() { r.RemoteAddr = addr }() 21 | } 22 | } 23 | next(w, r) 24 | } 25 | } 26 | 27 | func parseXFF(iplist string) string { 28 | for _, ip := range strings.Split(iplist, ",") { 29 | ip = strings.TrimSpace(ip) 30 | if addr := net.ParseIP(ip); addr != nil { 31 | return ip 32 | } 33 | } 34 | return "" 35 | } 36 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdutext/latin1_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdutext 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | ) 11 | 12 | func TestLatin1Encoder(t *testing.T) { 13 | want := []byte("Ol\xe1 mund\xe3o") 14 | text := []byte("Olá mundão") 15 | s := Latin1(text) 16 | if s.Type() != 0x03 { 17 | t.Fatalf("Unexpected data type; want 0x03, have %d", s.Type()) 18 | } 19 | have := s.Encode() 20 | if !bytes.Equal(want, have) { 21 | t.Fatalf("Unexpected text; want %q, have %q", want, have) 22 | } 23 | } 24 | 25 | func TestLatin1Decoder(t *testing.T) { 26 | want := []byte("Olá mundão") 27 | text := []byte("Ol\xe1 mund\xe3o") 28 | s := Latin1(text) 29 | if s.Type() != 0x03 { 30 | t.Fatalf("Unexpected data type; want 0x03, have %d", s.Type()) 31 | } 32 | have := s.Decode() 33 | if !bytes.Equal(want, have) { 34 | t.Fatalf("Unexpected text; want %q, have %q", want, have) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdufield/body_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdufield 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | ) 11 | 12 | func TestData_SM(t *testing.T) { 13 | want := []byte("hello") 14 | d := New(ShortMessage, want) 15 | f, ok := d.(*SM) 16 | if !ok { 17 | t.Fatalf("unexpected field type: want SM, have %#v", f) 18 | } 19 | if !bytes.Equal(want, f.Bytes()) { 20 | t.Fatalf("unexpected field data: want %q, have %q", want, f.Bytes()) 21 | } 22 | want = []byte("") 23 | d = New(ShortMessage, nil) 24 | f, ok = d.(*SM) 25 | if !ok { 26 | t.Fatalf("unexpected field type: want SM, have %#v", f) 27 | } 28 | if !bytes.Equal(want, f.Bytes()) { 29 | t.Fatalf("unexpected field data: want %q, have %q", want, f.Bytes()) 30 | } 31 | } 32 | 33 | func TestData_Invalid(t *testing.T) { 34 | d := New("foobar", nil) 35 | if d != nil { 36 | t.Fatalf("unexpected field: %#v", d) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdutext/ucs2_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdutext 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | ) 11 | 12 | func TestUCS2Encoder(t *testing.T) { 13 | want := []byte("\x00O\x00l\x00\xe1\x00 \x00m\x00u\x00n\x00d\x00\xe3\x00o") 14 | text := []byte("Olá mundão") 15 | s := UCS2(text) 16 | if s.Type() != 0x08 { 17 | t.Fatalf("Unexpected data type; want 0x08, have %d", s.Type()) 18 | } 19 | have := s.Encode() 20 | if !bytes.Equal(want, have) { 21 | t.Fatalf("Unexpected text; want %q, have %q", want, have) 22 | } 23 | } 24 | 25 | func TestUCS2Decoder(t *testing.T) { 26 | want := []byte("Olá mundão") 27 | text := []byte("\x00O\x00l\x00\xe1\x00 \x00m\x00u\x00n\x00d\x00\xe3\x00o") 28 | s := UCS2(text) 29 | if s.Type() != 0x08 { 30 | t.Fatalf("Unexpected data type; want 0x08, have %d", s.Type()) 31 | } 32 | have := s.Decode() 33 | if !bytes.Equal(want, have) { 34 | t.Fatalf("Unexpected text; want %q, have %q", want, have) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 sms-api-server authors. All rights reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httplog/error.go: -------------------------------------------------------------------------------- 1 | package httplog 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/go-web/httpmux" 8 | 9 | "golang.org/x/net/context" 10 | ) 11 | 12 | // ErrorType is the type used for storing error in a context.Context. 13 | type ErrorType int 14 | 15 | // ErrorID is the key used for storing and retrieving errors to/from context. 16 | var ErrorID ErrorType 17 | 18 | // Error associates message v with the request context. 19 | func Error(r *http.Request, v ...interface{}) { 20 | ctx := httpmux.Context(r) 21 | ctx = context.WithValue(ctx, ErrorID, fmt.Sprint(v...)) 22 | httpmux.SetContext(ctx, r) 23 | } 24 | 25 | // Errorf associates message v with the request context. 26 | func Errorf(r *http.Request, format string, v ...interface{}) { 27 | ctx := httpmux.Context(r) 28 | ctx = context.WithValue(ctx, ErrorID, fmt.Sprintf(format, v...)) 29 | httpmux.SetContext(ctx, r) 30 | } 31 | 32 | // Errorln associates message v with the request context. 33 | func Errorln(r *http.Request, v ...interface{}) { 34 | ctx := httpmux.Context(r) 35 | ctx = context.WithValue(ctx, ErrorID, fmt.Sprintln(v...)) 36 | httpmux.SetContext(ctx, r) 37 | } 38 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 go-smpp authors. All rights reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdutext/codec.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdutext 6 | 7 | // DataCoding to define text codecs. 8 | type DataCoding uint8 9 | 10 | // Supported text codecs. 11 | const ( 12 | Latin1Type DataCoding = 0x03 13 | UCS2Type DataCoding = 0x08 14 | ) 15 | 16 | // Codec defines a text codec. 17 | type Codec interface { 18 | // Type returns the value for the data_coding PDU. 19 | Type() DataCoding 20 | 21 | // Encode text. 22 | Encode() []byte 23 | 24 | // Decode text. 25 | Decode() []byte 26 | } 27 | 28 | // Encode text. 29 | func Encode(typ DataCoding, text []byte) []byte { 30 | switch typ { 31 | case Latin1Type: 32 | return Latin1(text).Encode() 33 | case UCS2Type: 34 | return UCS2(text).Encode() 35 | default: 36 | return text 37 | } 38 | } 39 | 40 | // Decode text. 41 | func Decode(typ DataCoding, text []byte) []byte { 42 | switch typ { 43 | case Latin1Type: 44 | return Latin1(text).Decode() 45 | case UCS2Type: 46 | return UCS2(text).Decode() 47 | default: 48 | return text 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /apiserver/form_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 sms-api-server authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package apiserver 6 | 7 | import ( 8 | "net/url" 9 | "testing" 10 | ) 11 | 12 | func TestForm(t *testing.T) { 13 | var a, b, c, d string 14 | f := form{ 15 | {"a", "param a", true, nil, &a}, 16 | {"b", "param b", false, nil, &b}, 17 | {"c", "param c", true, []string{"1", "2"}, &c}, 18 | {"d", "param d", false, []string{"3", "4"}, &d}, 19 | } 20 | test := []struct { 21 | Desc string 22 | Form url.Values 23 | Good bool 24 | }{ 25 | {"missing a, d", url.Values{"b": {"."}, "c": {"."}}, false}, 26 | {"missing b, d", url.Values{"a": {"."}, "c": {"1"}}, true}, 27 | {"missing c, d", url.Values{"a": {"."}, "b": {"3"}}, false}, 28 | {"missing b", url.Values{"a": {"."}, "c": {"2"}, "d": {"4"}}, true}, 29 | {"invalid c", url.Values{"a": {"."}, "c": {"."}}, false}, 30 | } 31 | for _, el := range test { 32 | err := f.Validate(el.Form) 33 | if el.Good && err != nil { 34 | t.Fatal(err) 35 | } 36 | if !el.Good && err == nil { 37 | t.Fatal("bad form parsed without error:", el.Desc) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httpmux/example_test.go: -------------------------------------------------------------------------------- 1 | package httpmux_test 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | 8 | "golang.org/x/net/context" 9 | 10 | "github.com/go-web/httpmux" 11 | ) 12 | 13 | func authHandler(next http.HandlerFunc) http.HandlerFunc { 14 | return func(w http.ResponseWriter, r *http.Request) { 15 | u, p, ok := r.BasicAuth() 16 | if ok && u == "foobar" && p == "foobared" { 17 | ctx := httpmux.Context(r) 18 | ctx = context.WithValue(ctx, "user", u) 19 | httpmux.SetContext(ctx, r) 20 | next(w, r) 21 | return 22 | } 23 | w.Header().Set("WWW-Authenticate", `realm="restricted"`) 24 | w.WriteHeader(http.StatusUnauthorized) 25 | } 26 | } 27 | 28 | func Example() { 29 | // curl -i localhost:8080 30 | // curl -i -XPOST --basic -u foobar:foobared localhost:8080/auth/login 31 | root := httpmux.New() 32 | root.GET("/", func(w http.ResponseWriter, r *http.Request) { 33 | io.WriteString(w, "hello, world\n") 34 | }) 35 | auth := httpmux.New() 36 | { 37 | auth.UseFunc(authHandler) 38 | auth.POST("/login", func(w http.ResponseWriter, r *http.Request) { 39 | u := httpmux.Context(r).Value("user") 40 | fmt.Fprintln(w, "hello,", u) 41 | }) 42 | } 43 | root.Append("/auth", auth) 44 | http.ListenAndServe(":8080", root) 45 | } 46 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httplog/apache_test.go: -------------------------------------------------------------------------------- 1 | package httplog 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "log" 7 | "net/http" 8 | "net/http/httptest" 9 | "net/url" 10 | "testing" 11 | 12 | "github.com/go-web/httpmux" 13 | ) 14 | 15 | func TestApacheCommonFormat(t *testing.T) { 16 | var b bytes.Buffer 17 | l := log.New(&b, "", 0) 18 | mux := httpmux.New() 19 | mux.UseFunc(ApacheCommonFormat(l)) 20 | mux.GET("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 21 | w.WriteHeader(http.StatusNotFound) 22 | io.WriteString(w, "foobar") 23 | })) 24 | r := &http.Request{ 25 | Method: "GET", 26 | URL: &url.URL{Path: "/"}, 27 | } 28 | w := &httptest.ResponseRecorder{} 29 | mux.ServeHTTP(w, r) 30 | // TODO: Test the format. 31 | } 32 | 33 | func TestApacheCombinedFormat(t *testing.T) { 34 | var b bytes.Buffer 35 | l := log.New(&b, "", 0) 36 | mux := httpmux.New() 37 | mux.UseFunc(ApacheCommonFormat(l)) 38 | mux.GET("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 39 | w.WriteHeader(http.StatusNotFound) 40 | io.WriteString(w, "foobar") 41 | })) 42 | r := &http.Request{ 43 | Method: "GET", 44 | URL: &url.URL{Path: "/"}, 45 | } 46 | w := &httptest.ResponseRecorder{} 47 | mux.ServeHTTP(w, r) 48 | // TODO: Test the format. 49 | } 50 | -------------------------------------------------------------------------------- /vendor/golang.org/x/text/encoding/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package encoding_test 6 | 7 | import ( 8 | "fmt" 9 | "io" 10 | "os" 11 | "strings" 12 | 13 | "golang.org/x/text/encoding" 14 | "golang.org/x/text/encoding/charmap" 15 | "golang.org/x/text/transform" 16 | ) 17 | 18 | func ExampleDecodeWindows1252() { 19 | sr := strings.NewReader("Gar\xe7on !") 20 | tr := transform.NewReader(sr, charmap.Windows1252.NewDecoder()) 21 | io.Copy(os.Stdout, tr) 22 | // Output: Garçon ! 23 | } 24 | 25 | func ExampleUTF8Validator() { 26 | for i := 0; i < 2; i++ { 27 | transformer := charmap.Windows1252.NewEncoder() 28 | if i == 1 { 29 | transformer = transform.Chain(encoding.UTF8Validator, transformer) 30 | } 31 | dst := make([]byte, 256) 32 | src := []byte("abc\xffxyz") // src is invalid UTF-8. 33 | nDst, nSrc, err := transformer.Transform(dst, src, true) 34 | fmt.Printf("i=%d: produced %q, consumed %q, error %v\n", 35 | i, dst[:nDst], src[:nSrc], err) 36 | } 37 | // Output: 38 | // i=0: produced "abc\x1axyz", consumed "abc\xffxyz", error 39 | // i=1: produced "abc", consumed "abc", error encoding: invalid UTF-8 40 | } 41 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/receiver_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package smpp 6 | 7 | import ( 8 | "testing" 9 | "time" 10 | 11 | "github.com/fiorix/go-smpp/smpp/pdu" 12 | "github.com/fiorix/go-smpp/smpp/smpptest" 13 | ) 14 | 15 | func TestReceiver(t *testing.T) { 16 | s := smpptest.NewServer() 17 | defer s.Close() 18 | rc := make(chan pdu.Body) 19 | r := &Receiver{ 20 | Addr: s.Addr(), 21 | User: smpptest.DefaultUser, 22 | Passwd: smpptest.DefaultPasswd, 23 | Handler: func(p pdu.Body) { rc <- p }, 24 | } 25 | defer r.Close() 26 | conn := <-r.Bind() 27 | switch conn.Status() { 28 | case Connected: 29 | default: 30 | t.Fatal(conn.Error()) 31 | } 32 | // cheat: inject GenericNACK PDU for the server to echo back. 33 | p := pdu.NewGenericNACK() 34 | r.conn.Lock() 35 | r.conn.Write(p) 36 | r.conn.Unlock() 37 | // check response. 38 | select { 39 | case m := <-rc: 40 | want, have := *p.Header(), *m.Header() 41 | if want != have { 42 | t.Fatalf("unexpected PDU: want %#v, have %#v", 43 | want, have) 44 | } 45 | case <-time.After(time.Second): 46 | t.Fatal("timeout waiting for server to echo") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httplog/default.go: -------------------------------------------------------------------------------- 1 | package httplog 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "strconv" 8 | "time" 9 | 10 | "github.com/go-web/httpmux" 11 | ) 12 | 13 | // DefaultFormat returns a middleware that logs http requests 14 | // to the given logger using the default log format. 15 | func DefaultFormat(l *log.Logger) httpmux.MiddlewareFunc { 16 | return func(next http.HandlerFunc) http.HandlerFunc { 17 | return func(w http.ResponseWriter, r *http.Request) { 18 | start := time.Now() 19 | rw := NewResponseWriter(w) 20 | next(rw, r) 21 | b := getBuffer() 22 | b.WriteString(r.Proto) 23 | b.Write([]byte(" ")) 24 | b.WriteString(strconv.Itoa(rw.Code())) 25 | b.Write([]byte(" ")) 26 | b.WriteString(r.Method) 27 | b.Write([]byte(" ")) 28 | b.WriteString(r.URL.RequestURI()) 29 | b.Write([]byte(" from ")) 30 | b.WriteString(r.RemoteAddr) 31 | b.Write([]byte(" ")) 32 | fmt.Fprintf(b, "%q", r.Header.Get("User-Agent")) 33 | b.Write([]byte(" ")) 34 | b.WriteString(strconv.Itoa(rw.Bytes())) 35 | b.Write([]byte(" bytes in ")) 36 | b.WriteString(time.Since(start).String()) 37 | if err := httpmux.Context(r).Value(ErrorID); err != nil { 38 | fmt.Fprintf(b, " err: %v", err) 39 | } 40 | l.Print(b.String()) 41 | putBuffer(b) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /apiserver/cors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 sms-api-server authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package apiserver 6 | 7 | import ( 8 | "net/http" 9 | "strings" 10 | ) 11 | 12 | // cors is an HTTP handler for managing cross-origin resource sharing. 13 | // Ref: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing. 14 | func cors(f http.HandlerFunc, methods ...string) http.HandlerFunc { 15 | ms := strings.Join(methods, ", ") + ", OPTIONS" 16 | md := make(map[string]struct{}) 17 | for _, method := range methods { 18 | md[method] = struct{}{} 19 | } 20 | return func(w http.ResponseWriter, r *http.Request) { 21 | origin := "*" 22 | if len(r.Header.Get("Origin")) > 0 { 23 | origin = r.Header.Get("Origin") 24 | } 25 | w.Header().Set("Access-Control-Allow-Origin", origin) 26 | w.Header().Set("Access-Control-Allow-Methods", ms) 27 | w.Header().Set("Access-Control-Allow-Credentials", "true") 28 | if r.Method == "OPTIONS" { 29 | w.WriteHeader(http.StatusOK) 30 | return 31 | } 32 | if _, exists := md[r.Method]; exists { 33 | f.ServeHTTP(w, r) 34 | return 35 | } 36 | w.Header().Set("Allow", ms) 37 | http.Error(w, 38 | http.StatusText(http.StatusMethodNotAllowed), 39 | http.StatusMethodNotAllowed) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/body.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdu 6 | 7 | import ( 8 | "io" 9 | 10 | "github.com/fiorix/go-smpp/smpp/pdu/pdufield" 11 | ) 12 | 13 | // MaxSize is the maximum size allowed for a PDU. 14 | const MaxSize = 4096 15 | 16 | // Body is an abstract Protocol Data Unit (PDU) interface 17 | // for manipulating PDUs. 18 | type Body interface { 19 | // Header returns the PDU header, decoded. Header fields 20 | // can be updated (e.g. Seq) before re-serializing the PDU. 21 | Header() *Header 22 | 23 | // Len returns the length of the PDU binary data, in bytes. 24 | Len() int 25 | 26 | // FieldList returns a list of mandatory PDU fields for 27 | // encoding or decoding the PDU. The order in the list 28 | // dictates how PDUs are decoded and serialized. 29 | FieldList() pdufield.List 30 | 31 | // Fields return a decoded map of PDU fields. The returned 32 | // map can be modified before re-serializing the PDU. 33 | Fields() pdufield.Map 34 | 35 | // TLVFields return a decoded map of PDU TLV fields. 36 | TLVFields() pdufield.TLVMap 37 | 38 | // SerializeTo encodes the PDU to its binary form, including 39 | // the header and all fields. 40 | SerializeTo(w io.Writer) error 41 | } 42 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /vendor/golang.org/x/text/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdutext/codec_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdutext 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | ) 11 | 12 | func TestEncode(t *testing.T) { 13 | test := []struct { 14 | typ DataCoding 15 | text []byte 16 | want []byte 17 | }{ 18 | {Latin1Type, []byte("áéíóú moço"), []byte("\xe1\xe9\xed\xf3\xfa mo\xe7o")}, 19 | {UCS2Type, []byte("áéíóú moço"), []byte("\x00\xe1\x00\xe9\x00\xed\x00\xf3\x00\xfa\x00 \x00m\x00o\x00\xe7\x00o")}, 20 | } 21 | for _, tc := range test { 22 | have := Encode(tc.typ, tc.text) 23 | if !bytes.Equal(tc.want, have) { 24 | t.Fatalf("unexpected text for %#x:\nwant: %q\nhave: %q", 25 | tc.typ, tc.want, have) 26 | } 27 | } 28 | } 29 | 30 | func TestDecode(t *testing.T) { 31 | test := []struct { 32 | typ DataCoding 33 | want []byte 34 | text []byte 35 | }{ 36 | {Latin1Type, []byte("áéíóú moço"), []byte("\xe1\xe9\xed\xf3\xfa mo\xe7o")}, 37 | {UCS2Type, []byte("áéíóú moço"), []byte("\x00\xe1\x00\xe9\x00\xed\x00\xf3\x00\xfa\x00 \x00m\x00o\x00\xe7\x00o")}, 38 | } 39 | for _, tc := range test { 40 | have := Decode(tc.typ, tc.text) 41 | if !bytes.Equal(tc.want, have) { 42 | t.Fatalf("unexpected text for %#x:\nwant: %q\nhave: %q", 43 | tc.typ, tc.want, have) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httplog/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 httplog authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * The names of authors or contributors may NOT be used to endorse or 14 | promote products derived from this software without specific prior 15 | written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httpmux/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 httpmux authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * The names of authors or contributors may NOT be used to endorse or 14 | promote products derived from this software without specific prior 15 | written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/julienschmidt/httprouter/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Julien Schmidt. All rights reserved. 2 | 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * The names of the contributors may not be used to endorse or promote 12 | products derived from this software without specific prior written 13 | permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL JULIEN SCHMIDT BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdufield/body.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdufield 6 | 7 | import "io" 8 | 9 | // Body is an interface for manipulating binary PDU field data. 10 | type Body interface { 11 | Len() int 12 | Raw() interface{} 13 | String() string 14 | Bytes() []byte 15 | SerializeTo(w io.Writer) error 16 | } 17 | 18 | // New parses the given binary data and returns a Data object, 19 | // or nil if the field Name is unknown. 20 | func New(n Name, data []byte) Body { 21 | switch n { 22 | case 23 | AddrNPI, 24 | AddrTON, 25 | DataCoding, 26 | DestAddrNPI, 27 | DestAddrTON, 28 | ESMClass, 29 | ErrorCode, 30 | InterfaceVersion, 31 | MessageState, 32 | PriorityFlag, 33 | ProtocolID, 34 | RegisteredDelivery, 35 | ReplaceIfPresentFlag, 36 | SMDefaultMsgID, 37 | SMLength, 38 | SourceAddrNPI, 39 | SourceAddrTON: 40 | if data == nil { 41 | data = []byte{0} 42 | } 43 | return &Fixed{Data: data[0]} 44 | case 45 | AddressRange, 46 | DestinationAddr, 47 | FinalDate, 48 | MessageID, 49 | Password, 50 | ScheduleDeliveryTime, 51 | ServiceType, 52 | SourceAddr, 53 | SystemID, 54 | SystemType, 55 | ValidityPeriod: 56 | if data == nil { 57 | data = []byte{} 58 | } 59 | return &Variable{Data: data} 60 | case ShortMessage: 61 | if data == nil { 62 | data = []byte{} 63 | } 64 | return &SM{Data: data} 65 | default: 66 | return nil 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/golang.org/x/text/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /apiserver/form.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 sms-api-server authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package apiserver 6 | 7 | import ( 8 | "fmt" 9 | "net/url" 10 | "strings" 11 | ) 12 | 13 | // param defines a parameter from HTTP POST. 14 | type param struct { 15 | Name string 16 | Desc string 17 | Required bool 18 | Options []string // possible options for this param, optional 19 | Value *string // where the value[name] will be stored, optional 20 | } 21 | 22 | // ValidOption checks whether the given value match the defined 23 | // options for the parameter, if options are provided. 24 | func (p param) ValidOption(v string) bool { 25 | if p.Options == nil { 26 | return true 27 | } 28 | for _, opt := range p.Options { 29 | if v == opt { 30 | return true 31 | } 32 | } 33 | return false 34 | } 35 | 36 | // form is a list of parameters. 37 | type form []param 38 | 39 | // Validate validates each parameter in this form, and store the 40 | // parameter's value in the Value field, if provided. 41 | func (f form) Validate(values url.Values) error { 42 | for _, p := range f { 43 | v := values.Get(p.Name) 44 | if v == "" && p.Required { 45 | return fmt.Errorf("missing parameter %q: %s", 46 | p.Name, p.Desc) 47 | } 48 | if v != "" && !p.ValidOption(v) { 49 | return fmt.Errorf("invalid parameter %q=%q; supported: %s", 50 | p.Name, v, strings.Join(p.Options, ", ")) 51 | } 52 | if p.Value != nil { 53 | *p.Value = v 54 | } 55 | } 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /apiserver/httpws_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 sms-api-server authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package apiserver 6 | 7 | import ( 8 | "errors" 9 | "net/http" 10 | "net/http/httptest" 11 | "net/rpc" 12 | "net/rpc/jsonrpc" 13 | "strings" 14 | "testing" 15 | "time" 16 | 17 | "github.com/fiorix/go-smpp/smpp" 18 | "golang.org/x/net/websocket" 19 | ) 20 | 21 | var deliverErr = make(chan error, 1) 22 | 23 | func (sm *SM) Deliver(args *DeliveryReceipt, resp *string) error { 24 | if args.Text != "delivery receipt here" { 25 | deliverErr <- errors.New("unexpected delivery receipt") 26 | } 27 | *resp = "" 28 | return nil 29 | } 30 | 31 | func TestWebSocket_Deliver(t *testing.T) { 32 | mux := http.NewServeMux() 33 | h := Handler{Tx: &smpp.Transceiver{Addr: ":0"}} 34 | <-h.Register(mux) 35 | s := httptest.NewServer(mux) 36 | defer s.Close() 37 | u := strings.Replace(s.URL, "http:", "ws:", 1) 38 | ws, err := websocket.Dial(u+"/v1/ws/jsonrpc/events", "", "http://localhost") 39 | if err != nil { 40 | t.Fatal(err) 41 | } 42 | defer ws.Close() 43 | h.pool.Broadcast(&DeliveryReceipt{ 44 | Src: "bart", 45 | Dst: "lisa", 46 | Text: "delivery receipt here", 47 | }) 48 | srv := rpc.NewServer() 49 | NewSM(h.Tx, srv) 50 | go func() { 51 | deliverErr <- srv.ServeRequest(jsonrpc.NewServerCodec(ws)) 52 | }() 53 | select { 54 | case err = <-deliverErr: 55 | if err != nil { 56 | t.Fatal(err) 57 | } 58 | case <-time.After(2 * time.Second): 59 | t.Fatal("timeout waiting for delivery receipt") 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdufield/map.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdufield 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/fiorix/go-smpp/smpp/pdu/pdutext" 11 | ) 12 | 13 | // Map is a collection of PDU field data indexed by name. 14 | type Map map[Name]Body 15 | 16 | // Set updates the PDU map with the given key and value, and 17 | // returns error if the value cannot be converted to type Data. 18 | // 19 | // This is a shortcut for m[k] = New(k, v) converting v properly. 20 | // 21 | // If k is ShortMessage and v is of type pdutext.Codec, text is 22 | // encoded and data_coding PDU and sm_length PDUs are set. 23 | func (m Map) Set(k Name, v interface{}) error { 24 | switch v.(type) { 25 | case nil: 26 | m[k] = New(k, nil) // use default value 27 | case uint8: 28 | m[k] = New(k, []byte{v.(uint8)}) 29 | case int: 30 | m[k] = New(k, []byte{uint8(v.(int))}) 31 | case string: 32 | m[k] = New(k, []byte(v.(string))) 33 | case []byte: 34 | m[k] = New(k, []byte(v.([]byte))) 35 | case Body: 36 | m[k] = v.(Body) 37 | case pdutext.Codec: 38 | c := v.(pdutext.Codec) 39 | m[k] = New(k, c.Encode()) 40 | if k == ShortMessage { 41 | m[DataCoding] = &Fixed{Data: uint8(c.Type())} 42 | } 43 | default: 44 | return fmt.Errorf("unsupported field data: %#v", v) 45 | } 46 | if k == ShortMessage { 47 | m[SMLength] = &Fixed{Data: uint8(m[k].Len())} 48 | } 49 | return nil 50 | } 51 | 52 | // TLVMap is a collection of PDU TLV field data indexed by type. 53 | type TLVMap map[TLVType]*TLVBody 54 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/smpptest/conn.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package smpptest 6 | 7 | import ( 8 | "bufio" 9 | "bytes" 10 | "io" 11 | "net" 12 | 13 | "github.com/fiorix/go-smpp/smpp/pdu" 14 | ) 15 | 16 | // Conn implements a server side connection. 17 | type Conn interface { 18 | // Write serializes the given PDU and writes to the connection. 19 | Write(p pdu.Body) error 20 | 21 | // Close terminates the connection. 22 | Close() error 23 | 24 | // RemoteAddr returns the peer address. 25 | RemoteAddr() net.Addr 26 | } 27 | 28 | // conn provides the basics of an SMPP connection. 29 | type conn struct { 30 | rwc net.Conn 31 | r *bufio.Reader 32 | w *bufio.Writer 33 | } 34 | 35 | func newConn(c net.Conn) *conn { 36 | return &conn{ 37 | rwc: c, 38 | r: bufio.NewReader(c), 39 | w: bufio.NewWriter(c), 40 | } 41 | } 42 | 43 | // RemoteAddr implements the Conn interface. 44 | func (c *conn) RemoteAddr() net.Addr { 45 | return c.rwc.RemoteAddr() 46 | } 47 | 48 | // Read reads PDU off the wire. 49 | func (c *conn) Read() (pdu.Body, error) { 50 | return pdu.Decode(c.r) 51 | } 52 | 53 | // Write implements the Conn interface. 54 | func (c *conn) Write(p pdu.Body) error { 55 | var b bytes.Buffer 56 | err := p.SerializeTo(&b) 57 | if err != nil { 58 | return err 59 | } 60 | _, err = io.Copy(c.w, &b) 61 | if err != nil { 62 | return err 63 | } 64 | return c.w.Flush() 65 | } 66 | 67 | // Close implements the Conn interface. 68 | func (c *conn) Close() error { 69 | return c.rwc.Close() 70 | } 71 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httplog/error_test.go: -------------------------------------------------------------------------------- 1 | package httplog 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "net/url" 7 | "testing" 8 | 9 | "golang.org/x/net/context" 10 | 11 | "github.com/go-web/httpmux" 12 | ) 13 | 14 | func TestError(t *testing.T) { 15 | var ctx context.Context 16 | mux := httpmux.New() 17 | mux.GET("/", func(w http.ResponseWriter, r *http.Request) { 18 | Error(r, "hello", "world") 19 | ctx = httpmux.Context(r) 20 | }) 21 | r := &http.Request{Method: "GET", URL: &url.URL{Path: "/"}} 22 | mux.ServeHTTP(&httptest.ResponseRecorder{}, r) 23 | if ctx.Value(ErrorID) != "helloworld" { 24 | t.Fatalf("Unexpected value. Want \"helloworld\", have %v", ctx.Value("error")) 25 | } 26 | } 27 | 28 | func TestErrorf(t *testing.T) { 29 | var ctx context.Context 30 | mux := httpmux.New() 31 | mux.GET("/", func(w http.ResponseWriter, r *http.Request) { 32 | Errorf(r, "hello, world") 33 | ctx = httpmux.Context(r) 34 | }) 35 | r := &http.Request{Method: "GET", URL: &url.URL{Path: "/"}} 36 | mux.ServeHTTP(&httptest.ResponseRecorder{}, r) 37 | if ctx.Value(ErrorID) != "hello, world" { 38 | t.Fatalf("Unexpected value. Want \"hello, world\", have %v", ctx.Value("error")) 39 | } 40 | } 41 | 42 | func TestErrorln(t *testing.T) { 43 | var ctx context.Context 44 | mux := httpmux.New() 45 | mux.GET("/", func(w http.ResponseWriter, r *http.Request) { 46 | Errorln(r, "hello", "world") 47 | ctx = httpmux.Context(r) 48 | }) 49 | r := &http.Request{Method: "GET", URL: &url.URL{Path: "/"}} 50 | mux.ServeHTTP(&httptest.ResponseRecorder{}, r) 51 | if ctx.Value(ErrorID) != "hello world\n" { 52 | t.Fatalf("Unexpected value. Want \"hello world\\n\", have %v", ctx.Value("error")) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /vendor/golang.org/x/text/encoding/internal/internal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package internal contains code that is shared among encoding implementations. 6 | package internal 7 | 8 | import ( 9 | "golang.org/x/text/encoding" 10 | "golang.org/x/text/encoding/internal/identifier" 11 | "golang.org/x/text/transform" 12 | ) 13 | 14 | // Encoding is an implementation of the Encoding interface that adds the String 15 | // and ID methods to an existing encoding. 16 | type Encoding struct { 17 | encoding.Encoding 18 | Name string 19 | MIB identifier.MIB 20 | } 21 | 22 | // _ verifies that Encoding implements identifier.Interface. 23 | var _ identifier.Interface = (*Encoding)(nil) 24 | 25 | func (e *Encoding) String() string { 26 | return e.Name 27 | } 28 | 29 | func (e *Encoding) ID() (mib identifier.MIB, other string) { 30 | return e.MIB, "" 31 | } 32 | 33 | // SimpleEncoding is an Encoding that combines two Transformers. 34 | type SimpleEncoding struct { 35 | Decoder transform.Transformer 36 | Encoder transform.Transformer 37 | } 38 | 39 | func (e *SimpleEncoding) NewDecoder() transform.Transformer { 40 | return e.Decoder 41 | } 42 | 43 | func (e *SimpleEncoding) NewEncoder() transform.Transformer { 44 | return e.Encoder 45 | } 46 | 47 | // FuncEncoding is an Encoding that combines two functions returning a new 48 | // Transformer. 49 | type FuncEncoding struct { 50 | Decoder func() transform.Transformer 51 | Encoder func() transform.Transformer 52 | } 53 | 54 | func (e FuncEncoding) NewDecoder() transform.Transformer { 55 | return e.Decoder() 56 | } 57 | 58 | func (e FuncEncoding) NewEncoder() transform.Transformer { 59 | return e.Encoder() 60 | } 61 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdufield/map_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdufield 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | 11 | "github.com/fiorix/go-smpp/smpp/pdu/pdutext" 12 | ) 13 | 14 | func TestMapSet(t *testing.T) { 15 | m := make(Map) 16 | test := []struct { 17 | k Name 18 | v interface{} 19 | ok bool 20 | }{ 21 | {SystemID, nil, true}, 22 | {SystemID, "hello", true}, 23 | {SystemID, []byte("hello"), true}, 24 | {DataCoding, nil, true}, 25 | {DataCoding, uint8(1), true}, 26 | {DataCoding, int(1), true}, 27 | {DataCoding, t, false}, 28 | {DataCoding, New(DataCoding, []byte{0x03}), true}, 29 | } 30 | for _, el := range test { 31 | if err := m.Set(el.k, el.v); el.ok && err != nil { 32 | t.Fatal(err) 33 | } else if !el.ok && err == nil { 34 | t.Fatalf("unexpected set of %q=%#v", el.k, el.v) 35 | } 36 | } 37 | } 38 | 39 | func TestMapSetTextCodec(t *testing.T) { 40 | m := make(Map) 41 | text := pdutext.Latin1("Olá mundo") 42 | err := m.Set(ShortMessage, text) 43 | if err != nil { 44 | t.Fatal(err) 45 | } 46 | dc, exists := m[DataCoding] 47 | if !exists { 48 | t.Fatal("missing data_coding pdu") 49 | } 50 | dv, ok := dc.(*Fixed) 51 | if !ok { 52 | t.Fatalf("unexpected type for data_coding: %#v", dc) 53 | } 54 | if dv.Data != uint8(text.Type()) { 55 | t.Fatalf("unexpected value for data_coding: want %d, have %d", 56 | text.Type(), dv.Data) 57 | } 58 | pt, exists := m[ShortMessage] 59 | if !exists { 60 | t.Fatal("missing short_message pdu") 61 | } 62 | nt := pdutext.Latin1(pt.Bytes()).Decode() 63 | if !bytes.Equal(text, nt) { 64 | t.Fatalf("unexpected text: want %q, have %q", text, nt) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/transceiver.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package smpp 6 | 7 | import ( 8 | "crypto/tls" 9 | "fmt" 10 | "time" 11 | 12 | "github.com/fiorix/go-smpp/smpp/pdu" 13 | "github.com/fiorix/go-smpp/smpp/pdu/pdufield" 14 | ) 15 | 16 | // Transceiver implements an SMPP transceiver. 17 | // 18 | // The API is a combination of the Transmitter and Receiver. 19 | type Transceiver struct { 20 | Addr string 21 | User string 22 | Passwd string 23 | SystemType string 24 | EnquireLink time.Duration 25 | RespTimeout time.Duration 26 | TLS *tls.Config 27 | Handler HandlerFunc 28 | 29 | Transmitter 30 | } 31 | 32 | // Bind implements the ClientConn interface. 33 | func (t *Transceiver) Bind() <-chan ConnStatus { 34 | t.conn.Lock() 35 | defer t.conn.Unlock() 36 | if t.conn.client != nil { 37 | return t.conn.Status 38 | } 39 | t.tx.Lock() 40 | t.tx.inflight = make(map[uint32]chan *tx) 41 | t.tx.Unlock() 42 | c := &client{ 43 | Addr: t.Addr, 44 | TLS: t.TLS, 45 | EnquireLink: t.EnquireLink, 46 | RespTimeout: t.RespTimeout, 47 | Status: make(chan ConnStatus, 1), 48 | BindFunc: t.bindFunc, 49 | } 50 | t.conn.client = c 51 | c.init() 52 | go c.Bind() 53 | return c.Status 54 | } 55 | 56 | func (t *Transceiver) bindFunc(c Conn) error { 57 | p := pdu.NewBindTransceiver() 58 | f := p.Fields() 59 | f.Set(pdufield.SystemID, t.User) 60 | f.Set(pdufield.Password, t.Passwd) 61 | f.Set(pdufield.SystemType, t.SystemType) 62 | resp, err := bind(c, p) 63 | if err != nil { 64 | return err 65 | } 66 | if resp.Header().ID != pdu.BindTransceiverRespID { 67 | return fmt.Errorf("unexpected response for BindTransceiver: %s", 68 | resp.Header().ID) 69 | } 70 | go t.handlePDU(t.Handler) 71 | return nil 72 | } 73 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdufield/list_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdufield 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | ) 11 | 12 | func TestListDecoder_Fixed(t *testing.T) { 13 | l := List{DataCoding} 14 | want := []byte{0x02} 15 | b := bytes.NewBuffer(want) 16 | m, err := l.Decode(b) 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | f, ok := m[DataCoding] 21 | if !ok { 22 | t.Fatalf("missing %q key: %#v", DataCoding, m) 23 | } 24 | v, ok := f.(*Fixed) 25 | if !ok { 26 | t.Fatalf("field is not type Fixed: %#v", f) 27 | } 28 | if !bytes.Equal(want, v.Bytes()) { 29 | t.Fatalf("unexpected data: want %q, have %q", want, v) 30 | } 31 | } 32 | 33 | func TestListDecoder_Variable(t *testing.T) { 34 | l := List{SystemID} 35 | want := []byte{'h', 'e', 'l', 'l', 'o', 0x00} 36 | b := bytes.NewBuffer(want) 37 | m, err := l.Decode(b) 38 | if err != nil { 39 | t.Fatal(err) 40 | } 41 | f, ok := m[SystemID] 42 | if !ok { 43 | t.Fatalf("missing %q key: %#v", SystemID, m) 44 | } 45 | v, ok := f.(*Variable) 46 | if !ok { 47 | t.Fatalf("field is not type Variable: %#v", f) 48 | } 49 | if !bytes.Equal(want, v.Bytes()) { 50 | t.Fatalf("unexpected data: want %q, have %q", want, v) 51 | } 52 | } 53 | 54 | func TestListDecoder_SM(t *testing.T) { 55 | l := List{SMLength, ShortMessage} 56 | want := []byte{0x05, 'h', 'e', 'l', 'l', 'o', 0x0A, 0x0B} 57 | b := bytes.NewBuffer(want) 58 | m, err := l.Decode(b) 59 | if err != nil { 60 | t.Fatal(err) 61 | } 62 | f, ok := m[ShortMessage] 63 | if !ok { 64 | t.Fatalf("missing %q key: %#v", ShortMessage, m) 65 | } 66 | v, ok := f.(*SM) 67 | if !ok { 68 | t.Fatalf("field is not type SM: %#v", f) 69 | } 70 | want = []byte("hello") 71 | if !bytes.Equal(want, v.Bytes()) { 72 | t.Fatalf("unexpected data: want %q, have %q", want, v) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/smpptest/server_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package smpptest 6 | 7 | import ( 8 | "bytes" 9 | "net" 10 | "testing" 11 | 12 | "github.com/fiorix/go-smpp/smpp/pdu" 13 | "github.com/fiorix/go-smpp/smpp/pdu/pdufield" 14 | "github.com/fiorix/go-smpp/smpp/pdu/pdutext" 15 | ) 16 | 17 | func TestServer(t *testing.T) { 18 | s := NewServer() 19 | defer s.Close() 20 | c, err := net.Dial("tcp", s.Addr()) 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | defer c.Close() 25 | rw := newConn(c) 26 | // bind 27 | p := pdu.NewBindTransmitter() 28 | f := p.Fields() 29 | f.Set(pdufield.SystemID, "client") 30 | f.Set(pdufield.Password, "secret") 31 | f.Set(pdufield.InterfaceVersion, 0x34) 32 | if err = rw.Write(p); err != nil { 33 | t.Fatal(err) 34 | } 35 | // bind resp 36 | resp, err := rw.Read() 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | id, ok := resp.Fields()[pdufield.SystemID] 41 | if !ok { 42 | t.Fatalf("missing system_id field: %#v", resp) 43 | } 44 | if id.String() != "smpptest" { 45 | t.Fatalf("unexpected system_id: want smpptest, have %q", id) 46 | } 47 | // submit_sm 48 | p = pdu.NewSubmitSM() 49 | f = p.Fields() 50 | f.Set(pdufield.SourceAddr, "foobar") 51 | f.Set(pdufield.DestinationAddr, "bozo") 52 | f.Set(pdufield.ShortMessage, pdutext.Latin1("Lorem ipsum")) 53 | if err = rw.Write(p); err != nil { 54 | t.Fatal(err) 55 | } 56 | // same submit_sm 57 | r, err := rw.Read() 58 | if err != nil { 59 | t.Fatal(err) 60 | } 61 | want, have := *p.Header(), *r.Header() 62 | if want != have { 63 | t.Fatalf("unexpected header: want %#v, have %#v", want, have) 64 | } 65 | for k, v := range p.Fields() { 66 | vv, exists := r.Fields()[k] 67 | if !exists { 68 | t.Fatalf("unexpected fields: want %#v, have %#v", 69 | p.Fields(), r.Fields()) 70 | } 71 | if !bytes.Equal(v.Bytes(), vv.Bytes()) { 72 | t.Fatalf("unexpected field data: want %#v, have %#v", 73 | v, vv) 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /apiserver/main_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 sms-api-server authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package apiserver 6 | 7 | import ( 8 | "os" 9 | "testing" 10 | 11 | "github.com/fiorix/go-smpp/smpp" 12 | "github.com/fiorix/go-smpp/smpp/pdu" 13 | "github.com/fiorix/go-smpp/smpp/pdu/pdufield" 14 | "github.com/fiorix/go-smpp/smpp/smpptest" 15 | ) 16 | 17 | var ts *smpptest.Server 18 | 19 | func TestMain(m *testing.M) { 20 | ts = smpptest.NewUnstartedServer() 21 | ts.Handler = pduHandler 22 | ts.Start() 23 | defer ts.Close() 24 | os.Exit(m.Run()) 25 | } 26 | 27 | func pduHandler(c smpptest.Conn, p pdu.Body) { 28 | src := p.Fields()[pdufield.SourceAddr] 29 | fail := src != nil && src.String() == "root" 30 | switch p.Header().ID { 31 | case pdu.SubmitSMID: 32 | r := pdu.NewSubmitSMResp() 33 | r.Header().Seq = p.Header().Seq 34 | if fail { 35 | r.Header().Status = 0x00000045 // submitsm failed 36 | } else { 37 | r.Fields().Set(pdufield.MessageID, "foobar") 38 | } 39 | c.Write(r) 40 | rd := p.Fields()[pdufield.RegisteredDelivery] 41 | if rd == nil || rd.Bytes()[0] == 0 { 42 | return 43 | } 44 | r = pdu.NewDeliverSM() 45 | rf := r.Fields() 46 | pf := p.Fields() 47 | rf.Set(pdufield.SourceAddr, pf[pdufield.SourceAddr]) 48 | rf.Set(pdufield.DestinationAddr, pf[pdufield.DestinationAddr]) 49 | rf.Set(pdufield.ShortMessage, "delivery receipt here") 50 | c.Write(r) 51 | case pdu.QuerySMID: 52 | r := pdu.NewQuerySMResp() 53 | r.Header().Seq = p.Header().Seq 54 | if fail { 55 | r.Header().Status = 0x00000067 // querysm failed 56 | } else { 57 | pf := p.Fields() 58 | rf := r.Fields() 59 | rf.Set(pdufield.MessageID, pf[pdufield.MessageID]) 60 | rf.Set(pdufield.MessageState, 2) // DELIVERED 61 | } 62 | c.Write(r) 63 | default: 64 | smpptest.EchoHandler(c, p) 65 | } 66 | } 67 | 68 | func newTransceiver() *smpp.Transceiver { 69 | return &smpp.Transceiver{ 70 | Addr: ts.Addr(), 71 | User: smpptest.DefaultUser, 72 | Passwd: smpptest.DefaultPasswd, 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/header_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdu 6 | 7 | import ( 8 | "bytes" 9 | "encoding/hex" 10 | "testing" 11 | ) 12 | 13 | func TestHeader(t *testing.T) { 14 | want := []byte{ 15 | 0x00, 0x00, 0x00, 0x10, // 16 Len 16 | 0x80, 0x00, 0x00, 0x00, // GenericNACK ID 17 | 0x00, 0x00, 0x00, 0x01, // Invalid message length Status 18 | 0x00, 0x00, 0x00, 0x0D, // 13 Seq 19 | } 20 | h, err := DecodeHeader(bytes.NewBuffer(want)) 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | if h.Len != 16 { 25 | t.Fatalf("unexpected Len: want 16, have %d", h.Len) 26 | } 27 | if h.ID != GenericNACKID { 28 | t.Fatalf("unexpected ID: want GenericNACK, have %d", h.ID) 29 | } 30 | if h.Status != 1 { 31 | t.Fatalf("unexpected Status: want 1, have %d", h.Status) 32 | } 33 | if h.Seq != 13 { 34 | t.Fatalf("unexpected Seq: want 13, have %d", h.Seq) 35 | } 36 | var b bytes.Buffer 37 | if err := h.SerializeTo(&b); err != nil { 38 | t.Fatal(err) 39 | } 40 | if !bytes.Equal(want, b.Bytes()) { 41 | t.Fatalf("malformed header:\nwant:%s\nhave:\n%s", 42 | hex.Dump(want), hex.Dump(b.Bytes())) 43 | } 44 | we := "invalid message length" 45 | if have := h.Status.Error(); have != we { 46 | t.Fatalf("unexpected status: want %q, have %q", we, have) 47 | } 48 | h.Status = 0x2000 49 | we = "unknown status: 8192" 50 | if have := h.Status.Error(); have != we { 51 | t.Fatalf("unexpected status: want %q, have %q", we, have) 52 | } 53 | } 54 | 55 | func TestDecodeHeader(t *testing.T) { 56 | h, err := DecodeHeader(bytes.NewBuffer(nil)) 57 | if err == nil { 58 | t.Fatalf("unexpected parsing of no data: %#v", h) 59 | } 60 | bin := []byte{ 61 | 0x00, 0x00, 0x00, 0x01, // 1 Len 62 | 0x00, 0x00, 0x00, 0x00, 63 | 0x00, 0x00, 0x00, 0x00, 64 | 0x00, 0x00, 0x00, 0x00, 65 | } 66 | h, err = DecodeHeader(bytes.NewBuffer(bin)) 67 | if err == nil { 68 | t.Fatalf("unexpected parsing of short Len: %#v", h) 69 | } 70 | bin[2] = 0x20 71 | h, err = DecodeHeader(bytes.NewBuffer(bin)) 72 | if err == nil { 73 | t.Fatalf("unexpected parsing of big Len: %#v", h) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httplog/apache.go: -------------------------------------------------------------------------------- 1 | package httplog 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "log" 7 | "net" 8 | "net/http" 9 | "strconv" 10 | "time" 11 | 12 | "github.com/go-web/httpmux" 13 | ) 14 | 15 | func apacheCommonLog(w *ResponseWriter, r *http.Request, start time.Time) *bytes.Buffer { 16 | var username string 17 | if r.URL.User != nil { 18 | username = r.URL.User.Username() 19 | } 20 | if username == "" { 21 | username = "-" 22 | } 23 | host, _, err := net.SplitHostPort(r.RemoteAddr) 24 | if err != nil { 25 | host = r.RemoteAddr 26 | } 27 | b := getBuffer() 28 | b.WriteString(host) 29 | b.Write([]byte(" - ")) 30 | b.WriteString(username) 31 | b.Write([]byte(" [")) 32 | b.WriteString(start.Format("02/Jan/2006:15:04:05 -0700")) 33 | b.Write([]byte("] \"")) 34 | b.WriteString(r.Method) 35 | b.Write([]byte(" ")) 36 | b.WriteString(r.URL.RequestURI()) 37 | b.Write([]byte(" ")) 38 | b.WriteString(r.Proto) 39 | b.Write([]byte("\" ")) 40 | b.WriteString(strconv.Itoa(w.Code())) 41 | b.Write([]byte(" ")) 42 | b.WriteString(strconv.Itoa(w.Bytes())) 43 | return b 44 | } 45 | 46 | // ApacheCommonFormat returns a middleware that logs http requests 47 | // to the given logger using the Apache Common log format. 48 | func ApacheCommonFormat(l *log.Logger) httpmux.MiddlewareFunc { 49 | return func(next http.HandlerFunc) http.HandlerFunc { 50 | return func(w http.ResponseWriter, r *http.Request) { 51 | start := time.Now() 52 | rw := NewResponseWriter(w) 53 | next(rw, r) 54 | b := apacheCommonLog(rw, r, start) 55 | l.Print(b.String()) 56 | putBuffer(b) 57 | } 58 | } 59 | } 60 | 61 | // ApacheCombinedFormat returns a middleware that logs http requests 62 | // to the given logger using the Apache Combined log format. 63 | func ApacheCombinedFormat(l *log.Logger) httpmux.MiddlewareFunc { 64 | return func(next http.HandlerFunc) http.HandlerFunc { 65 | return func(w http.ResponseWriter, r *http.Request) { 66 | start := time.Now() 67 | rw := NewResponseWriter(w) 68 | next(rw, r) 69 | b := apacheCommonLog(rw, r, start) 70 | b.Write([]byte(" ")) 71 | fmt.Fprintf(b, "%q %q", 72 | r.Header.Get("Referer"), 73 | r.Header.Get("User-Agent"), 74 | ) 75 | l.Print(b.String()) 76 | putBuffer(b) 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HTTP API for sending SMS via SMPP 2 | 3 | The sms-api-server is a web server that connects to an SMSC via 4 | SMPP v3.4. It provides HTTP and WebSocket APIs for sending short 5 | messages and querying their status (when supported by the SMSC). 6 | It supports sending delivery receipts via WebSockets or 7 | [Server-Sent Events](http://www.w3schools.com/html/html5_serversentevents.asp). 8 | 9 | [![Build Status](https://secure.travis-ci.org/fiorix/sms-api-server.png)](https://travis-ci.org/fiorix/sms-api-server) 10 | 11 | ## Running 12 | 13 | Developers can build the source code and run. For everyone 14 | else, the easiest way to run is via Docker: 15 | 16 | docker run --rm -i -t fiorix/sms-api-server [--help] 17 | 18 | See [this link](https://hub.docker.com/r/fiorix/sms-api-server/) 19 | on the Docker hub for details. 20 | 21 | ## Usage 22 | 23 | With the server running, send a message: 24 | 25 | curl localhost:8080/v1/send -X POST -F src=bart -F dst=lisa -F text=hi 26 | 27 | In case of success, the server returns a JSON document containing a 28 | message ID that can be used for querying its delivery status later. 29 | This functionality is not always available on some SMSCs. 30 | 31 | Example: 32 | 33 | curl "localhost:8080/v1/query?src=bart&message_id=1234" 34 | 35 | To watch for incoming SMS, or delivery receipts: 36 | 37 | curl localhost:8080/v1/sse 38 | 39 | This is the Server-Sent Events (SSE) endpoint that deliver messages 40 | as events, as they arrive on the server. 41 | 42 | ## Send parameters 43 | 44 | The `/v1/send` endpoint supports the following parameters: 45 | 46 | - src: number of sender (optional) 47 | - dst: number of recipient 48 | - text: text message, encoded as UTF-8 49 | - enc: text encoding for short message: `latin1` or `ucs2` (optional) 50 | - register: register for delivery: `final` or `failure` (optional) 51 | 52 | If an encoding is not provided, data is sent as a binary blob and may 53 | not display correctly on some devices. 54 | 55 | For special characters, try: 56 | 57 | curl localhost:8080/v1/send -X POST -F dst=foobar -F enc=ucs2 -F text="é nóis" 58 | 59 | ## WebSocket API 60 | 61 | This server provides two websocket APIs: 62 | 63 | - One for sending messages and querying for message status 64 | - One for sending delivery receipts 65 | 66 | See [index.html](./index.html) for details. 67 | 68 | Have fun! 69 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/fiorix/sms-api-server", 3 | "GoVersion": "go1.6", 4 | "GodepVersion": "v62", 5 | "Packages": [ 6 | ".", 7 | "./apiserver/" 8 | ], 9 | "Deps": [ 10 | { 11 | "ImportPath": "github.com/fiorix/go-smpp/smpp", 12 | "Rev": "3f7937eafd71183615a8e7c7af8a76da7bfb91b1" 13 | }, 14 | { 15 | "ImportPath": "github.com/fiorix/go-smpp/smpp/pdu", 16 | "Rev": "3f7937eafd71183615a8e7c7af8a76da7bfb91b1" 17 | }, 18 | { 19 | "ImportPath": "github.com/fiorix/go-smpp/smpp/pdu/pdufield", 20 | "Rev": "3f7937eafd71183615a8e7c7af8a76da7bfb91b1" 21 | }, 22 | { 23 | "ImportPath": "github.com/fiorix/go-smpp/smpp/pdu/pdutext", 24 | "Rev": "3f7937eafd71183615a8e7c7af8a76da7bfb91b1" 25 | }, 26 | { 27 | "ImportPath": "github.com/fiorix/go-smpp/smpp/smpptest", 28 | "Rev": "3f7937eafd71183615a8e7c7af8a76da7bfb91b1" 29 | }, 30 | { 31 | "ImportPath": "github.com/go-web/httplog", 32 | "Rev": "580d0d49f0d3990a37bf9f11f7b14d4021c5d8fc" 33 | }, 34 | { 35 | "ImportPath": "github.com/go-web/httpmux", 36 | "Rev": "b3bb6ae19f3987423d95a537c28252e9910bcad8" 37 | }, 38 | { 39 | "ImportPath": "github.com/julienschmidt/httprouter", 40 | "Comment": "v1.1-1-g70708e4", 41 | "Rev": "70708e46004c7bcb09b70e685a8b74a690135387" 42 | }, 43 | { 44 | "ImportPath": "golang.org/x/net/context", 45 | "Rev": "e45385e9b226f570b1f086bf287b25d3d4117776" 46 | }, 47 | { 48 | "ImportPath": "golang.org/x/net/websocket", 49 | "Rev": "e45385e9b226f570b1f086bf287b25d3d4117776" 50 | }, 51 | { 52 | "ImportPath": "golang.org/x/text/encoding", 53 | "Rev": "0fe7e6856182a6ebfcf1e6a7aa90bead9a8e1bc0" 54 | }, 55 | { 56 | "ImportPath": "golang.org/x/text/encoding/charmap", 57 | "Rev": "0fe7e6856182a6ebfcf1e6a7aa90bead9a8e1bc0" 58 | }, 59 | { 60 | "ImportPath": "golang.org/x/text/encoding/internal", 61 | "Rev": "0fe7e6856182a6ebfcf1e6a7aa90bead9a8e1bc0" 62 | }, 63 | { 64 | "ImportPath": "golang.org/x/text/encoding/internal/identifier", 65 | "Rev": "0fe7e6856182a6ebfcf1e6a7aa90bead9a8e1bc0" 66 | }, 67 | { 68 | "ImportPath": "golang.org/x/text/encoding/unicode", 69 | "Rev": "0fe7e6856182a6ebfcf1e6a7aa90bead9a8e1bc0" 70 | }, 71 | { 72 | "ImportPath": "golang.org/x/text/transform", 73 | "Rev": "0fe7e6856182a6ebfcf1e6a7aa90bead9a8e1bc0" 74 | } 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/types_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdu 6 | 7 | import ( 8 | "bytes" 9 | "encoding/hex" 10 | "strconv" 11 | "testing" 12 | 13 | "github.com/fiorix/go-smpp/smpp/pdu/pdufield" 14 | ) 15 | 16 | func TestBind(t *testing.T) { 17 | tx := []byte{ 18 | 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x02, 19 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 20 | 0x73, 0x6D, 0x70, 0x70, 0x63, 0x6C, 0x69, 0x65, 21 | 0x6E, 0x74, 0x31, 0x00, 0x70, 0x61, 0x73, 0x73, 22 | 0x77, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x34, 0x00, 23 | 0x00, 0x00, 24 | } 25 | pdu := NewBindTransmitter() 26 | f := pdu.Fields() 27 | f.Set(pdufield.SystemID, "smppclient1") 28 | f.Set(pdufield.Password, "password") 29 | f.Set(pdufield.InterfaceVersion, 0x34) 30 | pdu.Header().Seq = 1 31 | var b bytes.Buffer 32 | if err := pdu.SerializeTo(&b); err != nil { 33 | t.Fatal(err) 34 | } 35 | l := uint32(b.Len()) 36 | if l != pdu.Header().Len { 37 | t.Fatalf("unexpected len: want %d, have %d", l, pdu.Header().Len) 38 | } 39 | if !bytes.Equal(tx, b.Bytes()) { 40 | t.Fatalf("unexpected bytes:\nwant:\n%s\nhave:\n%s", 41 | hex.Dump(tx), hex.Dump(b.Bytes())) 42 | } 43 | pdu, err := Decode(&b) 44 | if err != nil { 45 | t.Fatal(err) 46 | } 47 | h := pdu.Header() 48 | if h.ID != BindTransmitterID { 49 | t.Fatalf("unexpected ID: want %d, have %d", 50 | BindTransmitterID, h.ID) 51 | } 52 | if h.Seq != 1 { 53 | t.Fatalf("unexpected Seq: want 1, have %d", h.Seq) 54 | } 55 | test := []struct { 56 | n pdufield.Name 57 | v string 58 | }{ 59 | {pdufield.SystemID, "smppclient1"}, 60 | {pdufield.Password, "password"}, 61 | {pdufield.InterfaceVersion, strconv.Itoa(0x34)}, 62 | } 63 | for _, el := range test { 64 | f := pdu.Fields()[el.n] 65 | if f == nil { 66 | t.Fatalf("missing field: %s", el.n) 67 | } 68 | if f.String() != el.v { 69 | t.Fatalf("unexpected value for %q: want %q, have %q", 70 | el.n, el.v, f.String()) 71 | } 72 | } 73 | } 74 | 75 | /* 76 | func TestBindResp(t *testing.T) { 77 | tx := []byte{ 78 | 0x00, 0x00, 0x00, 0x18, 0x80, 0x00, 0x00, 0x02, 79 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 80 | 0x53, 0x4D, 0x50, 0x50, 0x53, 0x69, 0x6D, 0x00, 81 | } 82 | t.Log(tx) 83 | } 84 | */ 85 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httplog/recorder.go: -------------------------------------------------------------------------------- 1 | package httplog 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "net" 7 | "net/http" 8 | ) 9 | 10 | // The ResponseRecorder interface is implemented by ResponseWriters that 11 | // can record the response status code and bytes written to the client. 12 | type ResponseRecorder interface { 13 | Code() int // Response status code 14 | Bytes() int // Bytes written to the client 15 | } 16 | 17 | // ResponseWriter is an http.ResponseWriter + ResponseRecorder. 18 | type ResponseWriter struct { 19 | http.ResponseWriter 20 | http.Hijacker 21 | http.Flusher 22 | http.CloseNotifier 23 | ResponseRecorder 24 | bytes, code int 25 | } 26 | 27 | // NewResponseWriter creates and initializes a new ResponseWriter. 28 | func NewResponseWriter(w http.ResponseWriter) *ResponseWriter { 29 | return &ResponseWriter{ 30 | ResponseWriter: w, 31 | code: http.StatusOK, 32 | } 33 | } 34 | 35 | // Header implements the http.ResponseWriter interface. 36 | func (w *ResponseWriter) Header() http.Header { 37 | return w.ResponseWriter.Header() 38 | } 39 | 40 | // Write implements the http.ResponseWriter interface. 41 | func (w *ResponseWriter) Write(b []byte) (int, error) { 42 | n, err := w.ResponseWriter.Write(b) 43 | w.bytes += n 44 | return n, err 45 | } 46 | 47 | // WriteHeader implements the http.ResponseWriter interface. 48 | func (w *ResponseWriter) WriteHeader(code int) { 49 | w.code = code 50 | w.ResponseWriter.WriteHeader(code) 51 | } 52 | 53 | // Code implements the ResponseRecorder interface. 54 | func (w *ResponseWriter) Code() int { return w.code } 55 | 56 | // Bytes implements the ResponseRecorder interface. 57 | func (w *ResponseWriter) Bytes() int { return w.bytes } 58 | 59 | // Hijack implements the http.Hijacker interface. 60 | func (w *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 61 | f, ok := w.ResponseWriter.(http.Hijacker) 62 | if !ok { 63 | return nil, nil, errors.New("hijacker not supported") 64 | } 65 | return f.Hijack() 66 | } 67 | 68 | // Flush implements the http.Flusher interface. 69 | func (w *ResponseWriter) Flush() { 70 | f, ok := w.ResponseWriter.(http.Flusher) 71 | if ok { 72 | f.Flush() 73 | } 74 | } 75 | 76 | // CloseNotify implements the http.CloseNotififer interface. 77 | func (w *ResponseWriter) CloseNotify() <-chan bool { 78 | f, ok := w.ResponseWriter.(http.CloseNotifier) 79 | if ok { 80 | return f.CloseNotify() 81 | } 82 | return make(chan bool) // ugh. 83 | } 84 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/transceiver_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package smpp 6 | 7 | import ( 8 | "fmt" 9 | "testing" 10 | "time" 11 | 12 | "github.com/fiorix/go-smpp/smpp/pdu" 13 | "github.com/fiorix/go-smpp/smpp/pdu/pdufield" 14 | "github.com/fiorix/go-smpp/smpp/pdu/pdutext" 15 | "github.com/fiorix/go-smpp/smpp/smpptest" 16 | ) 17 | 18 | func TestTransceiver(t *testing.T) { 19 | s := smpptest.NewUnstartedServer() 20 | s.Handler = func(c smpptest.Conn, p pdu.Body) { 21 | switch p.Header().ID { 22 | case pdu.SubmitSMID: 23 | r := pdu.NewSubmitSMResp() 24 | r.Header().Seq = p.Header().Seq 25 | r.Fields().Set(pdufield.MessageID, "foobar") 26 | c.Write(r) 27 | pf := p.Fields() 28 | rd := pf[pdufield.RegisteredDelivery] 29 | if rd.Bytes()[0] == 0 { 30 | return 31 | } 32 | r = pdu.NewDeliverSM() 33 | f := r.Fields() 34 | f.Set(pdufield.SourceAddr, pf[pdufield.SourceAddr]) 35 | f.Set(pdufield.DestinationAddr, pf[pdufield.DestinationAddr]) 36 | f.Set(pdufield.ShortMessage, pf[pdufield.ShortMessage]) 37 | c.Write(r) 38 | default: 39 | smpptest.EchoHandler(c, p) 40 | } 41 | } 42 | s.Start() 43 | defer s.Close() 44 | ack := make(chan error) 45 | receiver := func(p pdu.Body) { 46 | defer close(ack) 47 | if p.Header().ID != pdu.DeliverSMID { 48 | ack <- fmt.Errorf("unexpected PDU: %s", p.Header().ID) 49 | } 50 | } 51 | tc := &Transceiver{ 52 | Addr: s.Addr(), 53 | User: smpptest.DefaultUser, 54 | Passwd: smpptest.DefaultPasswd, 55 | Handler: receiver, 56 | } 57 | defer tc.Close() 58 | conn := <-tc.Bind() 59 | switch conn.Status() { 60 | case Connected: 61 | default: 62 | t.Fatal(conn.Error()) 63 | } 64 | sm, err := tc.Submit(&ShortMessage{ 65 | Src: "root", 66 | Dst: "foobar", 67 | Text: pdutext.Raw("Lorem ipsum"), 68 | Register: FinalDeliveryReceipt, 69 | }) 70 | if err != nil { 71 | t.Fatal(err) 72 | } 73 | msgid := sm.RespID() 74 | if msgid == "" { 75 | t.Fatalf("pdu does not contain msgid: %#v", sm.Resp()) 76 | } 77 | if msgid != "foobar" { 78 | t.Fatalf("unexpected msgid: want foobar, have %q", msgid) 79 | } 80 | select { 81 | case err := <-ack: 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | case <-time.After(time.Second): 86 | t.Fatal("timeout waiting for ack") 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /apiserver/pool.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 sms-api-server authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package apiserver 6 | 7 | import ( 8 | "sync" 9 | "sync/atomic" 10 | 11 | "github.com/fiorix/go-smpp/smpp/pdu" 12 | "github.com/fiorix/go-smpp/smpp/pdu/pdufield" 13 | ) 14 | 15 | // DeliveryReceipt contains the arguments of RPC call to SM.Deliver. 16 | // We only call it, not handle. 17 | type DeliveryReceipt struct { 18 | Src string `json:"src"` 19 | Dst string `json:"dst"` 20 | Text string `json:"text"` 21 | } 22 | 23 | var deliveryID uint64 24 | 25 | // deliveryPool let peers register themselves to receive broadcast 26 | // notifications with delivery receipts. 27 | type deliveryPool struct { 28 | mu sync.Mutex 29 | m map[uint64]chan *DeliveryReceipt 30 | } 31 | 32 | func newPool() *deliveryPool { 33 | return &deliveryPool{m: make(map[uint64]chan *DeliveryReceipt)} 34 | } 35 | 36 | // Handler handles DeliverSM coming from a Transceiver SMPP connection. 37 | // It broadcasts received delivery receipt to all registered peers. 38 | func (pool *deliveryPool) Handler(p pdu.Body) { 39 | switch p.Header().ID { 40 | case pdu.DeliverSMID: 41 | f := p.Fields() 42 | dr := &DeliveryReceipt{ 43 | Src: f[pdufield.SourceAddr].String(), 44 | Dst: f[pdufield.DestinationAddr].String(), 45 | Text: f[pdufield.ShortMessage].String(), 46 | } 47 | pool.Broadcast(dr) 48 | } 49 | } 50 | 51 | // Register returns a channel that get broadcasts from the pool. 52 | // The returned ID (uint64) is used to Unregister. 53 | func (pool *deliveryPool) Register() (uint64, <-chan *DeliveryReceipt) { 54 | id := atomic.AddUint64(&deliveryID, 1) 55 | c := make(chan *DeliveryReceipt, 10) 56 | pool.mu.Lock() 57 | pool.m[id] = c 58 | pool.mu.Unlock() 59 | return id, c 60 | } 61 | 62 | // Unregister removes an entry from the delivery receipt broadcast, 63 | // and closes the channel previously returned by Register. 64 | func (pool *deliveryPool) Unregister(id uint64) { 65 | pool.mu.Lock() 66 | c := pool.m[id] 67 | if c != nil { 68 | delete(pool.m, id) 69 | close(c) 70 | } 71 | pool.mu.Unlock() 72 | } 73 | 74 | // Broadcast broadcasts the given delivery receipt to all registered peers. 75 | func (pool *deliveryPool) Broadcast(r *DeliveryReceipt) { 76 | pool.mu.Lock() 77 | for _, c := range pool.m { 78 | select { 79 | case c <- r: 80 | default: 81 | // TODO: Increment drop counter here. 82 | } 83 | } 84 | pool.mu.Unlock() 85 | } 86 | -------------------------------------------------------------------------------- /vendor/github.com/julienschmidt/httprouter/path_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Julien Schmidt. All rights reserved. 2 | // Based on the path package, Copyright 2009 The Go Authors. 3 | // Use of this source code is governed by a BSD-style license that can be found 4 | // in the LICENSE file. 5 | 6 | package httprouter 7 | 8 | import ( 9 | "runtime" 10 | "testing" 11 | ) 12 | 13 | var cleanTests = []struct { 14 | path, result string 15 | }{ 16 | // Already clean 17 | {"/", "/"}, 18 | {"/abc", "/abc"}, 19 | {"/a/b/c", "/a/b/c"}, 20 | {"/abc/", "/abc/"}, 21 | {"/a/b/c/", "/a/b/c/"}, 22 | 23 | // missing root 24 | {"", "/"}, 25 | {"abc", "/abc"}, 26 | {"abc/def", "/abc/def"}, 27 | {"a/b/c", "/a/b/c"}, 28 | 29 | // Remove doubled slash 30 | {"//", "/"}, 31 | {"/abc//", "/abc/"}, 32 | {"/abc/def//", "/abc/def/"}, 33 | {"/a/b/c//", "/a/b/c/"}, 34 | {"/abc//def//ghi", "/abc/def/ghi"}, 35 | {"//abc", "/abc"}, 36 | {"///abc", "/abc"}, 37 | {"//abc//", "/abc/"}, 38 | 39 | // Remove . elements 40 | {".", "/"}, 41 | {"./", "/"}, 42 | {"/abc/./def", "/abc/def"}, 43 | {"/./abc/def", "/abc/def"}, 44 | {"/abc/.", "/abc/"}, 45 | 46 | // Remove .. elements 47 | {"..", "/"}, 48 | {"../", "/"}, 49 | {"../../", "/"}, 50 | {"../..", "/"}, 51 | {"../../abc", "/abc"}, 52 | {"/abc/def/ghi/../jkl", "/abc/def/jkl"}, 53 | {"/abc/def/../ghi/../jkl", "/abc/jkl"}, 54 | {"/abc/def/..", "/abc"}, 55 | {"/abc/def/../..", "/"}, 56 | {"/abc/def/../../..", "/"}, 57 | {"/abc/def/../../..", "/"}, 58 | {"/abc/def/../../../ghi/jkl/../../../mno", "/mno"}, 59 | 60 | // Combinations 61 | {"abc/./../def", "/def"}, 62 | {"abc//./../def", "/def"}, 63 | {"abc/../../././../def", "/def"}, 64 | } 65 | 66 | func TestPathClean(t *testing.T) { 67 | for _, test := range cleanTests { 68 | if s := CleanPath(test.path); s != test.result { 69 | t.Errorf("CleanPath(%q) = %q, want %q", test.path, s, test.result) 70 | } 71 | if s := CleanPath(test.result); s != test.result { 72 | t.Errorf("CleanPath(%q) = %q, want %q", test.result, s, test.result) 73 | } 74 | } 75 | } 76 | 77 | func TestPathCleanMallocs(t *testing.T) { 78 | if testing.Short() { 79 | t.Skip("skipping malloc count in short mode") 80 | } 81 | if runtime.GOMAXPROCS(0) > 1 { 82 | t.Log("skipping AllocsPerRun checks; GOMAXPROCS>1") 83 | return 84 | } 85 | 86 | for _, test := range cleanTests { 87 | allocs := testing.AllocsPerRun(100, func() { CleanPath(test.result) }) 88 | if allocs > 0 { 89 | t.Errorf("CleanPath(%q): %v allocs, want zero", test.result, allocs) 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/receiver.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package smpp 6 | 7 | import ( 8 | "crypto/tls" 9 | "fmt" 10 | "sync" 11 | "time" 12 | 13 | "github.com/fiorix/go-smpp/smpp/pdu" 14 | "github.com/fiorix/go-smpp/smpp/pdu/pdufield" 15 | ) 16 | 17 | // Receiver implements an SMPP client receiver. 18 | type Receiver struct { 19 | Addr string 20 | User string 21 | Passwd string 22 | SystemType string 23 | EnquireLink time.Duration 24 | TLS *tls.Config 25 | Handler HandlerFunc 26 | 27 | conn struct { 28 | sync.Mutex 29 | *client 30 | } 31 | } 32 | 33 | // HandlerFunc is the handler function that a Receiver calls 34 | // when a new PDU arrives. 35 | type HandlerFunc func(p pdu.Body) 36 | 37 | // Bind starts the Receiver. It creates a persistent connection 38 | // to the server, update its status via the returned channel, 39 | // and calls the registered Handler when new PDU arrives. 40 | // 41 | // Bind implements the ClientConn interface. 42 | func (r *Receiver) Bind() <-chan ConnStatus { 43 | r.conn.Lock() 44 | defer r.conn.Unlock() 45 | if r.conn.client != nil { 46 | return r.conn.Status 47 | } 48 | c := &client{ 49 | Addr: r.Addr, 50 | TLS: r.TLS, 51 | EnquireLink: r.EnquireLink, 52 | Status: make(chan ConnStatus, 1), 53 | BindFunc: r.bindFunc, 54 | } 55 | r.conn.client = c 56 | c.init() 57 | go c.Bind() 58 | return c.Status 59 | } 60 | 61 | func (r *Receiver) bindFunc(c Conn) error { 62 | p := pdu.NewBindReceiver() 63 | f := p.Fields() 64 | f.Set(pdufield.SystemID, r.User) 65 | f.Set(pdufield.Password, r.Passwd) 66 | f.Set(pdufield.SystemType, r.SystemType) 67 | resp, err := bind(c, p) 68 | if err != nil { 69 | return err 70 | } 71 | if resp.Header().ID != pdu.BindReceiverRespID { 72 | return fmt.Errorf("unexpected response for BindReceiver: %s", 73 | resp.Header().ID) 74 | } 75 | if r.Handler != nil { 76 | go r.handlePDU() 77 | } 78 | return nil 79 | } 80 | 81 | func (r *Receiver) handlePDU() { 82 | for { 83 | pdu, err := r.conn.Read() 84 | if err != nil { 85 | break 86 | } 87 | r.Handler(pdu) 88 | } 89 | } 90 | 91 | // Close implements the ClientConn interface. 92 | func (r *Receiver) Close() error { 93 | r.conn.Lock() 94 | defer r.conn.Unlock() 95 | if r.conn.client == nil { 96 | return ErrNotConnected 97 | } 98 | return r.conn.Close() 99 | } 100 | -------------------------------------------------------------------------------- /apiserver/cors_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 sms-api-server authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package apiserver 6 | 7 | import ( 8 | "bytes" 9 | "io" 10 | "io/ioutil" 11 | "net/http" 12 | "net/http/httptest" 13 | "net/url" 14 | "testing" 15 | ) 16 | 17 | func TestCORS(t *testing.T) { 18 | // set up the test server 19 | handler := func(w http.ResponseWriter, r *http.Request) { 20 | io.WriteString(w, "hello world") 21 | } 22 | mux := http.NewServeMux() 23 | mux.Handle("/", cors(handler, "GET")) 24 | ts := httptest.NewServer(mux) 25 | defer ts.Close() 26 | // create and issue an OPTIONS request and 27 | // validate response status and headers. 28 | req, err := http.NewRequest("OPTIONS", ts.URL, nil) 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | req.Header.Add("Origin", ts.URL) 33 | resp, err := http.DefaultClient.Do(req) 34 | if err != nil { 35 | t.Fatal(err) 36 | } 37 | defer resp.Body.Close() 38 | if resp.StatusCode != http.StatusOK { 39 | t.Fatalf("Unexpected response status: %s", resp.Status) 40 | } 41 | if resp.ContentLength != 0 { 42 | t.Fatalf("Unexpected Content-Length. Want 0, have %d", 43 | resp.ContentLength) 44 | } 45 | want := []struct { 46 | Name string 47 | Value string 48 | }{ 49 | {"Access-Control-Allow-Origin", ts.URL}, 50 | {"Access-Control-Allow-Methods", "GET, OPTIONS"}, 51 | {"Access-Control-Allow-Credentials", "true"}, 52 | } 53 | for _, th := range want { 54 | if v := resp.Header.Get(th.Name); v != th.Value { 55 | t.Fatalf("Unexpected value for %q. Want %q, have %q", 56 | th.Name, th.Value, v) 57 | } 58 | } 59 | // issue a GET request and validate response headers and body 60 | resp, err = http.Get(ts.URL) 61 | if err != nil { 62 | t.Fatal(err) 63 | } 64 | defer resp.Body.Close() 65 | want[0].Value = "*" // Origin 66 | for _, th := range want { 67 | if v := resp.Header.Get(th.Name); v != th.Value { 68 | t.Fatalf("Unexpected value for %q. Want %q, have %q", 69 | th.Name, th.Value, v) 70 | } 71 | } 72 | b, err := ioutil.ReadAll(resp.Body) 73 | if err != nil { 74 | t.Fatal(err) 75 | } 76 | wb := []byte("hello world") 77 | if !bytes.Equal(b, wb) { 78 | t.Fatalf("Unexpected response body. Want %q, have %q", b, wb) 79 | } 80 | // issue a POST request and validate response status 81 | resp, err = http.PostForm(ts.URL, url.Values{}) 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | defer resp.Body.Close() 86 | if resp.StatusCode != http.StatusMethodNotAllowed { 87 | t.Fatalf("Unexpected response status: %s", resp.Status) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdufield/types_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdufield 6 | 7 | import ( 8 | "bytes" 9 | "strconv" 10 | "testing" 11 | ) 12 | 13 | func TestFixed(t *testing.T) { 14 | f := &Fixed{Data: 0x34} 15 | if f.Len() != 1 { 16 | t.Fatalf("unexpected len: want 1, have %d", f.Len()) 17 | } 18 | if v, ok := f.Raw().(uint8); !ok { 19 | t.Fatalf("unexpected type: want uint8, have %#v", v) 20 | } 21 | ws := strconv.Itoa(0x34) 22 | if v := f.String(); v != string(ws) { 23 | t.Fatalf("unexpected string: want %q, have %q", ws, v) 24 | } 25 | wb := []byte{0x34} 26 | if v := f.Bytes(); !bytes.Equal(wb, v) { 27 | t.Fatalf("unexpected bytes: want %q, have %q", wb, v) 28 | } 29 | var b bytes.Buffer 30 | if err := f.SerializeTo(&b); err != nil { 31 | t.Fatalf("serialization failed: %s", err) 32 | } 33 | if v := b.Bytes(); !bytes.Equal(wb, v) { 34 | t.Fatalf("unexpected serialized bytes: want %q, have %q", wb, v) 35 | } 36 | } 37 | 38 | func TestVariable(t *testing.T) { 39 | want := []byte("foobar") 40 | f := &Variable{Data: want} 41 | lw := len(want) + 1 42 | if f.Len() != lw { 43 | t.Fatalf("unexpected len: want %d, have %d", lw, f.Len()) 44 | } 45 | if v, ok := f.Raw().([]byte); !ok { 46 | t.Fatalf("unexpected type: want []byte, have %#v", v) 47 | } 48 | if v := f.String(); v != string(want) { 49 | t.Fatalf("unexpected string: want %q have %q", want, v) 50 | } 51 | want = []byte("foobar\x00") 52 | if v := f.Bytes(); !bytes.Equal(want, v) { 53 | t.Fatalf("unexpected bytes: want %q, have %q", want, v) 54 | } 55 | var b bytes.Buffer 56 | if err := f.SerializeTo(&b); err != nil { 57 | t.Fatalf("serialization failed: %s", err) 58 | } 59 | if v := b.Bytes(); !bytes.Equal(want, v) { 60 | t.Fatalf("unexpected serialized bytes: want %q, have %q", want, v) 61 | } 62 | } 63 | 64 | func TestSM(t *testing.T) { 65 | want := []byte("foobar") 66 | f := &SM{Data: want} 67 | if f.Len() != len(want) { 68 | t.Fatalf("unexpected len: want %d, have %d", len(want), f.Len()) 69 | } 70 | if v, ok := f.Raw().([]byte); !ok { 71 | t.Fatalf("unexpected type: want []byte, have %#v", v) 72 | } 73 | if v := f.String(); v != string(want) { 74 | t.Fatalf("unexpected string: want %q have %q", want, v) 75 | } 76 | if v := f.Bytes(); !bytes.Equal(want, v) { 77 | t.Fatalf("unexpected bytes: want %q, have %q", want, v) 78 | } 79 | var b bytes.Buffer 80 | if err := f.SerializeTo(&b); err != nil { 81 | t.Fatalf("serialization failed: %s", err) 82 | } 83 | if v := b.Bytes(); !bytes.Equal(want, v) { 84 | t.Fatalf("unexpected serialized bytes: want %q, have %q", want, v) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httpmux/README.md: -------------------------------------------------------------------------------- 1 | # httpmux 2 | 3 | [![GoDoc](https://godoc.org/github.com/go-web/httpmux?status.svg)](http://godoc.org/github.com/go-web/httpmux) 4 | 5 | httpmux is an http request multiplexer for [Go](https://golang.org) built 6 | on top of the popular [httprouter](https://github.com/julienschmidt/httprouter), 7 | with modern features. 8 | 9 | The main motivation is to bring http handlers back to their original 10 | Handler interface as defined by [net/http](https://golang.org/pkg/net/http/) 11 | but leverage the speed and features of httprouter, such as dispatching 12 | handlers by method and handling HTTP 405 automatically. 13 | 14 | Another important aspect of httpmux is that it provides request context 15 | for arbitrary data, such as httprouter's URL parameters. Seasoned gophers 16 | migth immediately think this is an overlap of gorilla's 17 | [context](https://github.com/gorilla/context) package, however, their 18 | implementation rely on global variables and mutexes that can cause 19 | contention on heavily loaded systems. We use a different approach, which 20 | was stolen from [httpway](https://github.com/corneldamian/httpway), that 21 | hijacks the http.Request's Body field and replace it with an object that 22 | is an io.ReadCloser but also carries a [net/context](https://godoc.org/golang.org/x/net/context) 23 | object. This works well for middleware that wants to store arbitrary 24 | data in the request, serial, and once it hits your handler, the context 25 | can be passed around to goroutines. It is automatically cleared at the 26 | end of the middleware chain. 27 | 28 | There's been discussions for adding net/context to the standard library 29 | but most options require changing or creating a new interface and/or 30 | function signature for http handlers. In httpmux we remain close to 31 | net/http aiming at being more pluggable and composable with existing 32 | code in the wild, and if the Request type ends up getting a Context 33 | [field](https://github.com/golang/go/issues/14660), will be an easy 34 | change in httpmux. 35 | 36 | To make contexts more useful, httpmux provides the ability to register and 37 | chain wrapper handlers, middleware. Our implementation is based on blogs 38 | and especially [chi](https://github.com/pressly/chi), but much smaller. 39 | 40 | Last but not least, httpmux offers two more features for improving 41 | composability. First, is to configure a global prefix for all handlers 42 | in the multiplexer. This is for cases when you have to run your API 43 | behind a proxy, or mixed with other services, and have to be able to 44 | parse and understand the prefix in your handlers. Second, is to allow 45 | subtrees like [gin](https://github.com/gin-gonic/gin)'s groups, but 46 | in a more composable way. Think of cases where your code is an independent 47 | package that provides an http handler, that is tested and run isolated, 48 | but can be added to a larger API at run time. In chi, this is equivalent 49 | to the Mount function in their router. 50 | -------------------------------------------------------------------------------- /vendor/golang.org/x/text/encoding/unicode/override.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package unicode 6 | 7 | import ( 8 | "golang.org/x/text/transform" 9 | ) 10 | 11 | // BOMOverride returns a new decoder transformer that is identical to fallback, 12 | // except that the presence of a Byte Order Mark at the start of the input 13 | // causes it to switch to the corresponding Unicode decoding. It will only 14 | // consider BOMs for UTF-8, UTF-16BE, and UTF-16LE. 15 | // 16 | // This differs from using ExpectBOM by allowing a BOM to switch to UTF-8, not 17 | // just UTF-16 variants, and allowing falling back to any encoding scheme. 18 | // 19 | // This technique is recommended by the W3C for use in HTML 5: "For 20 | // compatibility with deployed content, the byte order mark (also known as BOM) 21 | // is considered more authoritative than anything else." 22 | // http://www.w3.org/TR/encoding/#specification-hooks 23 | // 24 | // Using BOMOverride is mostly intended for use cases where the first characters 25 | // of a fallback encoding are known to not be a BOM, for example, for valid HTML 26 | // and most encodings. 27 | func BOMOverride(fallback transform.Transformer) transform.Transformer { 28 | // TODO: possibly allow a variadic argument of unicode encodings to allow 29 | // specifying details of which fallbacks are supported as well as 30 | // specifying the details of the implementations. This would also allow for 31 | // support for UTF-32, which should not be supported by default. 32 | return &bomOverride{fallback: fallback} 33 | } 34 | 35 | type bomOverride struct { 36 | fallback transform.Transformer 37 | current transform.Transformer 38 | } 39 | 40 | func (d *bomOverride) Reset() { 41 | d.current = nil 42 | d.fallback.Reset() 43 | } 44 | 45 | var ( 46 | // TODO: we could use decode functions here, instead of allocating a new 47 | // decoder on every NewDecoder as IgnoreBOM decoders can be stateless. 48 | utf16le = UTF16(LittleEndian, IgnoreBOM) 49 | utf16be = UTF16(BigEndian, IgnoreBOM) 50 | ) 51 | 52 | const utf8BOM = "\ufeff" 53 | 54 | func (d *bomOverride) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { 55 | if d.current != nil { 56 | return d.current.Transform(dst, src, atEOF) 57 | } 58 | if len(src) < 3 && !atEOF { 59 | return 0, 0, transform.ErrShortSrc 60 | } 61 | d.current = d.fallback 62 | bomSize := 0 63 | if len(src) >= 2 { 64 | if src[0] == 0xFF && src[1] == 0xFE { 65 | d.current = utf16le.NewDecoder() 66 | bomSize = 2 67 | } else if src[0] == 0xFE && src[1] == 0xFF { 68 | d.current = utf16be.NewDecoder() 69 | bomSize = 2 70 | } else if len(src) >= 3 && 71 | src[0] == utf8BOM[0] && 72 | src[1] == utf8BOM[1] && 73 | src[2] == utf8BOM[2] { 74 | d.current = transform.Nop 75 | bomSize = 3 76 | } 77 | } 78 | if bomSize < len(src) { 79 | nDst, nSrc, err = d.current.Transform(dst, src[bomSize:], atEOF) 80 | } 81 | return nDst, nSrc + bomSize, err 82 | } 83 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/context/go17.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build go1.7 6 | 7 | package context 8 | 9 | import ( 10 | "context" // standard library's context, as of Go 1.7 11 | "time" 12 | ) 13 | 14 | var ( 15 | todo = context.TODO() 16 | background = context.Background() 17 | ) 18 | 19 | // Canceled is the error returned by Context.Err when the context is canceled. 20 | var Canceled = context.Canceled 21 | 22 | // DeadlineExceeded is the error returned by Context.Err when the context's 23 | // deadline passes. 24 | var DeadlineExceeded = context.DeadlineExceeded 25 | 26 | // WithCancel returns a copy of parent with a new Done channel. The returned 27 | // context's Done channel is closed when the returned cancel function is called 28 | // or when the parent context's Done channel is closed, whichever happens first. 29 | // 30 | // Canceling this context releases resources associated with it, so code should 31 | // call cancel as soon as the operations running in this Context complete. 32 | func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { 33 | ctx, f := context.WithCancel(parent) 34 | return ctx, CancelFunc(f) 35 | } 36 | 37 | // WithDeadline returns a copy of the parent context with the deadline adjusted 38 | // to be no later than d. If the parent's deadline is already earlier than d, 39 | // WithDeadline(parent, d) is semantically equivalent to parent. The returned 40 | // context's Done channel is closed when the deadline expires, when the returned 41 | // cancel function is called, or when the parent context's Done channel is 42 | // closed, whichever happens first. 43 | // 44 | // Canceling this context releases resources associated with it, so code should 45 | // call cancel as soon as the operations running in this Context complete. 46 | func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { 47 | ctx, f := context.WithDeadline(parent, deadline) 48 | return ctx, CancelFunc(f) 49 | } 50 | 51 | // WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). 52 | // 53 | // Canceling this context releases resources associated with it, so code should 54 | // call cancel as soon as the operations running in this Context complete: 55 | // 56 | // func slowOperationWithTimeout(ctx context.Context) (Result, error) { 57 | // ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) 58 | // defer cancel() // releases resources if slowOperation completes before timeout elapses 59 | // return slowOperation(ctx) 60 | // } 61 | func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { 62 | return WithDeadline(parent, time.Now().Add(timeout)) 63 | } 64 | 65 | // WithValue returns a copy of parent in which the value associated with key is 66 | // val. 67 | // 68 | // Use context Values only for request-scoped data that transits processes and 69 | // APIs, not for passing optional parameters to functions. 70 | func WithValue(parent Context, key interface{}, val interface{}) Context { 71 | return context.WithValue(parent, key, val) 72 | } 73 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdufield/list.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdufield 6 | 7 | import ( 8 | "bytes" 9 | "encoding/binary" 10 | "fmt" 11 | "io" 12 | 13 | "github.com/fiorix/go-smpp/smpp/pdu/pdutext" 14 | ) 15 | 16 | // List is a list of PDU fields. 17 | type List []Name 18 | 19 | // Decode decodes binary data in the given buffer to build a Map. 20 | // 21 | // If the ShortMessage field is present, and DataCoding as well, 22 | // we attempt to decode text automatically. See pdutext package 23 | // for more information. 24 | func (l List) Decode(r *bytes.Buffer) (Map, error) { 25 | f := make(Map) 26 | loop: 27 | for _, k := range l { 28 | switch k { 29 | case 30 | AddressRange, 31 | DestinationAddr, 32 | ErrorCode, 33 | FinalDate, 34 | MessageID, 35 | MessageState, 36 | Password, 37 | ScheduleDeliveryTime, 38 | ServiceType, 39 | SourceAddr, 40 | SystemID, 41 | SystemType, 42 | ValidityPeriod: 43 | b, err := r.ReadBytes(0x00) 44 | if err == io.EOF { 45 | break loop 46 | } 47 | if err != nil { 48 | return nil, err 49 | } 50 | f[k] = &Variable{Data: b} 51 | case 52 | AddrNPI, 53 | AddrTON, 54 | DataCoding, 55 | DestAddrNPI, 56 | DestAddrTON, 57 | ESMClass, 58 | InterfaceVersion, 59 | PriorityFlag, 60 | ProtocolID, 61 | RegisteredDelivery, 62 | ReplaceIfPresentFlag, 63 | SMDefaultMsgID, 64 | SourceAddrNPI, 65 | SourceAddrTON: 66 | b, err := r.ReadByte() 67 | if err == io.EOF { 68 | break loop 69 | } 70 | if err != nil { 71 | return nil, err 72 | } 73 | f[k] = &Fixed{Data: b} 74 | case SMLength: 75 | b, err := r.ReadByte() 76 | if err == io.EOF { 77 | break loop 78 | } 79 | if err != nil { 80 | return nil, err 81 | } 82 | l := int(b) 83 | f[k] = &Fixed{Data: b} 84 | if r.Len() < l { 85 | return nil, fmt.Errorf("short read for smlength: want %d, have %d", 86 | l, r.Len()) 87 | } 88 | f[ShortMessage] = &SM{Data: r.Next(l)} 89 | case ShortMessage: 90 | sm, exists := f[ShortMessage].(*SM) 91 | if !exists { 92 | continue 93 | } 94 | c, exists := f[DataCoding].(*Fixed) 95 | if !exists { 96 | continue 97 | } 98 | sm.Data = pdutext.Decode(pdutext.DataCoding(c.Data), sm.Data) 99 | } 100 | } 101 | return f, nil 102 | } 103 | 104 | // DecodeTLV scans the given byte slice to build a TLVMap from binary data. 105 | func (l List) DecodeTLV(r *bytes.Buffer) (TLVMap, error) { 106 | t := make(TLVMap) 107 | for r.Len() >= 4 { 108 | b := r.Next(4) 109 | ft := TLVType(binary.BigEndian.Uint16(b[0:2])) 110 | fl := binary.BigEndian.Uint16(b[2:4]) 111 | if r.Len() < int(fl) { 112 | return nil, fmt.Errorf("not enough data for tag %#x: want %d, have %d", 113 | ft, fl, r.Len()) 114 | } 115 | b = r.Next(int(fl)) 116 | t[ft] = &TLVBody{ 117 | Tag: ft, 118 | Len: fl, 119 | data: b, 120 | } 121 | } 122 | return t, nil 123 | } 124 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package smpp_test 6 | 7 | import ( 8 | "io" 9 | "log" 10 | "net/http" 11 | "time" 12 | 13 | "github.com/fiorix/go-smpp/smpp" 14 | "github.com/fiorix/go-smpp/smpp/pdu" 15 | "github.com/fiorix/go-smpp/smpp/pdu/pdufield" 16 | "github.com/fiorix/go-smpp/smpp/pdu/pdutext" 17 | ) 18 | 19 | func ExampleReceiver() { 20 | f := func(p pdu.Body) { 21 | switch p.Header().ID { 22 | case pdu.DeliverSMID: 23 | f := p.Fields() 24 | src := f[pdufield.SourceAddr] 25 | dst := f[pdufield.DestinationAddr] 26 | txt := f[pdufield.ShortMessage] 27 | log.Printf("Short message from=%q to=%q: %q", 28 | src, dst, txt) 29 | } 30 | } 31 | r := &smpp.Receiver{ 32 | Addr: "localhost:2775", 33 | User: "foobar", 34 | Passwd: "secret", 35 | Handler: f, 36 | } 37 | conn := r.Bind() // make persistent connection. 38 | time.AfterFunc(10*time.Second, func() { r.Close() }) 39 | for c := range conn { 40 | log.Println("SMPP connection status:", c.Status()) 41 | } 42 | } 43 | 44 | func ExampleTransmitter() { 45 | tx := &smpp.Transmitter{ 46 | Addr: "localhost:2775", 47 | User: "foobar", 48 | Passwd: "secret", 49 | } 50 | conn := <-tx.Bind() // make persistent connection. 51 | switch conn.Status() { 52 | case smpp.Connected: 53 | sm, err := tx.Submit(&smpp.ShortMessage{ 54 | Src: "sender", 55 | Dst: "recipient", 56 | Text: pdutext.Latin1("Olá mundo"), 57 | Register: smpp.NoDeliveryReceipt, 58 | }) 59 | if err != nil { 60 | log.Fatal(err) 61 | } 62 | log.Println("Message ID:", sm.RespID()) 63 | default: 64 | log.Fatal(conn.Error()) 65 | } 66 | } 67 | 68 | func ExampleTransceiver() { 69 | f := func(p pdu.Body) { 70 | switch p.Header().ID { 71 | case pdu.DeliverSMID: 72 | f := p.Fields() 73 | src := f[pdufield.SourceAddr] 74 | dst := f[pdufield.DestinationAddr] 75 | txt := f[pdufield.ShortMessage] 76 | log.Printf("Short message from=%q to=%q: %q", 77 | src, dst, txt) 78 | } 79 | } 80 | tx := &smpp.Transceiver{ 81 | Addr: "localhost:2775", 82 | User: "foobar", 83 | Passwd: "secret", 84 | Handler: f, 85 | } 86 | conn := tx.Bind() // make persistent connection. 87 | go func() { 88 | for c := range conn { 89 | log.Println("SMPP connection status:", c.Status()) 90 | } 91 | }() 92 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 93 | sm, err := tx.Submit(&smpp.ShortMessage{ 94 | Src: r.FormValue("src"), 95 | Dst: r.FormValue("dst"), 96 | Text: pdutext.Raw(r.FormValue("text")), 97 | Register: smpp.FinalDeliveryReceipt, 98 | }) 99 | if err == smpp.ErrNotConnected { 100 | http.Error(w, "Oops.", http.StatusServiceUnavailable) 101 | return 102 | } 103 | if err != nil { 104 | http.Error(w, err.Error(), http.StatusBadRequest) 105 | return 106 | } 107 | io.WriteString(w, sm.RespID()) 108 | }) 109 | log.Fatal(http.ListenAndServe(":8080", nil)) 110 | } 111 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/websocket/client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "bufio" 9 | "crypto/tls" 10 | "io" 11 | "net" 12 | "net/http" 13 | "net/url" 14 | ) 15 | 16 | // DialError is an error that occurs while dialling a websocket server. 17 | type DialError struct { 18 | *Config 19 | Err error 20 | } 21 | 22 | func (e *DialError) Error() string { 23 | return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error() 24 | } 25 | 26 | // NewConfig creates a new WebSocket config for client connection. 27 | func NewConfig(server, origin string) (config *Config, err error) { 28 | config = new(Config) 29 | config.Version = ProtocolVersionHybi13 30 | config.Location, err = url.ParseRequestURI(server) 31 | if err != nil { 32 | return 33 | } 34 | config.Origin, err = url.ParseRequestURI(origin) 35 | if err != nil { 36 | return 37 | } 38 | config.Header = http.Header(make(map[string][]string)) 39 | return 40 | } 41 | 42 | // NewClient creates a new WebSocket client connection over rwc. 43 | func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) { 44 | br := bufio.NewReader(rwc) 45 | bw := bufio.NewWriter(rwc) 46 | err = hybiClientHandshake(config, br, bw) 47 | if err != nil { 48 | return 49 | } 50 | buf := bufio.NewReadWriter(br, bw) 51 | ws = newHybiClientConn(config, buf, rwc) 52 | return 53 | } 54 | 55 | // Dial opens a new client connection to a WebSocket. 56 | func Dial(url_, protocol, origin string) (ws *Conn, err error) { 57 | config, err := NewConfig(url_, origin) 58 | if err != nil { 59 | return nil, err 60 | } 61 | if protocol != "" { 62 | config.Protocol = []string{protocol} 63 | } 64 | return DialConfig(config) 65 | } 66 | 67 | var portMap = map[string]string{ 68 | "ws": "80", 69 | "wss": "443", 70 | } 71 | 72 | func parseAuthority(location *url.URL) string { 73 | if _, ok := portMap[location.Scheme]; ok { 74 | if _, _, err := net.SplitHostPort(location.Host); err != nil { 75 | return net.JoinHostPort(location.Host, portMap[location.Scheme]) 76 | } 77 | } 78 | return location.Host 79 | } 80 | 81 | // DialConfig opens a new client connection to a WebSocket with a config. 82 | func DialConfig(config *Config) (ws *Conn, err error) { 83 | var client net.Conn 84 | if config.Location == nil { 85 | return nil, &DialError{config, ErrBadWebSocketLocation} 86 | } 87 | if config.Origin == nil { 88 | return nil, &DialError{config, ErrBadWebSocketOrigin} 89 | } 90 | switch config.Location.Scheme { 91 | case "ws": 92 | client, err = net.Dial("tcp", parseAuthority(config.Location)) 93 | 94 | case "wss": 95 | client, err = tls.Dial("tcp", parseAuthority(config.Location), config.TlsConfig) 96 | 97 | default: 98 | err = ErrBadScheme 99 | } 100 | if err != nil { 101 | goto Error 102 | } 103 | 104 | ws, err = NewClient(config, client) 105 | if err != nil { 106 | client.Close() 107 | goto Error 108 | } 109 | return 110 | 111 | Error: 112 | return nil, &DialError{config, err} 113 | } 114 | -------------------------------------------------------------------------------- /vendor/github.com/julienschmidt/httprouter/path.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Julien Schmidt. All rights reserved. 2 | // Based on the path package, Copyright 2009 The Go Authors. 3 | // Use of this source code is governed by a BSD-style license that can be found 4 | // in the LICENSE file. 5 | 6 | package httprouter 7 | 8 | // CleanPath is the URL version of path.Clean, it returns a canonical URL path 9 | // for p, eliminating . and .. elements. 10 | // 11 | // The following rules are applied iteratively until no further processing can 12 | // be done: 13 | // 1. Replace multiple slashes with a single slash. 14 | // 2. Eliminate each . path name element (the current directory). 15 | // 3. Eliminate each inner .. path name element (the parent directory) 16 | // along with the non-.. element that precedes it. 17 | // 4. Eliminate .. elements that begin a rooted path: 18 | // that is, replace "/.." by "/" at the beginning of a path. 19 | // 20 | // If the result of this process is an empty string, "/" is returned 21 | func CleanPath(p string) string { 22 | // Turn empty string into "/" 23 | if p == "" { 24 | return "/" 25 | } 26 | 27 | n := len(p) 28 | var buf []byte 29 | 30 | // Invariants: 31 | // reading from path; r is index of next byte to process. 32 | // writing to buf; w is index of next byte to write. 33 | 34 | // path must start with '/' 35 | r := 1 36 | w := 1 37 | 38 | if p[0] != '/' { 39 | r = 0 40 | buf = make([]byte, n+1) 41 | buf[0] = '/' 42 | } 43 | 44 | trailing := n > 2 && p[n-1] == '/' 45 | 46 | // A bit more clunky without a 'lazybuf' like the path package, but the loop 47 | // gets completely inlined (bufApp). So in contrast to the path package this 48 | // loop has no expensive function calls (except 1x make) 49 | 50 | for r < n { 51 | switch { 52 | case p[r] == '/': 53 | // empty path element, trailing slash is added after the end 54 | r++ 55 | 56 | case p[r] == '.' && r+1 == n: 57 | trailing = true 58 | r++ 59 | 60 | case p[r] == '.' && p[r+1] == '/': 61 | // . element 62 | r++ 63 | 64 | case p[r] == '.' && p[r+1] == '.' && (r+2 == n || p[r+2] == '/'): 65 | // .. element: remove to last / 66 | r += 2 67 | 68 | if w > 1 { 69 | // can backtrack 70 | w-- 71 | 72 | if buf == nil { 73 | for w > 1 && p[w] != '/' { 74 | w-- 75 | } 76 | } else { 77 | for w > 1 && buf[w] != '/' { 78 | w-- 79 | } 80 | } 81 | } 82 | 83 | default: 84 | // real path element. 85 | // add slash if needed 86 | if w > 1 { 87 | bufApp(&buf, p, w, '/') 88 | w++ 89 | } 90 | 91 | // copy element 92 | for r < n && p[r] != '/' { 93 | bufApp(&buf, p, w, p[r]) 94 | w++ 95 | r++ 96 | } 97 | } 98 | } 99 | 100 | // re-append trailing slash 101 | if trailing && w > 1 { 102 | bufApp(&buf, p, w, '/') 103 | w++ 104 | } 105 | 106 | if buf == nil { 107 | return p[:w] 108 | } 109 | return string(buf[:w]) 110 | } 111 | 112 | // internal helper to lazily create a buffer if necessary 113 | func bufApp(buf *[]byte, s string, w int, c byte) { 114 | if *buf == nil { 115 | if s[w] == c { 116 | return 117 | } 118 | 119 | *buf = make([]byte, len(s)) 120 | copy(*buf, s[:w]) 121 | } 122 | (*buf)[w] = c 123 | } 124 | -------------------------------------------------------------------------------- /vendor/golang.org/x/text/encoding/internal/identifier/identifier.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:generate go run gen.go 6 | 7 | // Package identifier defines the contract between implementations of Encoding 8 | // and Index by defining identifiers that uniquely identify standardized coded 9 | // character sets (CCS) and character encoding schemes (CES), which we will 10 | // together refer to as encodings, for which Encoding implementations provide 11 | // converters to and from UTF-8. This package is typically only of concern to 12 | // implementers of Indexes and Encodings. 13 | // 14 | // One part of the identifier is the MIB code, which is defined by IANA and 15 | // uniquely identifies a CCS or CES. Each code is associated with data that 16 | // references authorities, official documentation as well as aliases and MIME 17 | // names. 18 | // 19 | // Not all CESs are covered by the IANA registry. The "other" string that is 20 | // returned by ID can be used to identify other character sets or versions of 21 | // existing ones. 22 | // 23 | // It is recommended that each package that provides a set of Encodings provide 24 | // the All and Common variables to reference all supported encodings and 25 | // commonly used subset. This allows Index implementations to include all 26 | // available encodings without explicitly referencing or knowing about them. 27 | package identifier 28 | 29 | // Note: this package is internal, but could be made public if there is a need 30 | // for writing third-party Indexes and Encodings. 31 | 32 | // References: 33 | // - http://source.icu-project.org/repos/icu/icu/trunk/source/data/mappings/convrtrs.txt 34 | // - http://www.iana.org/assignments/character-sets/character-sets.xhtml 35 | // - http://www.iana.org/assignments/ianacharset-mib/ianacharset-mib 36 | // - http://www.ietf.org/rfc/rfc2978.txt 37 | // - http://www.unicode.org/reports/tr22/ 38 | // - http://www.w3.org/TR/encoding/ 39 | // - http://www.w3.org/TR/encoding/indexes/encodings.json 40 | // - https://encoding.spec.whatwg.org/ 41 | // - https://tools.ietf.org/html/rfc6657#section-5 42 | 43 | // Interface can be implemented by Encodings to define the CCS or CES for which 44 | // it implements conversions. 45 | type Interface interface { 46 | // ID returns an encoding identifier. Exactly one of the mib and other 47 | // values should be non-zero. 48 | // 49 | // In the usual case it is only necessary to indicate the MIB code. The 50 | // other string can be used to specify encodings for which there is no MIB, 51 | // such as "x-mac-dingbat". 52 | // 53 | // The other string may only contain the characters a-z, A-Z, 0-9, - and _. 54 | ID() (mib MIB, other string) 55 | 56 | // NOTE: the restrictions on the encoding are to allow extending the syntax 57 | // with additional information such as versions, vendors and other variants. 58 | } 59 | 60 | // A MIB identifies an encoding. It is derived from the IANA MIB codes and adds 61 | // some identifiers for some encodings that are not covered by the IANA 62 | // standard. 63 | // 64 | // See http://www.iana.org/assignments/ianacharset-mib. 65 | type MIB uint16 66 | 67 | // These additional MIB types are not defined in IANA. They are added because 68 | // they are common and defined within the text repo. 69 | const ( 70 | // Unofficial marks the start of encodings not registered by IANA. 71 | Unofficial MIB = 10000 + iota 72 | 73 | // TODO: add Replacement? 74 | 75 | // XUserDefined is the code for x-user-defined. 76 | XUserDefined 77 | 78 | // MacintoshCyrillic is the code for x-mac-cyrillic. 79 | MacintoshCyrillic 80 | ) 81 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/websocket/server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "bufio" 9 | "fmt" 10 | "io" 11 | "net/http" 12 | ) 13 | 14 | func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request, config *Config, handshake func(*Config, *http.Request) error) (conn *Conn, err error) { 15 | var hs serverHandshaker = &hybiServerHandshaker{Config: config} 16 | code, err := hs.ReadHandshake(buf.Reader, req) 17 | if err == ErrBadWebSocketVersion { 18 | fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) 19 | fmt.Fprintf(buf, "Sec-WebSocket-Version: %s\r\n", SupportedProtocolVersion) 20 | buf.WriteString("\r\n") 21 | buf.WriteString(err.Error()) 22 | buf.Flush() 23 | return 24 | } 25 | if err != nil { 26 | fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) 27 | buf.WriteString("\r\n") 28 | buf.WriteString(err.Error()) 29 | buf.Flush() 30 | return 31 | } 32 | if handshake != nil { 33 | err = handshake(config, req) 34 | if err != nil { 35 | code = http.StatusForbidden 36 | fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) 37 | buf.WriteString("\r\n") 38 | buf.Flush() 39 | return 40 | } 41 | } 42 | err = hs.AcceptHandshake(buf.Writer) 43 | if err != nil { 44 | code = http.StatusBadRequest 45 | fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) 46 | buf.WriteString("\r\n") 47 | buf.Flush() 48 | return 49 | } 50 | conn = hs.NewServerConn(buf, rwc, req) 51 | return 52 | } 53 | 54 | // Server represents a server of a WebSocket. 55 | type Server struct { 56 | // Config is a WebSocket configuration for new WebSocket connection. 57 | Config 58 | 59 | // Handshake is an optional function in WebSocket handshake. 60 | // For example, you can check, or don't check Origin header. 61 | // Another example, you can select config.Protocol. 62 | Handshake func(*Config, *http.Request) error 63 | 64 | // Handler handles a WebSocket connection. 65 | Handler 66 | } 67 | 68 | // ServeHTTP implements the http.Handler interface for a WebSocket 69 | func (s Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { 70 | s.serveWebSocket(w, req) 71 | } 72 | 73 | func (s Server) serveWebSocket(w http.ResponseWriter, req *http.Request) { 74 | rwc, buf, err := w.(http.Hijacker).Hijack() 75 | if err != nil { 76 | panic("Hijack failed: " + err.Error()) 77 | } 78 | // The server should abort the WebSocket connection if it finds 79 | // the client did not send a handshake that matches with protocol 80 | // specification. 81 | defer rwc.Close() 82 | conn, err := newServerConn(rwc, buf, req, &s.Config, s.Handshake) 83 | if err != nil { 84 | return 85 | } 86 | if conn == nil { 87 | panic("unexpected nil conn") 88 | } 89 | s.Handler(conn) 90 | } 91 | 92 | // Handler is a simple interface to a WebSocket browser client. 93 | // It checks if Origin header is valid URL by default. 94 | // You might want to verify websocket.Conn.Config().Origin in the func. 95 | // If you use Server instead of Handler, you could call websocket.Origin and 96 | // check the origin in your Handshake func. So, if you want to accept 97 | // non-browser clients, which do not send an Origin header, set a 98 | // Server.Handshake that does not check the origin. 99 | type Handler func(*Conn) 100 | 101 | func checkOrigin(config *Config, req *http.Request) (err error) { 102 | config.Origin, err = Origin(config, req) 103 | if err == nil && config.Origin == nil { 104 | return fmt.Errorf("null origin") 105 | } 106 | return err 107 | } 108 | 109 | // ServeHTTP implements the http.Handler interface for a WebSocket 110 | func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { 111 | s := Server{Handler: h, Handshake: checkOrigin} 112 | s.serveWebSocket(w, req) 113 | } 114 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/conn.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package smpp 6 | 7 | import ( 8 | "bufio" 9 | "bytes" 10 | "crypto/tls" 11 | "errors" 12 | "io" 13 | "net" 14 | "sync" 15 | 16 | "github.com/fiorix/go-smpp/smpp/pdu" 17 | ) 18 | 19 | var ( 20 | // ErrNotConnected is returned on attempts to use a dead connection. 21 | ErrNotConnected = errors.New("not connected") 22 | 23 | // ErrNotBound is returned on attempts to use a Transmitter, 24 | // Receiver or Transceiver before calling Bind. 25 | ErrNotBound = errors.New("not bound") 26 | ) 27 | 28 | // Conn is an SMPP connection. 29 | type Conn interface { 30 | Reader 31 | Writer 32 | Closer 33 | } 34 | 35 | // Reader is the interface that wraps the basic Read method. 36 | type Reader interface { 37 | // Read reads PDU binary data off the wire and returns it. 38 | Read() (pdu.Body, error) 39 | } 40 | 41 | // Writer is the interface that wraps the basic Write method. 42 | type Writer interface { 43 | // Write serializes the given PDU and writes to the connection. 44 | Write(w pdu.Body) error 45 | } 46 | 47 | // Closer is the interface that wraps the basic Close method. 48 | type Closer interface { 49 | // Close terminates the connection. 50 | Close() error 51 | } 52 | 53 | // Dial dials to the SMPP server and returns a Conn, or error. 54 | // TLS is only used if provided. 55 | func Dial(addr string, TLS *tls.Config) (Conn, error) { 56 | if addr == "" { 57 | addr = "localhost:2775" 58 | } 59 | fd, err := net.Dial("tcp", addr) 60 | if err != nil { 61 | return nil, err 62 | } 63 | if TLS != nil { 64 | fd = tls.Client(fd, TLS) 65 | } 66 | c := &conn{ 67 | rwc: fd, 68 | r: bufio.NewReader(fd), 69 | w: bufio.NewWriter(fd), 70 | } 71 | return c, nil 72 | } 73 | 74 | // conn provides the basics of a single client connection and 75 | // implements the Conn interface. 76 | type conn struct { 77 | rwc net.Conn 78 | r *bufio.Reader 79 | w *bufio.Writer 80 | } 81 | 82 | // Read implements the Conn interface. 83 | func (c *conn) Read() (pdu.Body, error) { 84 | return pdu.Decode(c.r) 85 | } 86 | 87 | // Write implements the Conn interface. 88 | func (c *conn) Write(w pdu.Body) error { 89 | var b bytes.Buffer 90 | err := w.SerializeTo(&b) 91 | if err != nil { 92 | return err 93 | } 94 | _, err = io.Copy(c.w, &b) 95 | if err != nil { 96 | return err 97 | } 98 | return c.w.Flush() 99 | } 100 | 101 | // Close implements the Conn interface. 102 | func (c *conn) Close() error { 103 | return c.rwc.Close() 104 | } 105 | 106 | // connSwitch implements the Conn interface but allows switching 107 | // the actual Conn object it wraps. 108 | // 109 | // If no Conn is available, any attempt to Read/Write/Close 110 | // returns ErrNotConnected. 111 | type connSwitch struct { 112 | mu sync.Mutex 113 | c Conn 114 | } 115 | 116 | // Set sets the underlying Conn with the given one. 117 | // If we hold a Conn already, it will be closed before switching over. 118 | func (cs *connSwitch) Set(c Conn) { 119 | cs.mu.Lock() 120 | if cs.c != nil { 121 | cs.c.Close() 122 | } 123 | cs.c = c 124 | cs.mu.Unlock() 125 | } 126 | 127 | // Read implements the Conn interface. 128 | func (cs *connSwitch) Read() (pdu.Body, error) { 129 | cs.mu.Lock() 130 | conn := cs.c 131 | cs.mu.Unlock() 132 | if conn == nil { 133 | return nil, ErrNotConnected 134 | } 135 | return conn.Read() 136 | } 137 | 138 | // Write implements the Conn interface. 139 | func (cs *connSwitch) Write(w pdu.Body) error { 140 | cs.mu.Lock() 141 | defer cs.mu.Unlock() 142 | if cs.c == nil { 143 | return ErrNotConnected 144 | } 145 | return cs.c.Write(w) 146 | } 147 | 148 | // Close implements the Conn interface. 149 | func (cs *connSwitch) Close() error { 150 | cs.mu.Lock() 151 | defer cs.mu.Unlock() 152 | if cs.c == nil { 153 | return ErrNotConnected 154 | } 155 | err := cs.c.Close() 156 | cs.c = nil 157 | return err 158 | } 159 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/transmitter_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package smpp 6 | 7 | import ( 8 | "testing" 9 | "time" 10 | 11 | "github.com/fiorix/go-smpp/smpp/pdu" 12 | "github.com/fiorix/go-smpp/smpp/pdu/pdufield" 13 | "github.com/fiorix/go-smpp/smpp/pdu/pdutext" 14 | "github.com/fiorix/go-smpp/smpp/smpptest" 15 | ) 16 | 17 | func TestShortMessage(t *testing.T) { 18 | s := smpptest.NewUnstartedServer() 19 | s.Handler = func(c smpptest.Conn, p pdu.Body) { 20 | switch p.Header().ID { 21 | case pdu.SubmitSMID: 22 | r := pdu.NewSubmitSMResp() 23 | r.Header().Seq = p.Header().Seq 24 | r.Fields().Set(pdufield.MessageID, "foobar") 25 | c.Write(r) 26 | default: 27 | smpptest.EchoHandler(c, p) 28 | } 29 | } 30 | s.Start() 31 | defer s.Close() 32 | tx := &Transmitter{ 33 | Addr: s.Addr(), 34 | User: smpptest.DefaultUser, 35 | Passwd: smpptest.DefaultPasswd, 36 | } 37 | defer tx.Close() 38 | conn := <-tx.Bind() 39 | switch conn.Status() { 40 | case Connected: 41 | default: 42 | t.Fatal(conn.Error()) 43 | } 44 | sm, err := tx.Submit(&ShortMessage{ 45 | Src: "root", 46 | Dst: "foobar", 47 | Text: pdutext.Raw("Lorem ipsum"), 48 | Validity: 10 * time.Minute, 49 | Register: NoDeliveryReceipt, 50 | }) 51 | if err != nil { 52 | t.Fatal(err) 53 | } 54 | msgid := sm.RespID() 55 | if msgid == "" { 56 | t.Fatalf("pdu does not contain msgid: %#v", sm.Resp()) 57 | } 58 | if msgid != "foobar" { 59 | t.Fatalf("unexpected msgid: want foobar, have %q", msgid) 60 | } 61 | } 62 | 63 | func TestLongMessage(t *testing.T) { 64 | s := smpptest.NewUnstartedServer() 65 | s.Handler = func(c smpptest.Conn, p pdu.Body) { 66 | switch p.Header().ID { 67 | case pdu.SubmitSMID: 68 | r := pdu.NewSubmitSMResp() 69 | r.Header().Seq = p.Header().Seq 70 | r.Fields().Set(pdufield.MessageID, "foobar") 71 | c.Write(r) 72 | default: 73 | smpptest.EchoHandler(c, p) 74 | } 75 | } 76 | s.Start() 77 | defer s.Close() 78 | tx := &Transmitter{ 79 | Addr: s.Addr(), 80 | User: smpptest.DefaultUser, 81 | Passwd: smpptest.DefaultPasswd, 82 | } 83 | defer tx.Close() 84 | conn := <-tx.Bind() 85 | switch conn.Status() { 86 | case Connected: 87 | default: 88 | t.Fatal(conn.Error()) 89 | } 90 | sm, err := tx.SubmitLongMsg(&ShortMessage{ 91 | Src: "root", 92 | Dst: "foobar", 93 | Text: pdutext.Raw("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam consequat nisl enim, vel finibus neque aliquet sit amet. Interdum et malesuada fames ac ante ipsum primis in faucibus."), 94 | Validity: 10 * time.Minute, 95 | Register: NoDeliveryReceipt, 96 | }) 97 | if err != nil { 98 | t.Fatal(err) 99 | } 100 | msgid := sm.RespID() 101 | if msgid == "" { 102 | t.Fatalf("pdu does not contain msgid: %#v", sm.Resp()) 103 | } 104 | if msgid != "foobar" { 105 | t.Fatalf("unexpected msgid: want foobar, have %q", msgid) 106 | } 107 | } 108 | 109 | func TestQuerySM(t *testing.T) { 110 | s := smpptest.NewUnstartedServer() 111 | s.Handler = func(c smpptest.Conn, p pdu.Body) { 112 | r := pdu.NewQuerySMResp() 113 | r.Header().Seq = p.Header().Seq 114 | r.Fields().Set(pdufield.MessageID, p.Fields()[pdufield.MessageID]) 115 | r.Fields().Set(pdufield.MessageState, 2) 116 | c.Write(r) 117 | } 118 | s.Start() 119 | defer s.Close() 120 | tx := &Transmitter{ 121 | Addr: s.Addr(), 122 | User: smpptest.DefaultUser, 123 | Passwd: smpptest.DefaultPasswd, 124 | } 125 | defer tx.Close() 126 | conn := <-tx.Bind() 127 | switch conn.Status() { 128 | case Connected: 129 | default: 130 | t.Fatal(conn.Error()) 131 | } 132 | qr, err := tx.QuerySM("root", "13") 133 | if err != nil { 134 | t.Fatal(err) 135 | } 136 | if qr.MsgID != "13" { 137 | t.Fatalf("unexpected msgid: want 13, have %s", qr.MsgID) 138 | } 139 | if qr.MsgState != "DELIVERED" { 140 | t.Fatalf("unexpected state: want DELIVERED, have %q", qr.MsgState) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /vendor/golang.org/x/text/encoding/internal/identifier/gen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build ignore 6 | 7 | package main 8 | 9 | import ( 10 | "bytes" 11 | "encoding/xml" 12 | "fmt" 13 | "io" 14 | "log" 15 | "strings" 16 | 17 | "golang.org/x/text/internal/gen" 18 | ) 19 | 20 | type registry struct { 21 | XMLName xml.Name `xml:"registry"` 22 | Updated string `xml:"updated"` 23 | Registry []struct { 24 | ID string `xml:"id,attr"` 25 | Record []struct { 26 | Name string `xml:"name"` 27 | Xref []struct { 28 | Type string `xml:"type,attr"` 29 | Data string `xml:"data,attr"` 30 | } `xml:"xref"` 31 | Desc struct { 32 | Data string `xml:",innerxml"` 33 | // Any []struct { 34 | // Data string `xml:",chardata"` 35 | // } `xml:",any"` 36 | // Data string `xml:",chardata"` 37 | } `xml:"description,"` 38 | MIB string `xml:"value"` 39 | Alias []string `xml:"alias"` 40 | MIME string `xml:"preferred_alias"` 41 | } `xml:"record"` 42 | } `xml:"registry"` 43 | } 44 | 45 | func main() { 46 | r := gen.OpenIANAFile("assignments/character-sets/character-sets.xml") 47 | reg := ®istry{} 48 | if err := xml.NewDecoder(r).Decode(®); err != nil && err != io.EOF { 49 | log.Fatalf("Error decoding charset registry: %v", err) 50 | } 51 | if len(reg.Registry) == 0 || reg.Registry[0].ID != "character-sets-1" { 52 | log.Fatalf("Unexpected ID %s", reg.Registry[0].ID) 53 | } 54 | 55 | w := &bytes.Buffer{} 56 | fmt.Fprintf(w, "const (\n") 57 | for _, rec := range reg.Registry[0].Record { 58 | constName := "" 59 | for _, a := range rec.Alias { 60 | if strings.HasPrefix(a, "cs") && strings.IndexByte(a, '-') == -1 { 61 | // Some of the constant definitions have comments in them. Strip those. 62 | constName = strings.Title(strings.SplitN(a[2:], "\n", 2)[0]) 63 | } 64 | } 65 | if constName == "" { 66 | switch rec.MIB { 67 | case "2085": 68 | constName = "HZGB2312" // Not listed as alias for some reason. 69 | default: 70 | log.Fatalf("No cs alias defined for %s.", rec.MIB) 71 | } 72 | } 73 | if rec.MIME != "" { 74 | rec.MIME = fmt.Sprintf(" (MIME: %s)", rec.MIME) 75 | } 76 | fmt.Fprintf(w, "// %s is the MIB identifier with IANA name %s%s.\n//\n", constName, rec.Name, rec.MIME) 77 | if len(rec.Desc.Data) > 0 { 78 | fmt.Fprint(w, "// ") 79 | d := xml.NewDecoder(strings.NewReader(rec.Desc.Data)) 80 | inElem := true 81 | attr := "" 82 | for { 83 | t, err := d.Token() 84 | if err != nil { 85 | if err != io.EOF { 86 | log.Fatal(err) 87 | } 88 | break 89 | } 90 | switch x := t.(type) { 91 | case xml.CharData: 92 | attr = "" // Don't need attribute info. 93 | a := bytes.Split([]byte(x), []byte("\n")) 94 | for i, b := range a { 95 | if b = bytes.TrimSpace(b); len(b) != 0 { 96 | if !inElem && i > 0 { 97 | fmt.Fprint(w, "\n// ") 98 | } 99 | inElem = false 100 | fmt.Fprintf(w, "%s ", string(b)) 101 | } 102 | } 103 | case xml.StartElement: 104 | if x.Name.Local == "xref" { 105 | inElem = true 106 | use := false 107 | for _, a := range x.Attr { 108 | if a.Name.Local == "type" { 109 | use = use || a.Value != "person" 110 | } 111 | if a.Name.Local == "data" && use { 112 | attr = a.Value + " " 113 | } 114 | } 115 | } 116 | case xml.EndElement: 117 | inElem = false 118 | fmt.Fprint(w, attr) 119 | } 120 | } 121 | fmt.Fprint(w, "\n") 122 | } 123 | for _, x := range rec.Xref { 124 | switch x.Type { 125 | case "rfc": 126 | fmt.Fprintf(w, "// Reference: %s\n", strings.ToUpper(x.Data)) 127 | case "uri": 128 | fmt.Fprintf(w, "// Reference: %s\n", x.Data) 129 | } 130 | } 131 | fmt.Fprintf(w, "%s MIB = %s\n", constName, rec.MIB) 132 | fmt.Fprintln(w) 133 | } 134 | fmt.Fprintln(w, ")") 135 | 136 | gen.WriteGoFile("mib.go", "identifier", w.Bytes()) 137 | } 138 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/pdufield/types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdufield 6 | 7 | import ( 8 | "io" 9 | "strconv" 10 | ) 11 | 12 | // Name is the name of a PDU field. 13 | type Name string 14 | 15 | // Supported PDU field names. 16 | const ( 17 | AddrNPI Name = "addr_npi" 18 | AddrTON Name = "addr_ton" 19 | AddressRange Name = "address_range" 20 | DataCoding Name = "data_coding" 21 | DestAddrNPI Name = "dest_addr_npi" 22 | DestAddrTON Name = "dest_addr_ton" 23 | DestinationAddr Name = "destination_addr" 24 | ESMClass Name = "esm_class" 25 | ErrorCode Name = "error_code" 26 | FinalDate Name = "final_date" 27 | InterfaceVersion Name = "interface_version" 28 | MessageID Name = "message_id" 29 | MessageState Name = "message_state" 30 | Password Name = "password" 31 | PriorityFlag Name = "priority_flag" 32 | ProtocolID Name = "protocol_id" 33 | RegisteredDelivery Name = "registered_delivery" 34 | ReplaceIfPresentFlag Name = "replace_if_present_flag" 35 | SMDefaultMsgID Name = "sm_default_msg_id" 36 | SMLength Name = "sm_length" 37 | ScheduleDeliveryTime Name = "schedule_delivery_time" 38 | ServiceType Name = "service_type" 39 | ShortMessage Name = "short_message" 40 | SourceAddr Name = "source_addr" 41 | SourceAddrNPI Name = "source_addr_npi" 42 | SourceAddrTON Name = "source_addr_ton" 43 | SystemID Name = "system_id" 44 | SystemType Name = "system_type" 45 | ValidityPeriod Name = "validity_period" 46 | ) 47 | 48 | // Fixed is a PDU of fixed length. 49 | type Fixed struct { 50 | Data uint8 51 | } 52 | 53 | // Len implements the Data interface. 54 | func (f *Fixed) Len() int { 55 | return 1 56 | } 57 | 58 | // Raw implements the Data interface. 59 | func (f *Fixed) Raw() interface{} { 60 | return f.Data 61 | } 62 | 63 | // String implements the Data interface. 64 | func (f *Fixed) String() string { 65 | return strconv.Itoa(int(f.Data)) 66 | } 67 | 68 | // Bytes implements the Data interface. 69 | func (f *Fixed) Bytes() []byte { 70 | return []byte{f.Data} 71 | } 72 | 73 | // SerializeTo implements the Data interface. 74 | func (f *Fixed) SerializeTo(w io.Writer) error { 75 | _, err := w.Write(f.Bytes()) 76 | return err 77 | } 78 | 79 | // Variable is a PDU field of variable length. 80 | type Variable struct { 81 | Data []byte 82 | } 83 | 84 | // Len implements the Data interface. 85 | func (v *Variable) Len() int { 86 | return len(v.Bytes()) 87 | } 88 | 89 | // Raw implements the Data interface. 90 | func (v *Variable) Raw() interface{} { 91 | return v.Data 92 | } 93 | 94 | // String implements the Data interface. 95 | func (v *Variable) String() string { 96 | if l := len(v.Data); l > 0 && v.Data[l-1] == 0x00 { 97 | return string(v.Data[:l-1]) 98 | } 99 | return string(v.Data) 100 | } 101 | 102 | // Bytes implements the Data interface. 103 | func (v *Variable) Bytes() []byte { 104 | if len(v.Data) > 0 && v.Data[len(v.Data)-1] == 0x00 { 105 | return v.Data 106 | } 107 | return append(v.Data, 0x00) 108 | } 109 | 110 | // SerializeTo implements the Data interface. 111 | func (v *Variable) SerializeTo(w io.Writer) error { 112 | _, err := w.Write(v.Bytes()) 113 | return err 114 | } 115 | 116 | // SM is a PDU field used for Short Messages. 117 | type SM struct { 118 | Data []byte 119 | } 120 | 121 | // Len implements the Data interface. 122 | func (sm *SM) Len() int { 123 | return len(sm.Data) 124 | } 125 | 126 | // Raw implements the Data interface. 127 | func (sm *SM) Raw() interface{} { 128 | return sm.Data 129 | } 130 | 131 | // String implements the Data interface. 132 | func (sm *SM) String() string { 133 | return string(sm.Data) 134 | } 135 | 136 | // Bytes implements the Data interface. 137 | func (sm *SM) Bytes() []byte { 138 | return sm.Data 139 | } 140 | 141 | // SerializeTo implements the Data interface. 142 | func (sm *SM) SerializeTo(w io.Writer) error { 143 | _, err := w.Write(sm.Bytes()) 144 | return err 145 | } 146 | -------------------------------------------------------------------------------- /apiserver/rpc_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 sms-api-server authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package apiserver 6 | 7 | import ( 8 | "net/rpc" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/fiorix/go-smpp/smpp" 13 | ) 14 | 15 | func TestSM_Submit_BadRequest(t *testing.T) { 16 | tx := newTransceiver() 17 | defer tx.Close() 18 | <-tx.Bind() 19 | sm := NewSM(tx, rpc.NewServer()) 20 | var resp ShortMessageResp 21 | err := sm.Submit(&ShortMessage{}, &resp) 22 | if err != nil { 23 | if !strings.HasPrefix(err.Error(), "400 Bad Request") { 24 | t.Fatal(err) 25 | } 26 | return 27 | } 28 | t.Fatal("submit with no params is not supposed to work") 29 | } 30 | 31 | func TestSM_Submit_BadGateway(t *testing.T) { 32 | tx := newTransceiver() 33 | defer tx.Close() 34 | <-tx.Bind() 35 | sm := NewSM(tx, rpc.NewServer()) 36 | req := &ShortMessage{ 37 | Src: "root", // causes failure 38 | Dst: "root", 39 | Text: "gotcha", 40 | } 41 | var resp ShortMessageResp 42 | err := sm.Submit(req, &resp) 43 | if err != nil { 44 | if !strings.HasPrefix(err.Error(), "502 Bad Gateway") { 45 | t.Fatal(err) 46 | } 47 | return 48 | } 49 | t.Fatal("submit with bad params is not supposed to work") 50 | } 51 | 52 | func TestSM_Submit_ServiceUnavailable(t *testing.T) { 53 | tx := smpp.Transceiver{Addr: ":0"} 54 | defer tx.Close() 55 | <-tx.Bind() 56 | sm := NewSM(&tx, rpc.NewServer()) 57 | req := &ShortMessage{ 58 | Dst: "root", 59 | Text: "gotcha", 60 | } 61 | var resp ShortMessageResp 62 | err := sm.Submit(req, &resp) 63 | if err != nil { 64 | if !strings.HasPrefix(err.Error(), "503 Service Unavailable") { 65 | t.Fatal(err) 66 | } 67 | return 68 | } 69 | t.Fatal("submit with no server is not supposed to work") 70 | } 71 | 72 | func TestSM_Submit_EncParams(t *testing.T) { 73 | tx := newTransceiver() 74 | defer tx.Close() 75 | <-tx.Bind() 76 | sm := NewSM(tx, rpc.NewServer()) 77 | for _, enc := range []string{"latin1", "ucs2", "fail-me"} { 78 | req := &ShortMessage{ 79 | Dst: "root", 80 | Text: "gotcha", 81 | Enc: enc, 82 | } 83 | var resp ShortMessageResp 84 | err := sm.Submit(req, &resp) 85 | if err != nil && enc != "fail-me" { 86 | t.Fatal(err) 87 | } 88 | } 89 | } 90 | 91 | func TestSM_Submit_RegisterParam(t *testing.T) { 92 | tx := newTransceiver() 93 | defer tx.Close() 94 | <-tx.Bind() 95 | sm := NewSM(tx, rpc.NewServer()) 96 | for _, reg := range []string{"final", "failure", "fail-me"} { 97 | req := &ShortMessage{ 98 | Dst: "root", 99 | Text: "gotcha", 100 | Register: reg, 101 | } 102 | var resp ShortMessageResp 103 | err := sm.Submit(req, &resp) 104 | if err != nil && reg != "fail-me" { 105 | t.Fatal(err) 106 | } 107 | } 108 | } 109 | 110 | func TestSM_Query_BadRequest(t *testing.T) { 111 | tx := newTransceiver() 112 | defer tx.Close() 113 | <-tx.Bind() 114 | sm := NewSM(tx, rpc.NewServer()) 115 | var resp QueryMessageResp 116 | err := sm.Query(&QueryMessage{}, &resp) 117 | if err != nil { 118 | if !strings.HasPrefix(err.Error(), "400 Bad Request") { 119 | t.Fatal(err) 120 | } 121 | return 122 | } 123 | t.Fatal("submit with no params is not supposed to work") 124 | } 125 | 126 | func TestSM_Query_BadGateway(t *testing.T) { 127 | tx := newTransceiver() 128 | defer tx.Close() 129 | <-tx.Bind() 130 | sm := NewSM(tx, rpc.NewServer()) 131 | req := &QueryMessage{ 132 | Src: "root", // causes failure 133 | MessageID: "13", 134 | } 135 | var resp QueryMessageResp 136 | err := sm.Query(req, &resp) 137 | if err != nil { 138 | if !strings.HasPrefix(err.Error(), "502 Bad Gateway") { 139 | t.Fatal(err) 140 | } 141 | return 142 | } 143 | t.Fatal("query with bad params is not supposed to work") 144 | } 145 | 146 | func TestSM_Query_ServiceUnavailable(t *testing.T) { 147 | tx := smpp.Transceiver{Addr: ":0"} 148 | defer tx.Close() 149 | <-tx.Bind() 150 | sm := NewSM(&tx, rpc.NewServer()) 151 | req := &QueryMessage{ 152 | MessageID: "13", 153 | } 154 | var resp QueryMessageResp 155 | err := sm.Query(req, &resp) 156 | if err != nil { 157 | if !strings.HasPrefix(err.Error(), "503 Service Unavailable") { 158 | t.Fatal(err) 159 | } 160 | return 161 | } 162 | t.Fatal("query with no server is not supposed to work") 163 | } 164 | -------------------------------------------------------------------------------- /apiserver/rpc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 sms-api-server authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package apiserver 6 | 7 | import ( 8 | "fmt" 9 | "net/http" 10 | "net/rpc" 11 | "net/url" 12 | 13 | "github.com/fiorix/go-smpp/smpp" 14 | "github.com/fiorix/go-smpp/smpp/pdu/pdutext" 15 | ) 16 | 17 | // SM export its public methods to JSON RPC. 18 | type SM struct { 19 | tx *smpp.Transceiver 20 | rpc *rpc.Server 21 | } 22 | 23 | // NewSM creates and initializes a new SM, registering its own 24 | // methods onto the given rpc server. 25 | func NewSM(tx *smpp.Transceiver, rs *rpc.Server) *SM { 26 | sm := &SM{ 27 | tx: tx, 28 | rpc: rs, 29 | } 30 | sm.rpc.Register(sm) // hax more 31 | return sm 32 | } 33 | 34 | // ShortMessage contains the arguments of RPC call to SM.Submit. 35 | type ShortMessage struct { 36 | Src string `json:"src"` 37 | Dst string `json:"dst"` 38 | Text string `json:"text"` 39 | Enc string `json:"enc"` 40 | Register string `json:"register"` 41 | } 42 | 43 | // ShortMessageResp contains of RPC response from SM.Submit. 44 | type ShortMessageResp struct { 45 | MessageID string `json:"message_id"` 46 | } 47 | 48 | // Submit sends a short message via RPC. 49 | func (rpc *SM) Submit(args *ShortMessage, resp *ShortMessageResp) error { 50 | req := url.Values{ 51 | "src": {args.Src}, 52 | "dst": {args.Dst}, 53 | "text": {args.Text}, 54 | "enc": {args.Enc}, 55 | "register": {args.Register}, 56 | } 57 | r, s, err := rpc.submit(req) 58 | if err != nil { 59 | return fmt.Errorf("%d %s: %v", s, http.StatusText(s), err) 60 | } 61 | *resp = *r 62 | return nil 63 | } 64 | 65 | func (rpc *SM) submit(req url.Values) (resp *ShortMessageResp, status int, err error) { 66 | sm := &smpp.ShortMessage{} 67 | var msg, enc, register string 68 | f := form{ 69 | {"src", "number of sender", false, nil, &sm.Src}, 70 | {"dst", "number of recipient", true, nil, &sm.Dst}, 71 | {"text", "text message", true, nil, &msg}, 72 | {"enc", "text encoding", false, []string{"latin1", "ucs2"}, &enc}, 73 | {"register", "registered delivery", false, []string{"final", "failure"}, ®ister}, 74 | } 75 | if err := f.Validate(req); err != nil { 76 | return nil, http.StatusBadRequest, err 77 | } 78 | switch enc { 79 | case "": 80 | sm.Text = pdutext.Raw(msg) 81 | case "latin1", "latin-1": 82 | sm.Text = pdutext.Latin1(msg) 83 | case "ucs2", "ucs-2": 84 | sm.Text = pdutext.UCS2(msg) 85 | } 86 | switch register { 87 | case "final": 88 | sm.Register = smpp.FinalDeliveryReceipt 89 | case "failure": 90 | sm.Register = smpp.FailureDeliveryReceipt 91 | } 92 | sm, err = rpc.tx.Submit(sm) 93 | if err == smpp.ErrNotConnected { 94 | return nil, http.StatusServiceUnavailable, err 95 | } 96 | if err != nil { 97 | return nil, http.StatusBadGateway, err 98 | } 99 | resp = &ShortMessageResp{MessageID: sm.RespID()} 100 | return resp, http.StatusOK, nil 101 | } 102 | 103 | // QueryMessage contains the arguments of RPC call to SM.Query. 104 | type QueryMessage struct { 105 | Src string `json:"src"` 106 | MessageID string `json:"message_id"` 107 | } 108 | 109 | // QueryMessageResp contains RPC response from SM.Query. 110 | type QueryMessageResp struct { 111 | MsgState string `json:"message_state"` 112 | FinalDate string `json:"final_date"` 113 | ErrCode uint8 `json:"error_code"` 114 | } 115 | 116 | // Query queries the status of a short message via RPC. 117 | func (rpc *SM) Query(args *QueryMessage, resp *QueryMessageResp) error { 118 | req := url.Values{ 119 | "src": {args.Src}, 120 | "message_id": {args.MessageID}, 121 | } 122 | r, s, err := rpc.query(req) 123 | if err != nil { 124 | return fmt.Errorf("%d %s: %v", s, http.StatusText(s), err) 125 | } 126 | *resp = *r 127 | return nil 128 | } 129 | 130 | func (rpc *SM) query(req url.Values) (resp *QueryMessageResp, status int, err error) { 131 | f := form{ 132 | {"src", "number of sender", false, nil, nil}, 133 | {"message_id", "message id from send", true, nil, nil}, 134 | } 135 | if err := f.Validate(req); err != nil { 136 | return nil, http.StatusBadRequest, err 137 | } 138 | qr, err := rpc.tx.QuerySM(req.Get("src"), req.Get("message_id")) 139 | if err == smpp.ErrNotConnected { 140 | return nil, http.StatusServiceUnavailable, err 141 | } 142 | if err != nil { 143 | return nil, http.StatusBadGateway, err 144 | } 145 | resp = &QueryMessageResp{ 146 | MsgState: qr.MsgState, 147 | FinalDate: qr.FinalDate, 148 | ErrCode: qr.ErrCode, 149 | } 150 | return resp, http.StatusOK, nil 151 | } 152 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 sms-api-server authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // HTTP API for sending SMS via SMPP. 6 | package main 7 | 8 | import ( 9 | "crypto/tls" 10 | "crypto/x509" 11 | "flag" 12 | "fmt" 13 | "io/ioutil" 14 | "log" 15 | "net" 16 | "net/http" 17 | "os" 18 | "os/signal" 19 | 20 | _ "net/http/pprof" 21 | 22 | "github.com/fiorix/go-smpp/smpp" 23 | "github.com/go-web/httplog" 24 | 25 | "github.com/fiorix/sms-api-server/apiserver" 26 | ) 27 | 28 | // Version of this server. 29 | var Version = "v1.2.2" 30 | 31 | type Opts struct { 32 | ListenAddr string 33 | APIPrefix string 34 | PublicDir string 35 | Log bool 36 | LogTS bool 37 | CAFile string 38 | CertFile string 39 | KeyFile string 40 | SMPPAddr string 41 | ClientTLS bool 42 | ClientTLSInsecure bool 43 | ShowVersion bool 44 | } 45 | 46 | func main() { 47 | o := ParseOpts() 48 | if o.ShowVersion { 49 | fmt.Println("sms-api-server", Version) 50 | os.Exit(0) 51 | } 52 | tx := &smpp.Transceiver{ 53 | Addr: o.SMPPAddr, 54 | User: os.Getenv("SMPP_USER"), 55 | Passwd: os.Getenv("SMPP_PASSWD"), 56 | } 57 | exit := make(chan os.Signal, 1) 58 | signal.Notify(exit, os.Interrupt, os.Kill) 59 | go func() { 60 | <-exit 61 | tx.Close() 62 | os.Exit(0) 63 | }() 64 | if o.ClientTLS { 65 | host, _, _ := net.SplitHostPort(tx.Addr) 66 | tx.TLS = &tls.Config{ 67 | ServerName: host, 68 | } 69 | if o.ClientTLSInsecure { 70 | tx.TLS.InsecureSkipVerify = true 71 | } 72 | } 73 | api := &apiserver.Handler{Prefix: o.APIPrefix, Tx: tx} 74 | conn := api.Register(http.DefaultServeMux) 75 | go func() { 76 | for c := range conn { 77 | m := fmt.Sprintf("SMPP connection status to %s: %s", 78 | o.SMPPAddr, c.Status()) 79 | if err := c.Error(); err != nil { 80 | m = fmt.Sprintf("%s (%v)", m, err) 81 | } 82 | log.Println(m) 83 | } 84 | }() 85 | if o.PublicDir != "" { 86 | fs := http.FileServer(http.Dir(o.PublicDir)) 87 | http.Handle("/", http.StripPrefix(o.APIPrefix, fs)) 88 | } 89 | mux := http.Handler(http.DefaultServeMux) 90 | if o.Log { 91 | var l *log.Logger 92 | if o.LogTS { 93 | l = log.New(os.Stderr, "", log.LstdFlags) 94 | } else { 95 | l = log.New(os.Stderr, "", 0) 96 | } 97 | mux = httplog.ApacheCombinedFormat(l)(mux.ServeHTTP) 98 | } 99 | err := ListenAndServe(o, mux) 100 | if err != nil { 101 | log.Fatal(err) 102 | } 103 | } 104 | 105 | func ParseOpts() *Opts { 106 | o := &Opts{ListenAddr: ":8080", SMPPAddr: "localhost:2775", LogTS: true} 107 | flag.StringVar(&o.ListenAddr, "http", o.ListenAddr, "host:port to listen on for http or https") 108 | flag.StringVar(&o.APIPrefix, "prefix", o.APIPrefix, "prefix for http(s) endpoints") 109 | flag.StringVar(&o.PublicDir, "public", o.PublicDir, "public dir to serve under \"/\", optional") 110 | flag.BoolVar(&o.Log, "log", o.Log, "log http requests") 111 | flag.BoolVar(&o.LogTS, "log-timestamp", o.LogTS, "add timestamp to logs") 112 | flag.StringVar(&o.CAFile, "ca", o.CAFile, "x509 CA certificate file (for client auth)") 113 | flag.StringVar(&o.CertFile, "cert", o.CertFile, "x509 certificate file for https server") 114 | flag.StringVar(&o.KeyFile, "key", o.KeyFile, "x509 key file for https server") 115 | flag.StringVar(&o.SMPPAddr, "smpp", o.SMPPAddr, "host:port of the SMSC to connect to via SMPP v3.4") 116 | flag.BoolVar(&o.ClientTLS, "tls", o.ClientTLS, "connect to SMSC using TLS") 117 | flag.BoolVar(&o.ClientTLSInsecure, "precaire", o.ClientTLSInsecure, "disable TLS checks for client connection") 118 | flag.BoolVar(&o.ShowVersion, "version", o.ShowVersion, "show version and exit") 119 | flag.Usage = func() { 120 | fmt.Printf("Usage: [env] %s [options]\n", os.Args[0]) 121 | fmt.Printf("Environment variables:\n") 122 | fmt.Printf(" SMPP_USER: username for smpp client connection\n") 123 | fmt.Printf(" SMPP_PASSWD: password for smpp client connection\n") 124 | fmt.Printf("Options:\n") 125 | flag.PrintDefaults() 126 | } 127 | flag.Parse() 128 | return o 129 | } 130 | 131 | func ListenAndServe(o *Opts, f http.Handler) error { 132 | s := &http.Server{Addr: o.ListenAddr, Handler: f} 133 | if o.CertFile == "" || o.KeyFile == "" { 134 | return s.ListenAndServe() 135 | } 136 | if o.CAFile != "" { 137 | b, err := ioutil.ReadFile(o.CAFile) 138 | if err != nil { 139 | return err 140 | } 141 | cp := x509.NewCertPool() 142 | cp.AppendCertsFromPEM(b) 143 | s.TLSConfig = &tls.Config{ 144 | ClientCAs: cp, 145 | ClientAuth: tls.RequireAndVerifyClientCert, 146 | } 147 | } 148 | return s.ListenAndServeTLS(o.CertFile, o.KeyFile) 149 | } 150 | -------------------------------------------------------------------------------- /vendor/github.com/go-web/httpmux/httpmux_test.go: -------------------------------------------------------------------------------- 1 | package httpmux 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "net/url" 10 | "testing" 11 | 12 | "golang.org/x/net/context" 13 | ) 14 | 15 | func TestHandler(t *testing.T) { 16 | mux := New() 17 | f := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 18 | w.WriteHeader(http.StatusOK) 19 | }) 20 | mux.DELETE("/", f) 21 | mux.GET("/", f) 22 | mux.HEAD("/", f) 23 | mux.OPTIONS("/", f) 24 | mux.PATCH("/:arg", f) 25 | mux.POST("/:arg", f) 26 | mux.PUT("/:arg", f) 27 | for i, method := range []string{ 28 | "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", 29 | } { 30 | r := &http.Request{ 31 | Method: method, 32 | URL: &url.URL{Path: "/"}, 33 | } 34 | switch method { 35 | case "PATCH", "POST", "PUT": 36 | r.Body = ioutil.NopCloser(bytes.NewBuffer([]byte{1})) 37 | r.URL.Path += "arg" 38 | } 39 | w := &httptest.ResponseRecorder{} 40 | mux.ServeHTTP(w, r) 41 | if w.Code != http.StatusOK { 42 | t.Errorf("Test %d: unexpected status code. Want %d, have %d", 43 | i, http.StatusOK, w.Code) 44 | } 45 | } 46 | } 47 | 48 | func TestSubtree(t *testing.T) { 49 | root := New() 50 | root.UseFunc(func(next http.HandlerFunc) http.HandlerFunc { 51 | return func(w http.ResponseWriter, r *http.Request) { 52 | w.Header().Set("X-Hello", "world") 53 | next(w, r) 54 | } 55 | }) 56 | c := DefaultConfig 57 | c.Prefix = "/ignore-me" 58 | c.UseFunc(func(next http.HandlerFunc) http.HandlerFunc { 59 | return func(w http.ResponseWriter, r *http.Request) { 60 | h := w.Header() 61 | h.Set("X-Hello", h.Get("X-Hello")+"z") 62 | next(w, r) 63 | } 64 | }) 65 | tree := NewHandler(&c) 66 | tree.GET("/foobar", func(w http.ResponseWriter, r *http.Request) { 67 | if w.Header().Get("X-Hello") == "worldz" { 68 | w.WriteHeader(http.StatusOK) 69 | return 70 | } 71 | http.NotFound(w, r) 72 | }) 73 | root.Append("/test", tree) 74 | r := &http.Request{ 75 | Method: "GET", 76 | URL: &url.URL{Path: "/test/foobar"}, 77 | } 78 | w := &httptest.ResponseRecorder{} 79 | root.ServeHTTP(w, r) 80 | if w.Code != http.StatusOK { 81 | t.Errorf("Unexpected status code. Want %d, have %d", 82 | http.StatusOK, w.Code) 83 | } 84 | 85 | } 86 | 87 | func testmw(want int) Middleware { 88 | return func(next http.Handler) http.Handler { 89 | f := func(w http.ResponseWriter, r *http.Request) { 90 | p := Params(r).ByName("opt") 91 | if p != "foobar" { 92 | http.Error(w, "missing parameter: foobar", 93 | http.StatusNotFound) 94 | return 95 | } 96 | ctx := Context(r) 97 | have, _ := ctx.Value("v").(int) 98 | if want != have { 99 | m := fmt.Sprintf("want=%d have=%d", want, have) 100 | http.Error(w, m, http.StatusNotFound) 101 | return 102 | } 103 | have++ 104 | ctx = context.WithValue(ctx, "v", have) 105 | SetContext(ctx, r) 106 | next.ServeHTTP(w, r) 107 | } 108 | return http.HandlerFunc(f) 109 | } 110 | } 111 | 112 | func TestMiddleware(t *testing.T) { 113 | root := New() 114 | root.Use(testmw(0)) 115 | root.Use(testmw(1)) 116 | f := func(w http.ResponseWriter, r *http.Request) { 117 | w.WriteHeader(http.StatusOK) 118 | } 119 | root.GET("/:opt", http.HandlerFunc(f)) 120 | r := &http.Request{ 121 | Method: "GET", 122 | URL: &url.URL{Path: "/foobar"}, 123 | } 124 | w := &httptest.ResponseRecorder{Body: &bytes.Buffer{}} 125 | root.ServeHTTP(w, r) 126 | if w.Code != http.StatusOK { 127 | t.Fatalf("Middleware chain is broken: %s", w.Body.Bytes()) 128 | } 129 | } 130 | 131 | func TestMiddlewareSubtree(t *testing.T) { 132 | root := New() 133 | root.Use(testmw(0)) 134 | root.Use(testmw(1)) 135 | subtree := New() 136 | subtree.Use(testmw(2)) 137 | f := func(w http.ResponseWriter, r *http.Request) { 138 | w.WriteHeader(http.StatusOK) 139 | } 140 | subtree.GET("/:opt", http.HandlerFunc(f)) 141 | root.Append("/a", subtree) 142 | r := &http.Request{ 143 | Method: "GET", 144 | URL: &url.URL{Path: "/a/foobar"}, 145 | } 146 | w := &httptest.ResponseRecorder{Body: &bytes.Buffer{}} 147 | root.ServeHTTP(w, r) 148 | if w.Code != http.StatusOK { 149 | t.Fatalf("Middleware chain is broken: %s", w.Body.Bytes()) 150 | } 151 | } 152 | 153 | func TestServeFiles(t *testing.T) { 154 | i := 0 155 | p := map[string]struct{ Dir, URL string }{ 156 | "/*filepath": {".", "/httpmux.go"}, 157 | "/foobar/*filepath": {".", "/foobar/httpmux.go"}, 158 | } 159 | for pattern, cfg := range p { 160 | mux := New() 161 | mux.ServeFiles(pattern, http.Dir(cfg.Dir)) 162 | w := &httptest.ResponseRecorder{} 163 | r := &http.Request{ 164 | Method: "GET", 165 | URL: &url.URL{Path: cfg.URL}, 166 | } 167 | mux.ServeHTTP(w, r) 168 | if w.Code != http.StatusOK { 169 | t.Errorf("Test %d: Unexpected status. Want %d, have %d", 170 | i, http.StatusOK, w.Code) 171 | } 172 | i++ 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/smpptest/server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package smpptest 6 | 7 | import ( 8 | "crypto/tls" 9 | "errors" 10 | "fmt" 11 | "io" 12 | "log" 13 | "net" 14 | "sync" 15 | 16 | "github.com/fiorix/go-smpp/smpp/pdu" 17 | "github.com/fiorix/go-smpp/smpp/pdu/pdufield" 18 | ) 19 | 20 | // Default settings. 21 | var ( 22 | DefaultUser = "client" 23 | DefaultPasswd = "secret" 24 | DefaultSystemID = "smpptest" 25 | ) 26 | 27 | // HandlerFunc is the signature of a function passed to Server instances, 28 | // that is called when client PDU messages arrive. 29 | type HandlerFunc func(c Conn, m pdu.Body) 30 | 31 | // Server is an SMPP server for testing purposes. By default it authenticate 32 | // clients with the configured credentials, and echoes any other PDUs 33 | // back to the client. 34 | type Server struct { 35 | User string 36 | Passwd string 37 | TLS *tls.Config 38 | Handler HandlerFunc 39 | 40 | mu sync.Mutex 41 | l net.Listener 42 | } 43 | 44 | // NewServer creates and initializes a new Server. Callers are supposed 45 | // to call Close on that server later. 46 | func NewServer() *Server { 47 | s := NewUnstartedServer() 48 | s.Start() 49 | return s 50 | } 51 | 52 | // NewUnstartedServer creates a new Server with default settings, and 53 | // does not start it. Callers are supposed to call Start and Close later. 54 | func NewUnstartedServer() *Server { 55 | return &Server{ 56 | User: DefaultUser, 57 | Passwd: DefaultPasswd, 58 | Handler: EchoHandler, 59 | l: newLocalListener(), 60 | } 61 | } 62 | 63 | func newLocalListener() net.Listener { 64 | l, err := net.Listen("tcp", "127.0.0.1:0") 65 | if err == nil { 66 | return l 67 | } 68 | if l, err = net.Listen("tcp6", "[::1]:0"); err != nil { 69 | panic(fmt.Sprintf("smpptest: failed to listen on a port: %v", err)) 70 | } 71 | return l 72 | } 73 | 74 | // Start starts the server. 75 | func (srv *Server) Start() { 76 | go srv.Serve() 77 | } 78 | 79 | // Addr returns the local address of the server, or an empty string 80 | // if the server hasn't been started yet. 81 | func (srv *Server) Addr() string { 82 | if srv.l == nil { 83 | return "" 84 | } 85 | return srv.l.Addr().String() 86 | } 87 | 88 | // Close stops the server, causing the accept loop to break out. 89 | func (srv *Server) Close() { 90 | if srv.l == nil { 91 | panic("smpptest: server is not started") 92 | } 93 | srv.l.Close() 94 | } 95 | 96 | // Serve accepts new clients and handle them by authenticating the 97 | // first PDU, expected to be a Bind PDU, then echoing all other PDUs. 98 | func (srv *Server) Serve() { 99 | for { 100 | cli, err := srv.l.Accept() 101 | if err != nil { 102 | break // on srv.l.Close 103 | } 104 | go srv.handle(newConn(cli)) 105 | } 106 | } 107 | 108 | // handle new clients. 109 | func (srv *Server) handle(c *conn) { 110 | defer c.Close() 111 | if err := srv.auth(c); err != nil { 112 | if err != io.EOF { 113 | log.Println("smpptest: server auth failed:", err) 114 | } 115 | return 116 | } 117 | for { 118 | pdu, err := c.Read() 119 | if err != nil { 120 | if err != io.EOF { 121 | log.Println("smpptest: read failed:", err) 122 | } 123 | break 124 | } 125 | srv.Handler(c, pdu) 126 | } 127 | } 128 | 129 | // auth authenticate new clients. 130 | func (srv *Server) auth(c *conn) error { 131 | p, err := c.Read() 132 | if err != nil { 133 | return err 134 | } 135 | var resp pdu.Body 136 | switch p.Header().ID { 137 | case pdu.BindTransmitterID: 138 | resp = pdu.NewBindTransmitterResp() 139 | case pdu.BindReceiverID: 140 | resp = pdu.NewBindReceiverResp() 141 | case pdu.BindTransceiverID: 142 | resp = pdu.NewBindTransceiverResp() 143 | default: 144 | return errors.New("unexpected pdu, want bind") 145 | } 146 | f := p.Fields() 147 | user := f[pdufield.SystemID] 148 | passwd := f[pdufield.Password] 149 | if user == nil || passwd == nil { 150 | return errors.New("malformed pdu, missing system_id/password") 151 | } 152 | if user.String() != srv.User { 153 | return errors.New("invalid user") 154 | } 155 | if passwd.String() != srv.Passwd { 156 | return errors.New("invalid passwd") 157 | } 158 | resp.Fields().Set(pdufield.SystemID, DefaultSystemID) 159 | if err = c.Write(resp); err != nil { 160 | return err 161 | } 162 | return nil 163 | } 164 | 165 | // EchoHandler is the default Server HandlerFunc, and echoes back 166 | // any PDUs received. 167 | func EchoHandler(cli Conn, m pdu.Body) { 168 | // log.Printf("smpptest: echo PDU from %s: %#v", cli.RemoteAddr(), m) 169 | // 170 | // Real servers will reply with at least the same sequence number 171 | // from the request: 172 | // resp := pdu.NewSubmitSMResp() 173 | // resp.Header().Seq = m.Header().Seq 174 | // resp.Fields().Set(pdufield.MessageID, "1234") 175 | // cli.Write(resp) 176 | // 177 | // We just echo m back: 178 | cli.Write(m) 179 | } 180 | -------------------------------------------------------------------------------- /apiserver/http.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 sms-api-server authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package apiserver 6 | 7 | import ( 8 | "encoding/json" 9 | "fmt" 10 | "io" 11 | "net/http" 12 | "net/rpc" 13 | "net/rpc/jsonrpc" 14 | "path/filepath" 15 | "strings" 16 | 17 | "github.com/fiorix/go-smpp/smpp" 18 | "golang.org/x/net/websocket" 19 | ) 20 | 21 | // Handler is an HTTP handler that provides the endpoints of this service. 22 | // It registers itself onto an existing ServeMux via Register. 23 | type Handler struct { 24 | http.Handler 25 | 26 | // Prefix of all endpoints served by the handler. 27 | // Defaults to "/" if not set. 28 | Prefix string 29 | 30 | // VersionTag that follows the prefix. 31 | // Defaults to "v1" if not set. 32 | VersionTag string 33 | 34 | // SMPP Transceiver for sending and receiving SMS. 35 | // Register will update its Handler and Bind it. 36 | Tx *smpp.Transceiver 37 | 38 | // clients registered for receipt 39 | pool *deliveryPool 40 | 41 | // sm for smpp functionality 42 | sm *SM 43 | } 44 | 45 | func (h *Handler) init() { 46 | // TODO: handle nil h.Tx 47 | h.pool = newPool() 48 | h.sm = NewSM(h.Tx, rpc.NewServer()) 49 | h.Tx.Handler = h.pool.Handler 50 | } 51 | 52 | // Register add the endpoints of this service to the given ServeMux, 53 | // and binds Tx. Returns the ConnStatus channel from Tx.Bind. 54 | // 55 | // Must be called once, before the server is started. 56 | func (h *Handler) Register(mux *http.ServeMux) <-chan smpp.ConnStatus { 57 | h.init() 58 | p := urlprefix(h) 59 | mux.Handle(p+"/send", h.send()) 60 | mux.Handle(p+"/query", h.query()) 61 | mux.Handle(p+"/sse", h.sse()) 62 | mux.Handle(p+"/ws/jsonrpc", h.wsrpc()) 63 | mux.Handle(p+"/ws/jsonrpc/events", h.wsrpcEvents()) 64 | h.Handler = mux 65 | return h.Tx.Bind() 66 | } 67 | 68 | func urlprefix(h *Handler) string { 69 | path := "/" + h.Prefix + "/" 70 | if h.VersionTag == "" { 71 | path += "v1" 72 | } else { 73 | path += h.VersionTag 74 | } 75 | return strings.TrimRight(filepath.Clean(path), "/") 76 | } 77 | 78 | func (h *Handler) send() http.Handler { 79 | f := func(w http.ResponseWriter, r *http.Request) { 80 | r.ParseMultipartForm(1 << 20) 81 | resp, status, err := h.sm.submit(r.Form) 82 | if err != nil { 83 | http.Error(w, err.Error(), status) 84 | return 85 | } 86 | w.Header().Set("Content-Type", "application/json") 87 | json.NewEncoder(w).Encode(resp) 88 | } 89 | return auth(cors(f, "PUT", "POST")) 90 | } 91 | 92 | func (h *Handler) query() http.Handler { 93 | f := func(w http.ResponseWriter, r *http.Request) { 94 | r.ParseForm() 95 | resp, status, err := h.sm.query(r.Form) 96 | if err != nil { 97 | http.Error(w, err.Error(), status) 98 | return 99 | } 100 | w.Header().Set("Content-Type", "application/json") 101 | json.NewEncoder(w).Encode(resp) 102 | } 103 | return auth(cors(f, "HEAD", "GET")) 104 | } 105 | 106 | func (h *Handler) sse() http.Handler { 107 | f := func(w http.ResponseWriter, r *http.Request) { 108 | n, ok := w.(http.CloseNotifier) 109 | if !ok { 110 | http.Error(w, "Notifier not supported", 111 | http.StatusInternalServerError) 112 | return 113 | } 114 | stop := n.CloseNotify() 115 | conn, ok := w.(http.Flusher) 116 | if !ok { 117 | http.Error(w, "Flusher not supported", 118 | http.StatusInternalServerError) 119 | return 120 | } 121 | w.Header().Set("Content-Type", "text/event-stream") 122 | w.WriteHeader(http.StatusOK) 123 | conn.Flush() 124 | id, dr := h.pool.Register() 125 | defer h.pool.Unregister(id) 126 | j := json.NewEncoder(w) 127 | for { 128 | select { 129 | case r := <-dr: 130 | fmt.Fprintf(w, "Data: ") 131 | j.Encode(&r) 132 | fmt.Fprintf(w, "\n") 133 | conn.Flush() 134 | case <-stop: 135 | return 136 | } 137 | } 138 | } 139 | return auth(cors(f, "GET")) 140 | } 141 | 142 | // WebSocket handler for JSON RPC exposing functions from the SM type. 143 | func (h *Handler) wsrpc() http.Handler { 144 | f := func(ws *websocket.Conn) { 145 | h.sm.rpc.ServeCodec(jsonrpc.NewServerCodec(ws)) 146 | } 147 | return auth(cors(websocket.Handler(f).ServeHTTP, "GET")) 148 | } 149 | 150 | // WebSocket handler for JSON RPC events, we call the client. 151 | func (h *Handler) wsrpcEvents() http.Handler { 152 | type conn struct { 153 | io.Reader 154 | io.WriteCloser 155 | } 156 | f := func(ws *websocket.Conn) { 157 | id, dr := h.pool.Register() 158 | defer h.pool.Unregister(id) 159 | stop := make(chan struct{}) 160 | r, w := io.Pipe() 161 | defer w.Close() 162 | go func() { 163 | io.Copy(w, ws) 164 | close(stop) 165 | }() 166 | rwc := &conn{Reader: r, WriteCloser: ws} 167 | cli := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(rwc)) 168 | for { 169 | select { 170 | case r := <-dr: 171 | err := cli.Call("SM.Deliver", r, nil) 172 | if err != nil { 173 | return 174 | } 175 | case <-stop: 176 | return 177 | } 178 | } 179 | } 180 | return auth(cors(websocket.Handler(f).ServeHTTP, "GET")) 181 | } 182 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/codec.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdu 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io" 11 | "sync/atomic" 12 | 13 | "github.com/fiorix/go-smpp/smpp/pdu/pdufield" 14 | ) 15 | 16 | var nextSeq uint32 17 | 18 | // codec is the base type of all PDUs. 19 | // It implements the PDU interface and provides a generic encoder. 20 | type codec struct { 21 | h *Header 22 | l pdufield.List 23 | f pdufield.Map 24 | t pdufield.TLVMap 25 | } 26 | 27 | // init initializes the codec's list and maps and sets the header 28 | // sequence number. 29 | func (pdu *codec) init() { 30 | if pdu.l == nil { 31 | pdu.l = pdufield.List{} 32 | } 33 | pdu.f = make(pdufield.Map) 34 | pdu.t = make(pdufield.TLVMap) 35 | pdu.h.Seq = atomic.AddUint32(&nextSeq, 1) 36 | } 37 | 38 | // setup replaces the codec's current maps with the given ones. 39 | func (pdu *codec) setup(f pdufield.Map, t pdufield.TLVMap) { 40 | pdu.f, pdu.t = f, t 41 | } 42 | 43 | // Header implements the PDU interface. 44 | func (pdu *codec) Header() *Header { 45 | return pdu.h 46 | } 47 | 48 | // Len implements the PDU interface. 49 | func (pdu *codec) Len() int { 50 | l := HeaderLen 51 | for _, f := range pdu.f { 52 | l += f.Len() 53 | } 54 | for _, t := range pdu.t { 55 | l += int(t.Len) 56 | } 57 | return l 58 | } 59 | 60 | // FieldList implements the PDU interface. 61 | func (pdu *codec) FieldList() pdufield.List { 62 | return pdu.l 63 | } 64 | 65 | // Fields implement the PDU interface. 66 | func (pdu *codec) Fields() pdufield.Map { 67 | return pdu.f 68 | } 69 | 70 | // TLVFields implement the PDU interface. 71 | func (pdu *codec) TLVFields() pdufield.TLVMap { 72 | return pdu.t 73 | } 74 | 75 | // SerializeTo implements the PDU interface. 76 | func (pdu *codec) SerializeTo(w io.Writer) error { 77 | var b bytes.Buffer 78 | for _, k := range pdu.FieldList() { 79 | f, ok := pdu.f[k] 80 | if !ok { 81 | pdu.f.Set(k, nil) 82 | f = pdu.f[k] 83 | } 84 | if err := f.SerializeTo(&b); err != nil { 85 | return err 86 | } 87 | } 88 | pdu.h.Len = uint32(pdu.Len()) 89 | err := pdu.h.SerializeTo(w) 90 | if err != nil { 91 | return err 92 | } 93 | _, err = io.Copy(w, &b) 94 | return err 95 | } 96 | 97 | // decoder wraps a PDU (e.g. Bind) and the codec together and is 98 | // used for initializing new PDUs with map data decoded off the wire. 99 | type decoder interface { 100 | Body 101 | setup(f pdufield.Map, t pdufield.TLVMap) 102 | } 103 | 104 | func decodeFields(pdu decoder, b []byte) (Body, error) { 105 | l := pdu.FieldList() 106 | r := bytes.NewBuffer(b) 107 | f, err := l.Decode(r) 108 | if err != nil { 109 | return nil, err 110 | } 111 | t, err := l.DecodeTLV(r) 112 | if err != nil { 113 | return nil, err 114 | } 115 | pdu.setup(f, t) 116 | return pdu, nil 117 | } 118 | 119 | // Decode decodes binary PDU data. It returns a new PDU object, e.g. Bind, 120 | // with header and all fields decoded. The returned PDU can be modified 121 | // and re-serialized to its binary form. 122 | func Decode(r io.Reader) (Body, error) { 123 | hdr, err := DecodeHeader(r) 124 | if err != nil { 125 | return nil, err 126 | } 127 | b := make([]byte, hdr.Len-HeaderLen) 128 | _, err = io.ReadFull(r, b) 129 | if err != nil { 130 | return nil, err 131 | } 132 | switch hdr.ID { 133 | case AlertNotificationID: 134 | // TODO(fiorix): Implement AlertNotification. 135 | case BindReceiverID, BindTransceiverID, BindTransmitterID: 136 | return decodeFields(newBind(hdr), b) 137 | case BindReceiverRespID, BindTransceiverRespID, BindTransmitterRespID: 138 | return decodeFields(newBindResp(hdr), b) 139 | case CancelSMID: 140 | // TODO(fiorix): Implement CancelSM. 141 | case CancelSMRespID: 142 | // TODO(fiorix): Implement CancelSMResp. 143 | case DataSMID: 144 | // TODO(fiorix): Implement DataSM. 145 | case DataSMRespID: 146 | // TODO(fiorix): Implement DataSMResp. 147 | case DeliverSMID: 148 | return decodeFields(newDeliverSM(hdr), b) 149 | case DeliverSMRespID: 150 | return decodeFields(newDeliverSMResp(hdr), b) 151 | case EnquireLinkID: 152 | return decodeFields(newEnquireLink(hdr), b) 153 | case EnquireLinkRespID: 154 | return decodeFields(newEnquireLinkResp(hdr), b) 155 | case GenericNACKID: 156 | return decodeFields(newGenericNACK(hdr), b) 157 | case OutbindID: 158 | // TODO(fiorix): Implement Outbind. 159 | case QuerySMID: 160 | return decodeFields(newQuerySM(hdr), b) 161 | case QuerySMRespID: 162 | return decodeFields(newQuerySMResp(hdr), b) 163 | case ReplaceSMID: 164 | // TODO(fiorix): Implement ReplaceSM. 165 | case ReplaceSMRespID: 166 | // TODO(fiorix): Implement ReplaceSMResp. 167 | case SubmitMultiID: 168 | // TODO(fiorix): Implement SubmitMulti. 169 | case SubmitMultiRespID: 170 | // TODO(fiorix): Implement SubmitMultiResp. 171 | case SubmitSMID: 172 | return decodeFields(newSubmitSM(hdr), b) 173 | case SubmitSMRespID: 174 | return decodeFields(newSubmitSMResp(hdr), b) 175 | case UnbindID: 176 | return decodeFields(newUnbind(hdr), b) 177 | case UnbindRespID: 178 | return decodeFields(newUnbindResp(hdr), b) 179 | default: 180 | return nil, fmt.Errorf("unknown PDU type: %#x", hdr.ID) 181 | } 182 | return nil, fmt.Errorf("PDU not implemented: %#x", hdr.ID) 183 | } 184 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/pdu/header.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package pdu 6 | 7 | import ( 8 | "encoding/binary" 9 | "fmt" 10 | "io" 11 | ) 12 | 13 | type ( 14 | // ID of the PDU header. 15 | ID uint32 16 | 17 | // Status is a property of the PDU header. 18 | Status uint32 19 | ) 20 | 21 | var idString = map[ID]string{ 22 | GenericNACKID: "GenericNACK", 23 | BindReceiverID: "BindReceiver", 24 | BindReceiverRespID: "BindReceiverResp", 25 | BindTransmitterID: "BindTransmitter", 26 | BindTransmitterRespID: "BindTransmitterResp", 27 | QuerySMID: "QuerySM", 28 | QuerySMRespID: "QuerySMResp", 29 | SubmitSMID: "SubmitSM", 30 | SubmitSMRespID: "SubmitSMResp", 31 | DeliverSMID: "DeliverSM", 32 | DeliverSMRespID: "DeliverSMResp", 33 | UnbindID: "Unbind", 34 | UnbindRespID: "UnbindResp", 35 | ReplaceSMID: "ReplaceSM", 36 | ReplaceSMRespID: "ReplaceSMResp", 37 | CancelSMID: "CancelSM", 38 | CancelSMRespID: "CancelSMResp", 39 | BindTransceiverID: "BindTransceiver", 40 | BindTransceiverRespID: "BindTransceiverResp", 41 | OutbindID: "Outbind", 42 | EnquireLinkID: "EnquireLink", 43 | EnquireLinkRespID: "EnquireLinkResp", 44 | SubmitMultiID: "SubmitMulti", 45 | SubmitMultiRespID: "SubmitMultiResp", 46 | AlertNotificationID: "AlertNotification", 47 | DataSMID: "DataSM", 48 | DataSMRespID: "DataSMResp", 49 | } 50 | 51 | // String returns the PDU type as a string. 52 | func (id ID) String() string { 53 | return idString[id] 54 | } 55 | 56 | // HeaderLen is the PDU header length. 57 | const HeaderLen = 16 58 | 59 | // Header is a PDU header. 60 | type Header struct { 61 | Len uint32 62 | ID ID 63 | Status Status 64 | Seq uint32 // Sequence number. 65 | } 66 | 67 | // DecodeHeader decodes binary PDU header data. 68 | func DecodeHeader(r io.Reader) (*Header, error) { 69 | b := make([]byte, HeaderLen) 70 | _, err := io.ReadFull(r, b) 71 | if err != nil { 72 | return nil, err 73 | } 74 | l := binary.BigEndian.Uint32(b[0:4]) 75 | if l < HeaderLen { 76 | return nil, fmt.Errorf("PDU too small: %d < %d", l, HeaderLen) 77 | } 78 | if l > MaxSize { 79 | return nil, fmt.Errorf("PDU too large: %d > %d", l, MaxSize) 80 | } 81 | hdr := &Header{ 82 | Len: l, 83 | ID: ID(binary.BigEndian.Uint32(b[4:8])), 84 | Status: Status(binary.BigEndian.Uint32(b[8:12])), 85 | Seq: binary.BigEndian.Uint32(b[12:16]), 86 | } 87 | return hdr, nil 88 | } 89 | 90 | // SerializeTo serializes the Header to its binary form to the given writer. 91 | func (h *Header) SerializeTo(w io.Writer) error { 92 | b := make([]byte, HeaderLen) 93 | binary.BigEndian.PutUint32(b[0:4], h.Len) 94 | binary.BigEndian.PutUint32(b[4:8], uint32(h.ID)) 95 | binary.BigEndian.PutUint32(b[8:12], uint32(h.Status)) 96 | binary.BigEndian.PutUint32(b[12:16], h.Seq) 97 | _, err := w.Write(b) 98 | return err 99 | } 100 | 101 | // Error implements the Error interface. 102 | func (s Status) Error() string { 103 | m, ok := esmeStatus[s] 104 | if !ok { 105 | return fmt.Sprintf("unknown status: %d", s) 106 | } 107 | return m 108 | } 109 | 110 | var esmeStatus = map[Status]string{ 111 | 0x00000000: "OK", 112 | 0x00000001: "invalid message length", 113 | 0x00000002: "invalid command length", 114 | 0x00000003: "invalid command id", 115 | 0x00000004: "incorrect bind status for given command", 116 | 0x00000005: "already in bound state", 117 | 0x00000006: "invalid priority flag", 118 | 0x00000007: "invalid registered delivery flag", 119 | 0x00000008: "system error", 120 | 0x0000000a: "invalid source address", 121 | 0x0000000b: "invalid destination address", 122 | 0x0000000c: "invalid message id", 123 | 0x0000000d: "bind failed", 124 | 0x0000000e: "invalid password", 125 | 0x0000000f: "invalid system id", 126 | 0x00000011: "cancelsm failed", 127 | 0x00000013: "replacesm failed", 128 | 0x00000014: "message queue full", 129 | 0x00000015: "invalid service type", 130 | 0x00000033: "invalid number of destinations", 131 | 0x00000034: "invalid distribution list name", 132 | 0x00000040: "invalid destination flag", 133 | 0x00000042: "invalid 'submit with replace' request", 134 | 0x00000043: "invalid esm class field data", 135 | 0x00000044: "cannot submit to distribution list", 136 | 0x00000045: "submitsm or submitmulti failed", 137 | 0x00000048: "invalid source address ton", 138 | 0x00000049: "invalid source address npi", 139 | 0x00000050: "invalid destination address ton", 140 | 0x00000051: "invalid destination address npi", 141 | 0x00000053: "invalid system type field", 142 | 0x00000054: "invalid replace_if_present flag", 143 | 0x00000055: "invalid number of messages", 144 | 0x00000058: "throttling error", 145 | 0x00000061: "invalid scheduled delivery time", 146 | 0x00000062: "invalid message validity period (expiry time)", 147 | 0x00000063: "predefined message invalid or not found", 148 | 0x00000064: "esme receiver temporary app error code", 149 | 0x00000065: "esme receiver permanent app error code", 150 | 0x00000066: "esme receiver reject message error code", 151 | 0x00000067: "querysm request failed", 152 | 0x000000c0: "error in the optional part of the pdu body", 153 | 0x000000c1: "optional parameter not allowed", 154 | 0x000000c2: "invalid parameter length", 155 | 0x000000c3: "expected optional parameter missing", 156 | 0x000000c4: "invalid optional parameter value", 157 | 0x000000fe: "delivery failure (used for datasmresp)", 158 | 0x000000ff: "unknown error", 159 | } 160 | -------------------------------------------------------------------------------- /vendor/golang.org/x/text/encoding/encoding.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package encoding defines an interface for character encodings, such as Shift 6 | // JIS and Windows 1252, that can convert to and from UTF-8. 7 | // 8 | // To convert the bytes of an io.Reader r from the encoding e to UTF-8: 9 | // rInUTF8 := transform.NewReader(r, e.NewDecoder()) 10 | // and to convert from UTF-8 to the encoding e: 11 | // wInUTF8 := transform.NewWriter(w, e.NewEncoder()) 12 | // In both cases, import "golang.org/x/text/transform". 13 | // 14 | // Encoding implementations are provided in other packages, such as 15 | // golang.org/x/text/encoding/charmap and 16 | // golang.org/x/text/encoding/japanese. 17 | package encoding 18 | 19 | import ( 20 | "errors" 21 | "unicode/utf8" 22 | 23 | "golang.org/x/text/transform" 24 | ) 25 | 26 | // Encoding is a character set encoding that can be transformed to and from 27 | // UTF-8. 28 | type Encoding interface { 29 | // NewDecoder returns a transformer that converts to UTF-8. 30 | // 31 | // Transforming source bytes that are not of that encoding will not 32 | // result in an error per se. Each byte that cannot be transcoded will 33 | // be represented in the output by the UTF-8 encoding of '\uFFFD', the 34 | // replacement rune. 35 | NewDecoder() transform.Transformer 36 | 37 | // NewEncoder returns a transformer that converts from UTF-8. 38 | // 39 | // Transforming source bytes that are not valid UTF-8 will not result in 40 | // an error per se. Each rune that cannot be transcoded will be 41 | // represented in the output by an encoding-specific replacement such as 42 | // "\x1a" (the ASCII substitute character) or "\xff\xfd". To return 43 | // early with error instead, use transform.Chain to preprocess the data 44 | // with a UTF8Validator. 45 | NewEncoder() transform.Transformer 46 | } 47 | 48 | // ASCIISub is the ASCII substitute character, as recommended by 49 | // http://unicode.org/reports/tr36/#Text_Comparison 50 | const ASCIISub = '\x1a' 51 | 52 | // Nop is the nop encoding. Its transformed bytes are the same as the source 53 | // bytes; it does not replace invalid UTF-8 sequences. 54 | var Nop Encoding = nop{} 55 | 56 | type nop struct{} 57 | 58 | func (nop) NewDecoder() transform.Transformer { 59 | return transform.Nop 60 | } 61 | 62 | func (nop) NewEncoder() transform.Transformer { 63 | return transform.Nop 64 | } 65 | 66 | // Replacement is the replacement encoding. Decoding from the replacement 67 | // encoding yields a single '\uFFFD' replacement rune. Encoding from UTF-8 to 68 | // the replacement encoding yields the same as the source bytes except that 69 | // invalid UTF-8 is converted to '\uFFFD'. 70 | // 71 | // It is defined at http://encoding.spec.whatwg.org/#replacement 72 | var Replacement Encoding = replacement{} 73 | 74 | type replacement struct{} 75 | 76 | func (replacement) NewDecoder() transform.Transformer { 77 | return replacementDecoder{} 78 | } 79 | 80 | func (replacement) NewEncoder() transform.Transformer { 81 | return replacementEncoder{} 82 | } 83 | 84 | type replacementDecoder struct{ transform.NopResetter } 85 | 86 | func (replacementDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { 87 | if len(dst) < 3 { 88 | return 0, 0, transform.ErrShortDst 89 | } 90 | if atEOF { 91 | const fffd = "\ufffd" 92 | dst[0] = fffd[0] 93 | dst[1] = fffd[1] 94 | dst[2] = fffd[2] 95 | nDst = 3 96 | } 97 | return nDst, len(src), nil 98 | } 99 | 100 | type replacementEncoder struct{ transform.NopResetter } 101 | 102 | func (replacementEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { 103 | r, size := rune(0), 0 104 | 105 | for ; nSrc < len(src); nSrc += size { 106 | r = rune(src[nSrc]) 107 | 108 | // Decode a 1-byte rune. 109 | if r < utf8.RuneSelf { 110 | size = 1 111 | 112 | } else { 113 | // Decode a multi-byte rune. 114 | r, size = utf8.DecodeRune(src[nSrc:]) 115 | if size == 1 { 116 | // All valid runes of size 1 (those below utf8.RuneSelf) were 117 | // handled above. We have invalid UTF-8 or we haven't seen the 118 | // full character yet. 119 | if !atEOF && !utf8.FullRune(src[nSrc:]) { 120 | err = transform.ErrShortSrc 121 | break 122 | } 123 | r = '\ufffd' 124 | } 125 | } 126 | 127 | if nDst+utf8.RuneLen(r) > len(dst) { 128 | err = transform.ErrShortDst 129 | break 130 | } 131 | nDst += utf8.EncodeRune(dst[nDst:], r) 132 | } 133 | return nDst, nSrc, err 134 | } 135 | 136 | // ErrInvalidUTF8 means that a transformer encountered invalid UTF-8. 137 | var ErrInvalidUTF8 = errors.New("encoding: invalid UTF-8") 138 | 139 | // UTF8Validator is a transformer that returns ErrInvalidUTF8 on the first 140 | // input byte that is not valid UTF-8. 141 | var UTF8Validator transform.Transformer = utf8Validator{} 142 | 143 | type utf8Validator struct{ transform.NopResetter } 144 | 145 | func (utf8Validator) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { 146 | n := len(src) 147 | if n > len(dst) { 148 | n = len(dst) 149 | } 150 | for i := 0; i < n; { 151 | if c := src[i]; c < utf8.RuneSelf { 152 | dst[i] = c 153 | i++ 154 | continue 155 | } 156 | _, size := utf8.DecodeRune(src[i:]) 157 | if size == 1 { 158 | // All valid runes of size 1 (those below utf8.RuneSelf) were 159 | // handled above. We have invalid UTF-8 or we haven't seen the 160 | // full character yet. 161 | err = ErrInvalidUTF8 162 | if !atEOF && !utf8.FullRune(src[i:]) { 163 | err = transform.ErrShortSrc 164 | } 165 | return i, i, err 166 | } 167 | if i+size > len(dst) { 168 | return i, i, transform.ErrShortDst 169 | } 170 | for ; size > 0; size-- { 171 | dst[i] = src[i] 172 | i++ 173 | } 174 | } 175 | if len(src) > len(dst) { 176 | err = transform.ErrShortDst 177 | } 178 | return n, n, err 179 | } 180 | -------------------------------------------------------------------------------- /vendor/golang.org/x/text/encoding/charmap/charmap.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package charmap provides simple character encodings such as IBM Code Page 437 6 | // and Windows 1252. 7 | package charmap 8 | 9 | import ( 10 | "unicode/utf8" 11 | 12 | "golang.org/x/text/encoding" 13 | "golang.org/x/text/encoding/internal" 14 | "golang.org/x/text/encoding/internal/identifier" 15 | "golang.org/x/text/transform" 16 | ) 17 | 18 | // These encodings vary only in the way clients should interpret them. Their 19 | // coded character set is identical and a single implementation can be shared. 20 | var ( 21 | // ISO8859_6E is the ISO 8859-6E encoding. 22 | ISO8859_6E encoding.Encoding = &iso8859_6E 23 | 24 | // ISO8859_6I is the ISO 8859-6I encoding. 25 | ISO8859_6I encoding.Encoding = &iso8859_6I 26 | 27 | // ISO8859_8E is the ISO 8859-8E encoding. 28 | ISO8859_8E encoding.Encoding = &iso8859_8E 29 | 30 | // ISO8859_8I is the ISO 8859-8I encoding. 31 | ISO8859_8I encoding.Encoding = &iso8859_8I 32 | 33 | iso8859_6E = internal.Encoding{ 34 | ISO8859_6, 35 | "ISO-8859-6E", 36 | identifier.ISO88596E, 37 | } 38 | 39 | iso8859_6I = internal.Encoding{ 40 | ISO8859_6, 41 | "ISO-8859-6I", 42 | identifier.ISO88596I, 43 | } 44 | 45 | iso8859_8E = internal.Encoding{ 46 | ISO8859_8, 47 | "ISO-8859-8E", 48 | identifier.ISO88598E, 49 | } 50 | 51 | iso8859_8I = internal.Encoding{ 52 | ISO8859_8, 53 | "ISO-8859-8I", 54 | identifier.ISO88598I, 55 | } 56 | ) 57 | 58 | // All is a list of all defined encodings in this package. 59 | var All = listAll 60 | 61 | // TODO: implement these encodings, in order of importance. 62 | // ASCII, ISO8859_1: Rather common. Close to Windows 1252. 63 | // ISO8859_9: Close to Windows 1254. 64 | 65 | // utf8Enc holds a rune's UTF-8 encoding in data[:len]. 66 | type utf8Enc struct { 67 | len uint8 68 | data [3]byte 69 | } 70 | 71 | // charmap describes an 8-bit character set encoding. 72 | type charmap struct { 73 | // name is the encoding's name. 74 | name string 75 | // mib is the encoding type of this encoder. 76 | mib identifier.MIB 77 | // asciiSuperset states whether the encoding is a superset of ASCII. 78 | asciiSuperset bool 79 | // low is the lower bound of the encoded byte for a non-ASCII rune. If 80 | // charmap.asciiSuperset is true then this will be 0x80, otherwise 0x00. 81 | low uint8 82 | // replacement is the encoded replacement character. 83 | replacement byte 84 | // decode is the map from encoded byte to UTF-8. 85 | decode [256]utf8Enc 86 | // encoding is the map from runes to encoded bytes. Each entry is a 87 | // uint32: the high 8 bits are the encoded byte and the low 24 bits are 88 | // the rune. The table entries are sorted by ascending rune. 89 | encode [256]uint32 90 | } 91 | 92 | func (m *charmap) NewDecoder() transform.Transformer { 93 | return charmapDecoder{charmap: m} 94 | } 95 | 96 | func (m *charmap) NewEncoder() transform.Transformer { 97 | return charmapEncoder{charmap: m} 98 | } 99 | 100 | func (m *charmap) String() string { 101 | return m.name 102 | } 103 | 104 | func (m *charmap) ID() (mib identifier.MIB, other string) { 105 | return m.mib, "" 106 | } 107 | 108 | // charmapDecoder implements transform.Transformer by decoding to UTF-8. 109 | type charmapDecoder struct { 110 | transform.NopResetter 111 | charmap *charmap 112 | } 113 | 114 | func (m charmapDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { 115 | for i, c := range src { 116 | if m.charmap.asciiSuperset && c < utf8.RuneSelf { 117 | if nDst >= len(dst) { 118 | err = transform.ErrShortDst 119 | break 120 | } 121 | dst[nDst] = c 122 | nDst++ 123 | nSrc = i + 1 124 | continue 125 | } 126 | 127 | decode := &m.charmap.decode[c] 128 | n := int(decode.len) 129 | if nDst+n > len(dst) { 130 | err = transform.ErrShortDst 131 | break 132 | } 133 | // It's 15% faster to avoid calling copy for these tiny slices. 134 | for j := 0; j < n; j++ { 135 | dst[nDst] = decode.data[j] 136 | nDst++ 137 | } 138 | nSrc = i + 1 139 | } 140 | return nDst, nSrc, err 141 | } 142 | 143 | // charmapEncoder implements transform.Transformer by encoding from UTF-8. 144 | type charmapEncoder struct { 145 | transform.NopResetter 146 | charmap *charmap 147 | } 148 | 149 | func (m charmapEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { 150 | r, size := rune(0), 0 151 | for nSrc < len(src) { 152 | if nDst >= len(dst) { 153 | err = transform.ErrShortDst 154 | break 155 | } 156 | r = rune(src[nSrc]) 157 | 158 | // Decode a 1-byte rune. 159 | if r < utf8.RuneSelf { 160 | nSrc++ 161 | if m.charmap.asciiSuperset { 162 | dst[nDst] = uint8(r) 163 | nDst++ 164 | continue 165 | } 166 | 167 | } else { 168 | // Decode a multi-byte rune. 169 | r, size = utf8.DecodeRune(src[nSrc:]) 170 | if size == 1 { 171 | // All valid runes of size 1 (those below utf8.RuneSelf) were 172 | // handled above. We have invalid UTF-8 or we haven't seen the 173 | // full character yet. 174 | if !atEOF && !utf8.FullRune(src[nSrc:]) { 175 | err = transform.ErrShortSrc 176 | break 177 | } 178 | } 179 | nSrc += size 180 | if r == utf8.RuneError { 181 | dst[nDst] = m.charmap.replacement 182 | nDst++ 183 | continue 184 | } 185 | } 186 | 187 | // Binary search in [low, high) for that rune in the m.charmap.encode table. 188 | for low, high := int(m.charmap.low), 0x100; ; { 189 | if low >= high { 190 | dst[nDst] = m.charmap.replacement 191 | nDst++ 192 | break 193 | } 194 | mid := (low + high) / 2 195 | got := m.charmap.encode[mid] 196 | gotRune := rune(got & (1<<24 - 1)) 197 | if gotRune < r { 198 | low = mid + 1 199 | } else if gotRune > r { 200 | high = mid 201 | } else { 202 | dst[nDst] = byte(got >> 24) 203 | nDst++ 204 | break 205 | } 206 | } 207 | } 208 | return nDst, nSrc, err 209 | } 210 | -------------------------------------------------------------------------------- /vendor/github.com/fiorix/go-smpp/smpp/client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-smpp authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | package smpp 6 | 7 | import ( 8 | "crypto/tls" 9 | "io" 10 | "math" 11 | "sync" 12 | "time" 13 | 14 | "github.com/fiorix/go-smpp/smpp/pdu" 15 | "github.com/fiorix/go-smpp/smpp/pdu/pdufield" 16 | ) 17 | 18 | // ConnStatus is an abstract interface for a connection status change. 19 | type ConnStatus interface { 20 | Status() ConnStatusID 21 | Error() error 22 | } 23 | 24 | type connStatus struct { 25 | s ConnStatusID 26 | err error 27 | } 28 | 29 | func (c *connStatus) Status() ConnStatusID { return c.s } 30 | func (c *connStatus) Error() error { return c.err } 31 | 32 | // ConnStatusID represents a connection status change. 33 | type ConnStatusID uint8 34 | 35 | // Supported connection statuses. 36 | const ( 37 | Connected ConnStatusID = iota + 1 38 | Disconnected 39 | ConnectionFailed 40 | BindFailed 41 | ) 42 | 43 | var connStatusText = map[ConnStatusID]string{ 44 | Connected: "Connected", 45 | Disconnected: "Disconnected", 46 | ConnectionFailed: "Connection failed", 47 | BindFailed: "Bind failed", 48 | } 49 | 50 | // String implements the Stringer interface. 51 | func (cs ConnStatusID) String() string { 52 | return connStatusText[cs] 53 | } 54 | 55 | // ClientConn provides a persistent client connection that handles 56 | // reconnection with a back-off algorithm. 57 | type ClientConn interface { 58 | // Bind starts the client connection and returns a 59 | // channel that is triggered every time the connection 60 | // status changes. 61 | Bind() <-chan ConnStatus 62 | 63 | // Closer embeds the Closer interface. When Close is 64 | // called, client sends the Unbind command first and 65 | // terminates the connection upon response, or 1s timeout. 66 | Closer 67 | } 68 | 69 | // client provides a persistent client connection. 70 | type client struct { 71 | Addr string 72 | TLS *tls.Config 73 | Status chan ConnStatus 74 | BindFunc func(c Conn) error 75 | EnquireLink time.Duration 76 | RespTimeout time.Duration 77 | 78 | // internal stuff. 79 | inbox chan pdu.Body 80 | conn *connSwitch 81 | stop chan struct{} 82 | once sync.Once 83 | } 84 | 85 | func (c *client) init() { 86 | c.inbox = make(chan pdu.Body) 87 | c.conn = &connSwitch{} 88 | c.stop = make(chan struct{}) 89 | if c.EnquireLink < 10*time.Second { 90 | c.EnquireLink = 10 * time.Second 91 | } 92 | } 93 | 94 | // Bind starts the connection manager and blocks until Close is called. 95 | // It must be called in a goroutine. 96 | func (c *client) Bind() { 97 | delay := 1.0 98 | const maxdelay = 120.0 99 | for !c.closed() { 100 | eli := make(chan struct{}) 101 | conn, err := Dial(c.Addr, c.TLS) 102 | if err != nil { 103 | c.notify(&connStatus{ 104 | s: ConnectionFailed, 105 | err: err, 106 | }) 107 | goto retry 108 | } 109 | c.conn.Set(conn) 110 | if err = c.BindFunc(c.conn); err != nil { 111 | c.notify(&connStatus{s: BindFailed, err: err}) 112 | goto retry 113 | } 114 | go c.enquireLink(eli) 115 | c.notify(&connStatus{s: Connected}) 116 | delay = 1 117 | for { 118 | p, err := c.conn.Read() 119 | if err != nil { 120 | c.notify(&connStatus{ 121 | s: Disconnected, 122 | err: err, 123 | }) 124 | break 125 | } 126 | switch p.Header().ID { 127 | case pdu.EnquireLinkID: 128 | // TODO: respond 129 | case pdu.EnquireLinkRespID: 130 | // TODO: don't just ignore 131 | default: 132 | c.inbox <- p 133 | } 134 | } 135 | retry: 136 | close(eli) 137 | c.conn.Close() 138 | delay = math.Min(delay*math.E, maxdelay) 139 | c.trysleep(time.Duration(delay) * time.Second) 140 | } 141 | close(c.Status) 142 | } 143 | 144 | func (c *client) enquireLink(stop chan struct{}) { 145 | for { 146 | select { 147 | case <-time.After(c.EnquireLink): 148 | err := c.conn.Write(pdu.NewEnquireLink()) 149 | if err != nil { 150 | return 151 | } 152 | case <-stop: 153 | return 154 | case <-c.stop: 155 | return 156 | } 157 | } 158 | } 159 | 160 | func (c *client) notify(ev ConnStatus) { 161 | select { 162 | case c.Status <- ev: 163 | default: 164 | } 165 | } 166 | 167 | // Read reads PDU binary data off the wire and returns it. 168 | func (c *client) Read() (pdu.Body, error) { 169 | select { 170 | case pdu := <-c.inbox: 171 | return pdu, nil 172 | case <-c.stop: 173 | return nil, io.EOF 174 | } 175 | } 176 | 177 | // Write serializes the given PDU and writes to the connection. 178 | func (c *client) Write(w pdu.Body) error { 179 | return c.conn.Write(w) 180 | } 181 | 182 | // Close terminates the current connection and stop any further attempts. 183 | func (c *client) Close() error { 184 | c.once.Do(func() { 185 | close(c.stop) 186 | if err := c.conn.Write(pdu.NewUnbind()); err == nil { 187 | select { 188 | case <-c.inbox: // TODO: validate UnbindResp 189 | case <-time.After(time.Second): 190 | } 191 | } 192 | c.conn.Close() 193 | }) 194 | return nil 195 | } 196 | 197 | // trysleep for the given duration, or return if Close is called. 198 | func (c *client) trysleep(d time.Duration) { 199 | select { 200 | case <-time.After(d): 201 | case <-c.stop: 202 | } 203 | } 204 | 205 | // closed returns true after Close is called once. 206 | func (c *client) closed() bool { 207 | select { 208 | case <-c.stop: 209 | return true 210 | default: 211 | return false 212 | } 213 | } 214 | 215 | // respTimeout returns a channel that fires based on the configured 216 | // response timeout, or the default 1s. 217 | func (c *client) respTimeout() <-chan time.Time { 218 | if c.RespTimeout == 0 { 219 | return time.After(time.Second) 220 | } 221 | return time.After(c.RespTimeout) 222 | } 223 | 224 | // bind attempts to bind the connection. 225 | func bind(c Conn, p pdu.Body) (pdu.Body, error) { 226 | f := p.Fields() 227 | f.Set(pdufield.InterfaceVersion, 0x34) 228 | err := c.Write(p) 229 | if err != nil { 230 | return nil, err 231 | } 232 | resp, err := c.Read() 233 | if err != nil { 234 | return nil, err 235 | } 236 | h := resp.Header() 237 | if h.Status != 0 { 238 | return nil, h.Status 239 | } 240 | return resp, nil 241 | } 242 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/context/context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package context defines the Context type, which carries deadlines, 6 | // cancelation signals, and other request-scoped values across API boundaries 7 | // and between processes. 8 | // 9 | // Incoming requests to a server should create a Context, and outgoing calls to 10 | // servers should accept a Context. The chain of function calls between must 11 | // propagate the Context, optionally replacing it with a modified copy created 12 | // using WithDeadline, WithTimeout, WithCancel, or WithValue. 13 | // 14 | // Programs that use Contexts should follow these rules to keep interfaces 15 | // consistent across packages and enable static analysis tools to check context 16 | // propagation: 17 | // 18 | // Do not store Contexts inside a struct type; instead, pass a Context 19 | // explicitly to each function that needs it. The Context should be the first 20 | // parameter, typically named ctx: 21 | // 22 | // func DoSomething(ctx context.Context, arg Arg) error { 23 | // // ... use ctx ... 24 | // } 25 | // 26 | // Do not pass a nil Context, even if a function permits it. Pass context.TODO 27 | // if you are unsure about which Context to use. 28 | // 29 | // Use context Values only for request-scoped data that transits processes and 30 | // APIs, not for passing optional parameters to functions. 31 | // 32 | // The same Context may be passed to functions running in different goroutines; 33 | // Contexts are safe for simultaneous use by multiple goroutines. 34 | // 35 | // See http://blog.golang.org/context for example code for a server that uses 36 | // Contexts. 37 | package context 38 | 39 | import "time" 40 | 41 | // A Context carries a deadline, a cancelation signal, and other values across 42 | // API boundaries. 43 | // 44 | // Context's methods may be called by multiple goroutines simultaneously. 45 | type Context interface { 46 | // Deadline returns the time when work done on behalf of this context 47 | // should be canceled. Deadline returns ok==false when no deadline is 48 | // set. Successive calls to Deadline return the same results. 49 | Deadline() (deadline time.Time, ok bool) 50 | 51 | // Done returns a channel that's closed when work done on behalf of this 52 | // context should be canceled. Done may return nil if this context can 53 | // never be canceled. Successive calls to Done return the same value. 54 | // 55 | // WithCancel arranges for Done to be closed when cancel is called; 56 | // WithDeadline arranges for Done to be closed when the deadline 57 | // expires; WithTimeout arranges for Done to be closed when the timeout 58 | // elapses. 59 | // 60 | // Done is provided for use in select statements: 61 | // 62 | // // Stream generates values with DoSomething and sends them to out 63 | // // until DoSomething returns an error or ctx.Done is closed. 64 | // func Stream(ctx context.Context, out <-chan Value) error { 65 | // for { 66 | // v, err := DoSomething(ctx) 67 | // if err != nil { 68 | // return err 69 | // } 70 | // select { 71 | // case <-ctx.Done(): 72 | // return ctx.Err() 73 | // case out <- v: 74 | // } 75 | // } 76 | // } 77 | // 78 | // See http://blog.golang.org/pipelines for more examples of how to use 79 | // a Done channel for cancelation. 80 | Done() <-chan struct{} 81 | 82 | // Err returns a non-nil error value after Done is closed. Err returns 83 | // Canceled if the context was canceled or DeadlineExceeded if the 84 | // context's deadline passed. No other values for Err are defined. 85 | // After Done is closed, successive calls to Err return the same value. 86 | Err() error 87 | 88 | // Value returns the value associated with this context for key, or nil 89 | // if no value is associated with key. Successive calls to Value with 90 | // the same key returns the same result. 91 | // 92 | // Use context values only for request-scoped data that transits 93 | // processes and API boundaries, not for passing optional parameters to 94 | // functions. 95 | // 96 | // A key identifies a specific value in a Context. Functions that wish 97 | // to store values in Context typically allocate a key in a global 98 | // variable then use that key as the argument to context.WithValue and 99 | // Context.Value. A key can be any type that supports equality; 100 | // packages should define keys as an unexported type to avoid 101 | // collisions. 102 | // 103 | // Packages that define a Context key should provide type-safe accessors 104 | // for the values stores using that key: 105 | // 106 | // // Package user defines a User type that's stored in Contexts. 107 | // package user 108 | // 109 | // import "golang.org/x/net/context" 110 | // 111 | // // User is the type of value stored in the Contexts. 112 | // type User struct {...} 113 | // 114 | // // key is an unexported type for keys defined in this package. 115 | // // This prevents collisions with keys defined in other packages. 116 | // type key int 117 | // 118 | // // userKey is the key for user.User values in Contexts. It is 119 | // // unexported; clients use user.NewContext and user.FromContext 120 | // // instead of using this key directly. 121 | // var userKey key = 0 122 | // 123 | // // NewContext returns a new Context that carries value u. 124 | // func NewContext(ctx context.Context, u *User) context.Context { 125 | // return context.WithValue(ctx, userKey, u) 126 | // } 127 | // 128 | // // FromContext returns the User value stored in ctx, if any. 129 | // func FromContext(ctx context.Context) (*User, bool) { 130 | // u, ok := ctx.Value(userKey).(*User) 131 | // return u, ok 132 | // } 133 | Value(key interface{}) interface{} 134 | } 135 | 136 | // Background returns a non-nil, empty Context. It is never canceled, has no 137 | // values, and has no deadline. It is typically used by the main function, 138 | // initialization, and tests, and as the top-level Context for incoming 139 | // requests. 140 | func Background() Context { 141 | return background 142 | } 143 | 144 | // TODO returns a non-nil, empty Context. Code should use context.TODO when 145 | // it's unclear which Context to use or it is not yet available (because the 146 | // surrounding function has not yet been extended to accept a Context 147 | // parameter). TODO is recognized by static analysis tools that determine 148 | // whether Contexts are propagated correctly in a program. 149 | func TODO() Context { 150 | return todo 151 | } 152 | 153 | // A CancelFunc tells an operation to abandon its work. 154 | // A CancelFunc does not wait for the work to stop. 155 | // After the first call, subsequent calls to a CancelFunc do nothing. 156 | type CancelFunc func() 157 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 18 | 19 | 20 | 21 | 119 | 120 | 121 | 122 | 123 |
124 | 125 |

SMS API

126 | 127 |
128 |
129 |

Connected

130 |

Disconnected

131 |
132 | 133 |
134 |
Send short message
135 |
136 |
137 |
138 |
139 |
140 | 141 | 142 |
143 |
144 |
145 | 146 |
147 | 152 |
153 |
154 |
155 |
156 |
157 |
158 | 159 | {{sm.text.length}} chars 160 | 161 |
162 |
163 |
164 | 165 |
166 | 171 |
172 |
173 |
174 | 175 | 176 |
177 |
178 | 179 | Done! Message ID: {{resp.id}} 180 |
181 |
182 | 183 | Failed: {{resp.err}} 184 |
185 |
186 |
187 |
188 |
189 | 190 |
191 |
Delivery receipts
192 |
193 |
194 | 195 |

From {{r.dst}} To {{r.src}} {{r.text}}

196 |
197 |
198 |
199 | 200 |
201 | 202 | 203 | 204 | --------------------------------------------------------------------------------