go-tg-screenshot-bot

Форк
0
178 строк · 5.8 Кб
1
package xgb
2

3
import (
4
	"errors"
5
	"io"
6
)
7

8
// Cookie is the internal representation of a cookie, where one is generated
9
// for *every* request sent by XGB.
10
// 'cookie' is most frequently used by embedding it into a more specific
11
// kind of cookie, i.e., 'GetInputFocusCookie'.
12
type Cookie struct {
13
	conn      *Conn
14
	Sequence  uint16
15
	replyChan chan []byte
16
	errorChan chan error
17
	pingChan  chan bool
18
}
19

20
// NewCookie creates a new cookie with the correct channels initialized
21
// depending upon the values of 'checked' and 'reply'. Together, there are
22
// four different kinds of cookies. (See more detailed comments in the
23
// function for more info on those.)
24
// Note that a sequence number is not set until just before the request
25
// corresponding to this cookie is sent over the wire.
26
//
27
// Unless you're building requests from bytes by hand, this method should
28
// not be used.
29
func (c *Conn) NewCookie(checked, reply bool) *Cookie {
30
	cookie := &Cookie{
31
		conn:      c,
32
		Sequence:  0, // we add the sequence id just before sending a request
33
		replyChan: nil,
34
		errorChan: nil,
35
		pingChan:  nil,
36
	}
37

38
	// There are four different kinds of cookies:
39
	// Checked requests with replies get a reply channel and an error channel.
40
	// Unchecked requests with replies get a reply channel and a ping channel.
41
	// Checked requests w/o replies get a ping channel and an error channel.
42
	// Unchecked requests w/o replies get no channels.
43
	// The reply channel is used to send reply data.
44
	// The error channel is used to send error data.
45
	// The ping channel is used when one of the 'reply' or 'error' channels
46
	// is missing but the other is present. The ping channel is way to force
47
	// the blocking to stop and basically say "the error has been received
48
	// in the main event loop" (when the ping channel is coupled with a reply
49
	// channel) or "the request you made that has no reply was successful"
50
	// (when the ping channel is coupled with an error channel).
51
	if checked {
52
		cookie.errorChan = make(chan error, 1)
53
		if !reply {
54
			cookie.pingChan = make(chan bool, 1)
55
		}
56
	}
57
	if reply {
58
		cookie.replyChan = make(chan []byte, 1)
59
		if !checked {
60
			cookie.pingChan = make(chan bool, 1)
61
		}
62
	}
63

64
	return cookie
65
}
66

67
// Reply detects whether this is a checked or unchecked cookie, and calls
68
// 'replyChecked' or 'replyUnchecked' appropriately.
69
//
70
// Unless you're building requests from bytes by hand, this method should
71
// not be used.
72
func (c Cookie) Reply() ([]byte, error) {
73
	// checked
74
	if c.errorChan != nil {
75
		return c.replyChecked()
76
	}
77
	return c.replyUnchecked()
78
}
79

80
// replyChecked waits for a response on either the replyChan or errorChan
81
// channels. If the former arrives, the bytes are returned with a nil error.
82
// If the latter arrives, no bytes are returned (nil) and the error received
83
// is returned.
84
// Returns (nil, io.EOF) when the connection is closed.
85
//
86
// Unless you're building requests from bytes by hand, this method should
87
// not be used.
88
func (c Cookie) replyChecked() ([]byte, error) {
89
	if c.replyChan == nil {
90
		return nil, errors.New("Cannot call 'replyChecked' on a cookie that " +
91
			"is not expecting a *reply* or an error.")
92
	}
93
	if c.errorChan == nil {
94
		return nil, errors.New("Cannot call 'replyChecked' on a cookie that " +
95
			"is not expecting a reply or an *error*.")
96
	}
97

98
	select {
99
	case reply := <-c.replyChan:
100
		return reply, nil
101
	case err := <-c.errorChan:
102
		return nil, err
103
	case <-c.conn.doneRead:
104
		// c.conn.readResponses is no more, there will be no replys or errors
105
		return nil, io.EOF
106
	}
107
}
108

109
// replyUnchecked waits for a response on either the replyChan or pingChan
110
// channels. If the former arrives, the bytes are returned with a nil error.
111
// If the latter arrives, no bytes are returned (nil) and a nil error
112
// is returned. (In the latter case, the corresponding error can be retrieved
113
// from (Wait|Poll)ForEvent asynchronously.)
114
// Returns (nil, io.EOF) when the connection is closed.
115
// In all honesty, you *probably* don't want to use this method.
116
//
117
// Unless you're building requests from bytes by hand, this method should
118
// not be used.
119
func (c Cookie) replyUnchecked() ([]byte, error) {
120
	if c.replyChan == nil {
121
		return nil, errors.New("Cannot call 'replyUnchecked' on a cookie " +
122
			"that is not expecting a *reply*.")
123
	}
124

125
	select {
126
	case reply := <-c.replyChan:
127
		return reply, nil
128
	case <-c.pingChan:
129
		return nil, nil
130
	case <-c.conn.doneRead:
131
		// c.conn.readResponses is no more, there will be no replys or pings
132
		return nil, io.EOF
133
	}
134
}
135

136
// Check is used for checked requests that have no replies. It is a mechanism
137
// by which to report "success" or "error" in a synchronous fashion. (Therefore,
138
// unchecked requests without replies cannot use this method.)
139
// If the request causes an error, it is sent to this cookie's errorChan.
140
// If the request was successful, there is no response from the server.
141
// Thus, pingChan is sent a value when the *next* reply is read.
142
// If no more replies are being processed, we force a round trip request with
143
// GetInputFocus.
144
// Returns io.EOF error when the connection is closed.
145
//
146
// Unless you're building requests from bytes by hand, this method should
147
// not be used.
148
func (c Cookie) Check() error {
149
	if c.replyChan != nil {
150
		return errors.New("Cannot call 'Check' on a cookie that is " +
151
			"expecting a *reply*. Use 'Reply' instead.")
152
	}
153
	if c.errorChan == nil {
154
		return errors.New("Cannot call 'Check' on a cookie that is " +
155
			"not expecting a possible *error*.")
156
	}
157

158
	// First do a quick non-blocking check to see if we've been pinged.
159
	select {
160
	case err := <-c.errorChan:
161
		return err
162
	case <-c.pingChan:
163
		return nil
164
	default:
165
	}
166

167
	// Now force a round trip and try again, but block this time.
168
	c.conn.Sync()
169
	select {
170
	case err := <-c.errorChan:
171
		return err
172
	case <-c.pingChan:
173
		return nil
174
	case <-c.conn.doneRead:
175
		// c.conn.readResponses is no more, there will be no errors or pings
176
		return io.EOF
177
	}
178
}
179

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.