forked from CloudInn/escpos
-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.go
134 lines (110 loc) · 2.9 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package escpos
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"net/http"
"github.com/moovweb/gokogiri"
)
const (
// DefaultEndpoint is the default server endpoint for ePOS printers.
DefaultEndpoint = "/cgi-bin/epos/service.cgi"
)
// Server wrap
type Server struct {
p *Printer
w *bufio.Writer
logger func(string, ...interface{})
}
// NewServer creates a new ePOS server.
func NewServer(w io.Writer, opts ...ServerOption) (*Server, error) {
var err error
// create printer
p, err := NewPrinter(w)
if err != nil {
return nil, err
}
s := &Server{
p: p,
w: bufio.NewWriter(p),
}
// apply opts
for _, o := range opts {
err = o(s)
if err != nil {
return nil, err
}
}
if s.logger == nil {
s.logger = func(string, ...interface{}) {}
}
return s, nil
}
// ServeHTTP handles OPTIONS, Origin, and POST for an ePOS server.
func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
s.logger("%s %s", req.Method, req.URL)
// send origin headers
if origin := req.Header.Get("Origin"); origin != "" {
res.Header().Set("Access-Control-Allow-Origin", origin)
res.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
res.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, If-Modified-Since, SOAPAction")
}
// stop if its options
if req.Method == "OPTIONS" {
return
}
// bail if not POST
if req.Method != "POST" {
http.Error(res, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
return
}
// grab posted body
body, err := ioutil.ReadAll(req.Body)
if err != nil {
http.Error(res, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
defer req.Body.Close()
// parse xml with gokogiri
doc, err := gokogiri.ParseXml(body)
if err != nil {
http.Error(res, "cannot load XML", http.StatusBadRequest)
return
}
defer doc.Free()
// load print nodes from xml doc
nodes, err := getBodyChildren(doc)
if err != nil {
http.Error(res, "cannot find SOAP request Body", http.StatusBadRequest)
return
}
// init printer
s.p.Init()
// loop over nodes
for _, n := range nodes {
// grab parameters
params := make(map[string]string)
for _, attr := range n.Attributes() {
params[attr.Name()] = attr.Value()
}
// write data to printer
s.p.WriteNode(n.Name(), params, n.Content())
}
// end
s.p.End()
// flush writer
s.w.Flush()
// write soap response
res.Header().Set("Content-Type", req.Header.Get("Content-Type"))
fmt.Fprintf(res, soapBody, true, "")
}
const (
// soapBody is a basic SOAP response body for an ePOS server response.
soapBody = `<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body xmlns:m="http://www.epson-pos.com/schemas/2011/03/epos-print">
<m:response success="%t" code="%s" status="0"></m:response>
</s:Body>
</s:Envelope>`
)