trilogy 2.4.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  #include <netdb.h>
2
2
  #include <netinet/tcp.h>
3
+ #include <netinet/in.h>
3
4
  #include <poll.h>
4
5
  #include <sys/socket.h>
5
6
  #include <sys/types.h>
@@ -65,7 +66,11 @@ static ssize_t _cb_raw_read(trilogy_sock_t *_sock, void *buf, size_t nread)
65
66
  struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
66
67
  ssize_t data_read = read(sock->fd, buf, nread);
67
68
  if (data_read < 0) {
68
- return (ssize_t)TRILOGY_SYSERR;
69
+ if (errno == EINTR || errno == EAGAIN) {
70
+ return (ssize_t)TRILOGY_AGAIN;
71
+ } else {
72
+ return (ssize_t)TRILOGY_SYSERR;
73
+ }
69
74
  }
70
75
  return data_read;
71
76
  }
@@ -75,6 +80,14 @@ static ssize_t _cb_raw_write(trilogy_sock_t *_sock, const void *buf, size_t nwri
75
80
  struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
76
81
  ssize_t data_written = write(sock->fd, buf, nwrite);
77
82
  if (data_written < 0) {
83
+ if (errno == EINTR || errno == EAGAIN) {
84
+ return (ssize_t)TRILOGY_AGAIN;
85
+ }
86
+
87
+ if (errno == EPIPE) {
88
+ return (ssize_t)TRILOGY_CLOSED_CONNECTION;
89
+ }
90
+
78
91
  return (ssize_t)TRILOGY_SYSERR;
79
92
  }
80
93
  return data_written;
@@ -109,7 +122,53 @@ static int _cb_raw_close(trilogy_sock_t *_sock)
109
122
  return rc;
110
123
  }
111
124
 
112
- static int _cb_raw_shutdown(trilogy_sock_t *_sock) { return shutdown(trilogy_sock_fd(_sock), SHUT_RDWR); }
125
+ static int _cb_shutdown_connect(trilogy_sock_t *_sock) {
126
+ (void)_sock;
127
+ return TRILOGY_CLOSED_CONNECTION;
128
+ }
129
+ static ssize_t _cb_shutdown_write(trilogy_sock_t *_sock, const void *buf, size_t nwrite) {
130
+ (void)_sock;
131
+ (void)buf;
132
+ (void)nwrite;
133
+ return TRILOGY_CLOSED_CONNECTION;
134
+ }
135
+ static ssize_t _cb_shutdown_read(trilogy_sock_t *_sock, void *buf, size_t nread) {
136
+ (void)_sock;
137
+ (void)buf;
138
+ (void)nread;
139
+ return TRILOGY_CLOSED_CONNECTION;
140
+ }
141
+ static int _cb_shutdown_wait(trilogy_sock_t *_sock, trilogy_wait_t wait) {
142
+ (void)_sock;
143
+ (void)wait;
144
+ return TRILOGY_OK;
145
+ }
146
+ static int _cb_shutdown_shutdown(trilogy_sock_t *_sock) {
147
+ (void)_sock;
148
+ return TRILOGY_OK;
149
+ }
150
+
151
+ // Shutdown will close the underlying socket fd and replace all I/O operations with stubs which perform no action.
152
+ static int _cb_raw_shutdown(trilogy_sock_t *_sock) {
153
+ struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
154
+
155
+ // Replace all operations with stubs which return immediately
156
+ sock->base.connect_cb = _cb_shutdown_connect;
157
+ sock->base.read_cb = _cb_shutdown_read;
158
+ sock->base.write_cb = _cb_shutdown_write;
159
+ sock->base.wait_cb = _cb_shutdown_wait;
160
+ sock->base.shutdown_cb = _cb_shutdown_shutdown;
161
+
162
+ // These "raw" callbacks won't attempt further operations on the socket and work correctly with fd set to -1
163
+ sock->base.close_cb = _cb_raw_close;
164
+ sock->base.fd_cb = _cb_raw_fd;
165
+
166
+ if (sock->fd != -1)
167
+ close(sock->fd);
168
+ sock->fd = -1;
169
+
170
+ return TRILOGY_OK;
171
+ }
113
172
 
114
173
  static int set_nonblocking_fd(int sock)
115
174
  {
@@ -306,12 +365,18 @@ fail:
306
365
 
307
366
  static ssize_t ssl_io_return(struct trilogy_sock *sock, ssize_t ret)
308
367
  {
309
- if (ret < 0) {
368
+ if (ret <= 0) {
310
369
  int rc = SSL_get_error(sock->ssl, (int)ret);
311
370
  if (rc == SSL_ERROR_WANT_WRITE || rc == SSL_ERROR_WANT_READ) {
312
371
  return (ssize_t)TRILOGY_AGAIN;
313
- } else if (rc == SSL_ERROR_SYSCALL && errno != 0) {
314
- return (ssize_t)TRILOGY_SYSERR;
372
+ } else if (rc == SSL_ERROR_SYSCALL && !ERR_peek_error()) {
373
+ if (errno == 0) {
374
+ // On OpenSSL <= 1.1.1, SSL_ERROR_SYSCALL with an errno value
375
+ // of 0 indicates unexpected EOF from the peer.
376
+ return (ssize_t)TRILOGY_CLOSED_CONNECTION;
377
+ } else {
378
+ return (ssize_t)TRILOGY_SYSERR;
379
+ }
315
380
  }
316
381
  return (ssize_t)TRILOGY_OPENSSL_ERR;
317
382
  }
@@ -321,6 +386,11 @@ static ssize_t ssl_io_return(struct trilogy_sock *sock, ssize_t ret)
321
386
  static ssize_t _cb_ssl_read(trilogy_sock_t *_sock, void *buf, size_t nread)
322
387
  {
323
388
  struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
389
+
390
+ // This shouldn't be necessary, but protects against other libraries in the same process incorrectly leaving errors
391
+ // in the queue.
392
+ ERR_clear_error();
393
+
324
394
  ssize_t data_read = (ssize_t)SSL_read(sock->ssl, buf, (int)nread);
325
395
  return ssl_io_return(sock, data_read);
326
396
  }
@@ -328,6 +398,11 @@ static ssize_t _cb_ssl_read(trilogy_sock_t *_sock, void *buf, size_t nread)
328
398
  static ssize_t _cb_ssl_write(trilogy_sock_t *_sock, const void *buf, size_t nwrite)
329
399
  {
330
400
  struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
401
+
402
+ // This shouldn't be necessary, but protects against other libraries in the same process incorrectly leaving errors
403
+ // in the queue.
404
+ ERR_clear_error();
405
+
331
406
  ssize_t data_written = (ssize_t)SSL_write(sock->ssl, buf, (int)nwrite);
332
407
  return ssl_io_return(sock, data_written);
333
408
  }
@@ -340,15 +415,9 @@ static int _cb_ssl_shutdown(trilogy_sock_t *_sock)
340
415
  // we need to close it. The OpenSSL explicitly states
341
416
  // not to call SSL_shutdown on a broken SSL socket.
342
417
  SSL_free(sock->ssl);
343
- // Reset the handlers since we tore down SSL, so we
344
- // fall back to the regular methods for detecting
345
- // we have a closed connection and for the cleanup.
346
- sock->base.read_cb = _cb_raw_read;
347
- sock->base.write_cb = _cb_raw_write;
348
- sock->base.shutdown_cb = _cb_raw_shutdown;
349
- sock->base.close_cb = _cb_raw_close;
350
418
  sock->ssl = NULL;
351
419
 
420
+ // This will rewrite the handlers
352
421
  return _cb_raw_shutdown(_sock);
353
422
  }
354
423
 
@@ -356,7 +425,12 @@ static int _cb_ssl_close(trilogy_sock_t *_sock)
356
425
  {
357
426
  struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
358
427
  if (sock->ssl != NULL) {
359
- SSL_shutdown(sock->ssl);
428
+ if (SSL_in_init(sock->ssl) == 0) {
429
+ (void)SSL_shutdown(sock->ssl);
430
+ // SSL_shutdown might return WANT_WRITE or WANT_READ. Ideally we would retry but we don't want to block.
431
+ // It may also push an error onto the OpenSSL error queue, so clear that.
432
+ ERR_clear_error();
433
+ }
360
434
  SSL_free(sock->ssl);
361
435
  sock->ssl = NULL;
362
436
  }
@@ -537,6 +611,10 @@ int trilogy_sock_upgrade_ssl(trilogy_sock_t *_sock)
537
611
  {
538
612
  struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
539
613
 
614
+ // This shouldn't be necessary, but protects against other libraries in the same process incorrectly leaving errors
615
+ // in the queue.
616
+ ERR_clear_error();
617
+
540
618
  SSL_CTX *ctx = trilogy_ssl_ctx(&sock->base.opts);
541
619
 
542
620
  if (!ctx) {
@@ -548,7 +626,7 @@ int trilogy_sock_upgrade_ssl(trilogy_sock_t *_sock)
548
626
 
549
627
  if (sock->base.opts.ssl_mode == TRILOGY_SSL_VERIFY_IDENTITY && sock->base.opts.hostname == NULL) {
550
628
  // If hostname validation is requested and no hostname provided, treat it as an error.
551
- #if OPENSSL_VERSION_NUMBER >= 0x1010000fL
629
+ #ifdef SSL_F_TLS_PROCESS_SERVER_CERTIFICATE
552
630
  ERR_put_error(ERR_LIB_SSL, SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, SSL_R_CERTIFICATE_VERIFY_FAILED, NULL, 0);
553
631
  #else
554
632
  ERR_put_error(ERR_LIB_SSL, SSL_F_SSL3_GET_SERVER_CERTIFICATE, SSL_R_CERTIFICATE_VERIFY_FAILED, NULL, 0);
@@ -579,6 +657,10 @@ int trilogy_sock_upgrade_ssl(trilogy_sock_t *_sock)
579
657
  goto fail;
580
658
 
581
659
  for (;;) {
660
+ // This shouldn't be necessary, but protects against other libraries in the same process incorrectly leaving errors
661
+ // in the queue.
662
+ ERR_clear_error();
663
+
582
664
  int ret = SSL_connect(sock->ssl);
583
665
  if (ret == 1) {
584
666
  #if OPENSSL_VERSION_NUMBER < 0x1000200fL
@@ -621,28 +703,3 @@ fail:
621
703
  sock->ssl = NULL;
622
704
  return TRILOGY_OPENSSL_ERR;
623
705
  }
624
-
625
- int trilogy_sock_discard(trilogy_sock_t *_sock)
626
- {
627
- struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
628
-
629
- if (sock->fd < 0) {
630
- return TRILOGY_OK;
631
- }
632
-
633
- int null_fd = open("/dev/null", O_RDWR | O_CLOEXEC);
634
- if (null_fd < 0) {
635
- return TRILOGY_SYSERR;
636
- }
637
-
638
- if (dup2(null_fd, sock->fd) < 0) {
639
- close(null_fd);
640
- return TRILOGY_SYSERR;
641
- }
642
-
643
- if (close(null_fd) < 0) {
644
- return TRILOGY_SYSERR;
645
- }
646
-
647
- return TRILOGY_OK;
648
- }
@@ -0,0 +1,97 @@
1
+ class Trilogy
2
+ module Encoding
3
+ RUBY_ENCODINGS = {
4
+ "big5" => "Big5",
5
+ "dec8" => nil,
6
+ "cp850" => "CP850",
7
+ "hp8" => nil,
8
+ "koi8r" => "KOI8-R",
9
+ "latin1" => "ISO-8859-1",
10
+ "latin2" => "ISO-8859-2",
11
+ "swe7" => nil,
12
+ "ascii" => "US-ASCII",
13
+ "ujis" => "eucJP-ms",
14
+ "sjis" => "Shift_JIS",
15
+ "hebrew" => "ISO-8859-8",
16
+ "tis620" => "TIS-620",
17
+ "euckr" => "EUC-KR",
18
+ "koi8u" => "KOI8-R",
19
+ "gb2312" => "GB2312",
20
+ "greek" => "ISO-8859-7",
21
+ "cp1250" => "Windows-1250",
22
+ "gbk" => "GBK",
23
+ "latin5" => "ISO-8859-9",
24
+ "armscii8" => nil,
25
+ "utf8" => "UTF-8",
26
+ "ucs2" => "UTF-16BE",
27
+ "cp866" => "IBM866",
28
+ "keybcs2" => nil,
29
+ "macce" => "macCentEuro",
30
+ "macroman" => "macRoman",
31
+ "cp852" => "CP852",
32
+ "latin7" => "ISO-8859-13",
33
+ "utf8mb4" => "UTF-8",
34
+ "cp1251" => "Windows-1251",
35
+ "utf16" => "UTF-16",
36
+ "cp1256" => "Windows-1256",
37
+ "cp1257" => "Windows-1257",
38
+ "utf32" => "UTF-32",
39
+ "binary" => "ASCII-8BIT",
40
+ "geostd8" => nil,
41
+ "cp932" => "Windows-31J",
42
+ "eucjpms" => "eucJP-ms",
43
+ "utf16le" => "UTF-16LE",
44
+ "gb18030" => "GB18030",
45
+ }.freeze
46
+
47
+ CHARSETS = {
48
+ "big5" => CHARSET_BIG5_CHINESE_CI,
49
+ "cp850" => CHARSET_CP850_GENERAL_CI,
50
+ "koi8r" => CHARSET_KOI8R_GENERAL_CI,
51
+ "latin1" => CHARSET_LATIN1_GENERAL_CI,
52
+ "latin2" => CHARSET_LATIN2_GENERAL_CI,
53
+ "ascii" => CHARSET_ASCII_GENERAL_CI,
54
+ "ujis" => CHARSET_UJIS_JAPANESE_CI,
55
+ "sjis" => CHARSET_SJIS_JAPANESE_CI,
56
+ "hebrew" => CHARSET_HEBREW_GENERAL_CI,
57
+ "tis620" => CHARSET_TIS620_THAI_CI,
58
+ "euckr" => CHARSET_EUCKR_KOREAN_CI,
59
+ "koi8u" => CHARSET_KOI8U_GENERAL_CI,
60
+ "gb2312" => CHARSET_GB2312_CHINESE_CI,
61
+ "greek" => CHARSET_GREEK_GENERAL_CI,
62
+ "cp1250" => CHARSET_CP1250_GENERAL_CI,
63
+ "gbk" => CHARSET_GBK_CHINESE_CI,
64
+ "latin5" => CHARSET_LATIN5_TURKISH_CI,
65
+ "utf8" => CHARSET_UTF8_GENERAL_CI,
66
+ "ucs2" => CHARSET_UCS2_GENERAL_CI,
67
+ "cp866" => CHARSET_CP866_GENERAL_CI,
68
+ "cp932" => CHARSET_CP932_JAPANESE_CI,
69
+ "eucjpms" => CHARSET_EUCJPMS_JAPANESE_CI,
70
+ "utf16le" => CHARSET_UTF16_GENERAL_CI,
71
+ "gb18030" => CHARSET_GB18030_CHINESE_CI,
72
+ "macce" => CHARSET_MACCE_GENERAL_CI,
73
+ "macroman" => CHARSET_MACROMAN_GENERAL_CI,
74
+ "cp852" => CHARSET_CP852_GENERAL_CI,
75
+ "latin7" => CHARSET_LATIN7_GENERAL_CI,
76
+ "utf8mb4" => CHARSET_UTF8MB4_GENERAL_CI,
77
+ "cp1251" => CHARSET_CP1251_GENERAL_CI,
78
+ "utf16" => CHARSET_UTF16_GENERAL_CI,
79
+ "cp1256" => CHARSET_CP1256_GENERAL_CI,
80
+ "cp1257" => CHARSET_CP1257_GENERAL_CI,
81
+ "utf32" => CHARSET_UTF32_GENERAL_CI,
82
+ "binary" => CHARSET_BINARY,
83
+ }.freeze
84
+
85
+ def self.find(mysql_encoding)
86
+ unless rb_encoding = RUBY_ENCODINGS[mysql_encoding]
87
+ raise ArgumentError, "Unknown or unsupported encoding: #{mysql_encoding}"
88
+ end
89
+
90
+ ::Encoding.find(rb_encoding)
91
+ end
92
+
93
+ def self.charset(mysql_encoding)
94
+ CHARSETS[mysql_encoding]
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,118 @@
1
+ class Trilogy
2
+ # Trilogy::Error is the base error type. All errors raised by Trilogy
3
+ # should be descendants of Trilogy::Error
4
+ module Error
5
+ attr_reader :error_code
6
+ end
7
+
8
+ # Trilogy::ConnectionError is the base error type for all potentially transient
9
+ # network errors.
10
+ module ConnectionError
11
+ include Error
12
+ end
13
+
14
+ # Trilogy may raise various syscall errors, which we treat as Trilogy::Errors.
15
+ class SyscallError
16
+ ERRORS = {}
17
+
18
+ Errno.constants
19
+ .map { |c| Errno.const_get(c) }.uniq
20
+ .select { |c| c.is_a?(Class) && c < SystemCallError }
21
+ .each do |c|
22
+ errno_name = c.to_s.split('::').last
23
+ ERRORS[c::Errno] = const_set(errno_name, Class.new(c) { include Trilogy::Error })
24
+ end
25
+
26
+ ERRORS.freeze
27
+
28
+ class << self
29
+ def from_errno(errno, message)
30
+ ERRORS[errno].new(message)
31
+ end
32
+ end
33
+ end
34
+
35
+ class BaseError < StandardError
36
+ include Error
37
+
38
+ def initialize(error_message = nil, error_code = nil)
39
+ message = error_code ? "#{error_code}: #{error_message}" : error_message
40
+ super(message)
41
+ @error_code = error_code
42
+ end
43
+ end
44
+
45
+ class BaseConnectionError < BaseError
46
+ include ConnectionError
47
+ end
48
+
49
+ # Trilogy::ClientError is the base error type for invalid queries or parameters
50
+ # that shouldn't be retried.
51
+ class ClientError < BaseError
52
+ include Error
53
+ end
54
+
55
+ class QueryError < ClientError
56
+ end
57
+
58
+ class CastError < ClientError
59
+ end
60
+
61
+ class TimeoutError < Errno::ETIMEDOUT
62
+ include ConnectionError
63
+
64
+ def initialize(error_message = nil, error_code = nil)
65
+ super
66
+ @error_code = error_code
67
+ end
68
+ end
69
+
70
+ class ConnectionRefusedError < Errno::ECONNREFUSED
71
+ include ConnectionError
72
+ end
73
+
74
+ class ConnectionResetError < Errno::ECONNRESET
75
+ include ConnectionError
76
+ end
77
+
78
+ # DatabaseError was replaced by ProtocolError, but we'll keep it around as an
79
+ # ancestor of ProtocolError for compatibility reasons (e.g. so `rescue DatabaseError`
80
+ # still works. We can remove this class in the next major release.
81
+ module DatabaseError
82
+ end
83
+
84
+ class ProtocolError < BaseError
85
+ include DatabaseError
86
+
87
+ ERROR_CODES = {
88
+ 1205 => TimeoutError, # ER_LOCK_WAIT_TIMEOUT
89
+ 1044 => BaseConnectionError, # ER_DBACCESS_DENIED_ERROR
90
+ 1045 => BaseConnectionError, # ER_ACCESS_DENIED_ERROR
91
+ 1064 => QueryError, # ER_PARSE_ERROR
92
+ 1152 => BaseConnectionError, # ER_ABORTING_CONNECTION
93
+ 1153 => BaseConnectionError, # ER_NET_PACKET_TOO_LARGE
94
+ 1154 => BaseConnectionError, # ER_NET_READ_ERROR_FROM_PIPE
95
+ 1155 => BaseConnectionError, # ER_NET_FCNTL_ERROR
96
+ 1156 => BaseConnectionError, # ER_NET_PACKETS_OUT_OF_ORDER
97
+ 1157 => BaseConnectionError, # ER_NET_UNCOMPRESS_ERROR
98
+ 1158 => BaseConnectionError, # ER_NET_READ_ERROR
99
+ 1159 => BaseConnectionError, # ER_NET_READ_INTERRUPTED
100
+ 1160 => BaseConnectionError, # ER_NET_ERROR_ON_WRITE
101
+ 1161 => BaseConnectionError, # ER_NET_WRITE_INTERRUPTED
102
+ 1927 => BaseConnectionError, # ER_CONNECTION_KILLED
103
+ }
104
+ class << self
105
+ def from_code(message, code)
106
+ ERROR_CODES.fetch(code, self).new(message, code)
107
+ end
108
+ end
109
+ end
110
+
111
+ class SSLError < BaseError
112
+ include ConnectionError
113
+ end
114
+
115
+ class ConnectionClosed < IOError
116
+ include ConnectionError
117
+ end
118
+ end
@@ -0,0 +1,33 @@
1
+ class Trilogy
2
+ class Result
3
+ attr_reader :fields, :rows, :query_time, :affected_rows, :last_insert_id
4
+
5
+ def count
6
+ rows.count
7
+ end
8
+
9
+ def each_hash
10
+ return enum_for(:each_hash) unless block_given?
11
+
12
+ rows.each do |row|
13
+ this_row = {}
14
+
15
+ idx = 0
16
+ row.each do |col|
17
+ this_row[fields[idx]] = col
18
+ idx += 1
19
+ end
20
+
21
+ yield this_row
22
+ end
23
+
24
+ self
25
+ end
26
+
27
+ def each(&bk)
28
+ rows.each(&bk)
29
+ end
30
+
31
+ include Enumerable
32
+ end
33
+ end
@@ -1,3 +1,3 @@
1
1
  class Trilogy
2
- VERSION = "2.4.0"
2
+ VERSION = "2.5.0"
3
3
  end