Qore Programming Language  0.9.4.6
qore_socket_private.h
1 /* -*- mode: c++; indent-tabs-mode: nil -*- */
2 /*
3  qore_socket_private.h
4 
5  Qore Programming Language
6 
7  Copyright (C) 2003 - 2020 Qore Technologies, s.r.o.
8 
9  Permission is hereby granted, free of charge, to any person obtaining a
10  copy of this software and associated documentation files (the "Software"),
11  to deal in the Software without restriction, including without limitation
12  the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  and/or sell copies of the Software, and to permit persons to whom the
14  Software is furnished to do so, subject to the following conditions:
15 
16  The above copyright notice and this permission notice shall be included in
17  all copies or substantial portions of the Software.
18 
19  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25  DEALINGS IN THE SOFTWARE.
26 
27  Note that the Qore library is released under a choice of three open-source
28  licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
29  information.
30 */
31 
32 #ifndef _QORE_QORE_SOCKET_PRIVATE_H
33 #define _QORE_QORE_SOCKET_PRIVATE_H
34 
35 #include "qore/intern/SSLSocketHelper.h"
36 
37 #include "qore/intern/QC_Queue.h"
38 
39 #include <cctype>
40 #include <cctype>
41 #include <cerrno>
42 #include <cstdlib>
43 #include <cstring>
44 #include <strings.h>
45 
46 #include <openssl/ssl.h>
47 #include <openssl/err.h>
48 
49 #if defined HAVE_POLL
50 #include <poll.h>
51 #elif defined HAVE_SYS_SELECT_H
52 #include <sys/select.h>
53 #elif (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
54 #define HAVE_SELECT 1
55 #else
56 #error no async socket I/O APIs available
57 #endif
58 
59 #ifndef DEFAULT_SOCKET_BUFSIZE
60 #define DEFAULT_SOCKET_BUFSIZE 4096
61 #endif
62 
63 #ifndef QORE_MAX_HEADER_SIZE
64 #define QORE_MAX_HEADER_SIZE 16384
65 #endif
66 
67 #define CHF_HTTP11 (1 << 0)
68 #define CHF_PROCESS (1 << 1)
69 #define CHF_REQUEST (1 << 2)
70 
71 #ifndef DEFAULT_SOCKET_MIN_THRESHOLD_BYTES
72 #define DEFAULT_SOCKET_MIN_THRESHOLD_BYTES 1024
73 #endif
74 
75 DLLLOCAL void concat_target(QoreString& str, const struct sockaddr *addr, const char* type = "target");
76 DLLLOCAL int do_read_error(qore_offset_t rc, const char* method_name, int timeout_ms, ExceptionSink* xsink);
77 DLLLOCAL int sock_get_raw_error();
78 DLLLOCAL int sock_get_error();
79 DLLLOCAL void qore_socket_error(ExceptionSink* xsink, const char* err, const char* cdesc, const char* mname = 0, const char* host = 0, const char* svc = 0, const struct sockaddr *addr = 0);
80 DLLLOCAL void qore_socket_error_intern(int rc, ExceptionSink* xsink, const char* err, const char* cdesc, const char* mname = 0, const char* host = 0, const char* svc = 0, const struct sockaddr *addr = 0);
81 DLLLOCAL void se_in_op(const char* cname, const char* meth, ExceptionSink* xsink);
82 DLLLOCAL void se_in_op_thread(const char* cname, const char* meth, ExceptionSink* xsink);
83 DLLLOCAL void se_not_open(const char* cname, const char* meth, ExceptionSink* xsink);
84 DLLLOCAL void se_timeout(const char* cname, const char* meth, int timeout_ms, ExceptionSink* xsink);
85 DLLLOCAL void se_closed(const char* cname, const char* mname, ExceptionSink* xsink);
86 
87 #ifdef _Q_WINDOWS
88 #define GETSOCKOPT_ARG_4 char*
89 #define SETSOCKOPT_ARG_4 const char*
90 #define SHUTDOWN_ARG SD_BOTH
91 #define QORE_INVALID_SOCKET ((int)INVALID_SOCKET)
92 #define QORE_SOCKET_ERROR SOCKET_ERROR
93 DLLLOCAL int check_windows_rc(int rc);
94 
95 #ifndef ECONNRESET
96 #define ECONNRESET WSAECONNRESET
97 #endif
98 
99 #else
100 // UNIX/Cygwin
101 #define GETSOCKOPT_ARG_4 void*
102 #define SETSOCKOPT_ARG_4 void*
103 #define SHUTDOWN_ARG SHUT_RDWR
104 #define QORE_INVALID_SOCKET -1
105 #define QORE_SOCKET_ERROR -1
106 #endif
107 
108 hashdecl qore_socketsource_private {
109  QoreStringNode* address;
110  QoreStringNode* hostname;
111 
112  DLLLOCAL qore_socketsource_private() : address(0), hostname(0) {
113  }
114 
115  DLLLOCAL ~qore_socketsource_private() {
116  if (address) address->deref();
117  if (hostname) hostname->deref();
118  }
119 
120  DLLLOCAL void setAddress(QoreStringNode* addr) {
121  assert(!address);
122  address = addr;
123  }
124 
125  DLLLOCAL void setAddress(const char* addr) {
126  assert(!address);
127  address = new QoreStringNode(addr);
128  }
129 
130  DLLLOCAL void setHostName(const char* host) {
131  assert(!hostname);
132  hostname = new QoreStringNode(host);
133  }
134 
135  DLLLOCAL void setAll(QoreObject* o, ExceptionSink* xsink) {
136  if (address) {
137  o->setValue("source", address, xsink);
138  address = 0;
139  }
140 
141  if (hostname) {
142  o->setValue("source_host", hostname, xsink);
143  hostname = 0;
144  }
145  }
146 };
147 
148 class OptionalNonBlockingHelper {
149 public:
150  qore_socket_private& sock;
151  ExceptionSink* xsink;
152  bool set;
153 
154  DLLLOCAL OptionalNonBlockingHelper(qore_socket_private& s, bool n_set, ExceptionSink* xs);
155  DLLLOCAL ~OptionalNonBlockingHelper();
156 };
157 
158 class PrivateQoreSocketTimeoutBase {
159 protected:
160  hashdecl qore_socket_private* sock;
161  int64 start;
162 
163 public:
164  DLLLOCAL PrivateQoreSocketTimeoutBase(qore_socket_private* s) : sock(s), start(sock ? q_clock_getmicros() : 0) {
165  }
166 };
167 
168 class PrivateQoreSocketTimeoutHelper : public PrivateQoreSocketTimeoutBase {
169 protected:
170  const char* op;
171 public:
172  DLLLOCAL PrivateQoreSocketTimeoutHelper(qore_socket_private* s, const char* op);
173  DLLLOCAL ~PrivateQoreSocketTimeoutHelper();
174 };
175 
176 class PrivateQoreSocketThroughputHelper : public PrivateQoreSocketTimeoutBase {
177 protected:
178  bool send;
179 public:
180  DLLLOCAL PrivateQoreSocketThroughputHelper(qore_socket_private* s, bool snd);
181  DLLLOCAL ~PrivateQoreSocketThroughputHelper();
182 
183  DLLLOCAL void finalize(int64 bytes);
184 };
185 
186 hashdecl qore_socket_private;
187 
188 hashdecl qore_socket_op_helper {
189 protected:
190  qore_socket_private* s;
191 
192 public:
193  DLLLOCAL qore_socket_op_helper(qore_socket_private* sock);
194  DLLLOCAL ~qore_socket_op_helper();
195 };
196 
197 class SSLSocketHelperHelper {
198 protected:
199  qore_socket_private* s;
200  SSLSocketHelper* ssl;
201  bool context_saved = false;
202 
203 public:
204  DLLLOCAL SSLSocketHelperHelper(qore_socket_private* sock, bool set_thread_context = false);
205 
206  DLLLOCAL ~SSLSocketHelperHelper();
207 
208  DLLLOCAL void error();
209 };
210 
211 hashdecl qore_socket_private {
212  friend class PrivateQoreSocketTimeoutHelper;
213  friend class PrivateQoreSocketThroughputHelper;
214 
215  // for client certificate capture
216  static thread_local qore_socket_private* current_socket;
217 
218  int sock, sfamily, port, stype, sprot;
219 
220  // issue #3558: connection sequence to show when a connection has been reestablished
221  int64 connection_id = 0;
222 
223  const QoreEncoding* enc;
224 
225  std::string socketname;
226  // issue #3053: client target for SNI
227  std::string client_target;
228  SSLSocketHelper* ssl = nullptr;
229  Queue* event_queue = nullptr,
230  * warn_queue = nullptr;
231 
232  // issue #3633: HTTP encoding to assume
233  std::string assume_http_encoding = "ISO-8859-1";
234 
235  // socket buffer for buffered reads
236  char rbuf[DEFAULT_SOCKET_BUFSIZE];
237 
238  // current buffer size
239  size_t buflen = 0,
240  bufoffset = 0;
241 
242  int64 tl_warning_us = 0; // timeout threshold for network action warning in microseconds
243  double tp_warning_bs = 0; // throughput warning threshold in B/s
244  int64 tp_bytes_sent = 0, // throughput: bytes sent
245  tp_bytes_recv = 0, // throughput: bytes received
246  tp_us_sent = 0, // throughput: time sending
247  tp_us_recv = 0, // throughput: time receiving
248  tp_us_min = 0 // throughput: minimum time for transfer to be considered
249  ;
250 
252  QoreValue warn_callback_arg;
254  QoreValue event_arg;
255  bool del = false,
256  http_exp_chunked_body = false,
257  ssl_accept_all_certs = false,
258  ssl_capture_remote_cert = false,
259  event_data = false;
260  int in_op = -1,
261  ssl_verify_mode = SSL_VERIFY_NONE;
262 
263  // issue #3512: the remote certificate captured
264  QoreObject* remote_cert = nullptr;
265 
266  // issue #3818: verbose certificate verification error info
267  QoreStringNode* ssl_err_str = nullptr;
268 
269  DLLLOCAL qore_socket_private(int n_sock = QORE_INVALID_SOCKET, int n_sfamily = AF_UNSPEC, int n_stype = SOCK_STREAM, int n_prot = 0, const QoreEncoding* n_enc = QCS_DEFAULT) :
270  sock(n_sock), sfamily(n_sfamily), port(-1), stype(n_stype), sprot(n_prot), enc(n_enc) {
271  }
272 
273  DLLLOCAL ~qore_socket_private() {
274  close_internal();
275 
276  // must be dereferenced and removed before deleting
277  assert(!event_queue);
278  assert(!warn_queue);
279  }
280 
281  DLLLOCAL bool isOpen() {
282  return sock != QORE_INVALID_SOCKET;
283  }
284 
285  DLLLOCAL int close() {
286  int rc = close_internal();
287  if (in_op >= 0)
288  in_op = -1;
289  if (http_exp_chunked_body)
290  http_exp_chunked_body = false;
291  sfamily = AF_UNSPEC;
292  stype = SOCK_STREAM;
293  sprot = 0;
294 
295  return rc;
296  }
297 
298  DLLLOCAL int close_and_reset() {
299  assert(sock != QORE_INVALID_SOCKET);
300  int rc;
301  while (true) {
302 #ifdef _Q_WINDOWS
303  rc = ::closesocket(sock);
304 #else
305  rc = ::close(sock);
306 #endif
307  // try again if close was interrupted by a signal
308  if (!rc || sock_get_error() != EINTR)
309  break;
310  }
311  //printd(5, "qore_socket_private::close_and_reset(this: %p) close(%d) returned %d\n", this, sock, rc);
312  sock = QORE_INVALID_SOCKET;
313  if (buflen)
314  buflen = 0;
315  if (bufoffset)
316  bufoffset = 0;
317  if (del)
318  del = false;
319  if (port != -1)
320  port = -1;
321  // issue #3053: clear hostname for SNI
322  client_target.clear();
323  return rc;
324  }
325 
326  DLLLOCAL int close_internal() {
327  //printd(5, "qore_socket_private::close_internal(this: %p) sock: %d\n", this, sock);
328  if (ssl_err_str) {
329  ssl_err_str->deref();
330  ssl_err_str = nullptr;
331  }
332  if (remote_cert) {
333  remote_cert->deref(nullptr);
334  remote_cert = nullptr;
335  }
336  if (sock >= 0) {
337  // if an SSL connection has been established, shut it down first
338  if (ssl) {
339  ssl->shutdown();
340  ssl->deref();
341  ssl = nullptr;
342  }
343 
344  if (!socketname.empty()) {
345  if (del)
346  unlink(socketname.c_str());
347  socketname.clear();
348  }
349  do_close_event();
350  // issue #3558: increment the connection sequence here. so the connection sequence is different as soon as
351  // it's closed
352  ++connection_id;
353 
354  return close_and_reset();
355  } else {
356  return 0;
357  }
358  }
359 
360  DLLLOCAL void setAssumedEncoding(const char* str) {
361  assume_http_encoding = str;
362  }
363 
364  DLLLOCAL const char* getAssumedEncoding() const {
365  return assume_http_encoding.c_str();
366  }
367 
368  DLLLOCAL int getSendTimeout() const {
369  hashdecl timeval tv;
370 
371 #if defined(HPUX) && defined(__ia64) && defined(__LP64__)
372  // on HPUX 64-bit the OS defines socklen_t to be 8 bytes
373  // but the library expects a 32-bit value
374  int size = sizeof(hashdecl timeval);
375 #else
376  socklen_t size = sizeof(hashdecl timeval);
377 #endif
378 
379  if (getsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (GETSOCKOPT_ARG_4)&tv, (socklen_t *)&size))
380  return -1;
381 
382  return tv.tv_sec * 1000 + tv.tv_usec / 1000;
383  }
384 
385  DLLLOCAL int getRecvTimeout() const {
386  hashdecl timeval tv;
387 
388 #if defined(HPUX) && defined(__ia64) && defined(__LP64__)
389  // on HPUX 64-bit the OS defines socklen_t to be 8 bytes
390  // but the library expects a 32-bit value
391  int size = sizeof(hashdecl timeval);
392 #else
393  socklen_t size = sizeof(hashdecl timeval);
394 #endif
395 
396  if (getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (GETSOCKOPT_ARG_4)&tv, (socklen_t *)&size))
397  return -1;
398 
399  return tv.tv_sec * 1000 + tv.tv_usec / 1000;
400  }
401 
402  DLLLOCAL int getPort() {
403  // if we don't need to find out what port we are, then return current value
404  if (sock == QORE_INVALID_SOCKET || (sfamily != AF_INET && sfamily != AF_INET6) || port > 0)
405  return port;
406 
407  // otherwise find out what port we're connected to
408  hashdecl sockaddr_storage addr;
409 #if defined(HPUX) && defined(__ia64) && defined(__LP64__)
410  // on HPUX 64-bit the OS defines socklen_t to be 8 bytes, but the library expects a 32-bit value
411  int size = sizeof addr;
412 #else
413  socklen_t size = sizeof addr;
414 #endif
415 
416  if (getsockname(sock, (struct sockaddr *)&addr, (socklen_t *)&size) < 0)
417  return -1;
418 
419  port = q_get_port_from_addr((const struct sockaddr *)&addr);
420  return port;
421  }
422 
423  DLLLOCAL static void do_header(const char* key, QoreString& hdr, const QoreValue& v) {
424  switch (v.getType()) {
425  case NT_STRING:
426  hdr.sprintf("%s: %s\r\n", key, v.get<const QoreStringNode>()->c_str());
427  break;
428  case NT_INT:
429  hdr.sprintf("%s: " QLLD "\r\n", key, v.getAsBigInt());
430  break;
431  case NT_FLOAT: {
432  hdr.sprintf("%s: ", key);
433  size_t offset = hdr.size();
434  hdr.sprintf("%f\r\n", v.getAsFloat());
435  // issue 1556: external modules that call setlocale() can change
436  // the decimal point character used here from '.' to ','
437  // only search the double added, QoreString::sprintf() concatenates
438  q_fix_decimal(&hdr, offset);
439  break;
440  }
441  case NT_NUMBER:
442  hdr.sprintf("%s: ", key);
443  v.get<const QoreNumberNode>()->toString(hdr);
444  hdr.concat("\r\n");
445  break;
446  case NT_BOOLEAN:
447  hdr.sprintf("%s: %d\r\n", key, (int)v.getAsBool());
448  break;
449  }
450  }
451 
452  // issue #3879: must add Content-Length if not present, even if there is no message body
455  DLLLOCAL static void do_headers(QoreString& hdr, const QoreHashNode* headers, qore_size_t size, bool addsize = true) {
456  // RFC-2616 4.4 (http://tools.ietf.org/html/rfc2616#section-4.4)
457  // add Content-Length: 0 to headers for responses without a body where there is no transfer-encoding
458  if (headers) {
459  ConstHashIterator hi(headers);
460 
461  while (hi.next()) {
462  const QoreValue v = hi.get();
463  const char* key = hi.getKey();
464  if (addsize && !strcasecmp(key, "transfer-encoding"))
465  addsize = false;
466  if (addsize && !strcasecmp(key, "content-length"))
467  addsize = false;
468  if (v.getType() == NT_LIST) {
469  ConstListIterator li(v.get<const QoreListNode>());
470  while (li.next())
471  do_header(key, hdr, li.getValue());
472  } else
473  do_header(key, hdr, v);
474  }
475  }
476  // add data and content-length header if necessary
477  if (size || addsize) {
478  hdr.sprintf("Content-Length: " QSD "\r\n", size);
479  }
480 
481  hdr.concat("\r\n");
482  }
483 
484  DLLLOCAL int listen(int backlog = 20) {
485  if (sock == QORE_INVALID_SOCKET)
486  return QSE_NOT_OPEN;
487  if (in_op >= 0)
488  return QSE_IN_OP;
489 #ifdef _Q_WINDOWS
490  if (::listen(sock, backlog)) {
491  // set errno
492  sock_get_error();
493  return -1;
494  }
495  return 0;
496 #else
497  return ::listen(sock, backlog);
498 #endif
499  }
500 
501  DLLLOCAL int accept_intern(ExceptionSink* xsink, struct sockaddr *addr, socklen_t *size, int timeout_ms = -1) {
502  //printd(5, "qore_socket_private::accept_intern() to: %d\n", timeout_ms);
503  assert(xsink);
504  while (true) {
505  if (timeout_ms >= 0 && !isDataAvailable(timeout_ms, "accept", xsink)) {
506  if (*xsink)
507  return -1;
508  // do not throw exception here, NOTHING will be returned in Qore on timeout
509  return QSE_TIMEOUT; // -3
510  }
511 
512  int rc = ::accept(sock, addr, size);
513  if (rc != QORE_INVALID_SOCKET)
514  return rc;
515 
516  // retry if interrupted by a signal
517  if (sock_get_error() == EINTR)
518  continue;
519 
520  qore_socket_error(xsink, "SOCKET-ACCEPT-ERROR", "error in accept()", 0, 0, 0, addr);
521  return -1;
522  }
523  }
524 
525  // returns a new socket
526  DLLLOCAL int accept_internal(ExceptionSink* xsink, SocketSource *source, int timeout_ms = -1) {
527  assert(xsink);
528  if (sock == QORE_INVALID_SOCKET) {
529  xsink->raiseException("SOCKET-NOT-OPEN", "socket must be opened, bound, and in a listening state before new connections can be accepted");
530  return QSE_NOT_OPEN;
531  }
532  if (in_op >= 0) {
533  if (in_op == gettid()) {
534  se_in_op("Socket", "accept", xsink);
535  return QSE_IN_OP;
536  }
537  se_in_op_thread("Socket", "accept", xsink);
538  return QSE_IN_OP_THREAD;
539  }
540 
541  int rc;
542  if (sfamily == AF_UNIX) {
543 #ifdef _Q_WINDOWS
544  xsink->raiseException("SOCKET-ACCEPT-ERROR", "UNIX sockets are not available under Windows");
545  return -1;
546 #else
547  hashdecl sockaddr_un addr_un;
548 
549 #if defined(HPUX) && defined(__ia64) && defined(__LP64__)
550  // on HPUX 64-bit the OS defines socklen_t to be 8 bytes
551  // but the library expects a 32-bit value
552  int size = sizeof(hashdecl sockaddr_un);
553 #else
554  socklen_t size = sizeof(hashdecl sockaddr_un);
555 #endif
556  rc = accept_intern(xsink, (struct sockaddr *)&addr_un, (socklen_t *)&size, timeout_ms);
557  //printd(1, "qore_socket_private::accept_internal() " QSD " bytes returned\n", size);
558 
559  if (rc >= 0 && source) {
560  QoreStringNode* addr = new QoreStringNode(enc);
561  addr->sprintf("UNIX socket: %s", socketname.c_str());
562  source->priv->setAddress(addr);
563  source->priv->setHostName("localhost");
564  }
565 #endif // windows
566  } else if (sfamily == AF_INET || sfamily == AF_INET6) {
567  hashdecl sockaddr_storage addr_in;
568 #if defined(HPUX) && defined(__ia64) && defined(__LP64__)
569  // on HPUX 64-bit the OS defines socklen_t to be 8 bytes
570  // but the library expects a 32-bit value
571  int size = sizeof(addr_in);
572 #else
573  socklen_t size = sizeof(addr_in);
574 #endif
575 
576  rc = accept_intern(xsink, (struct sockaddr *)&addr_in, (socklen_t *)&size, timeout_ms);
577  //printd(1, "qore_socket_private::accept_internal() rc: %d, %d bytes returned\n", rc, size);
578 
579  if (rc >= 0 && source) {
580  char host[NI_MAXHOST + 1];
581  char service[NI_MAXSERV + 1];
582 
583  if (!getnameinfo((struct sockaddr *)&addr_in, qore_get_in_len((struct sockaddr *)&addr_in), host, sizeof(host), service, sizeof(service), NI_NUMERICSERV)) {
584  source->priv->setHostName(host);
585  }
586 
587  // get ipv4 or ipv6 address
588  char ifname[INET6_ADDRSTRLEN];
589  if (inet_ntop(addr_in.ss_family, qore_get_in_addr((struct sockaddr *)&addr_in), ifname, sizeof(ifname))) {
590  //printd(5, "inet_ntop() '%s' host: '%s'\n", ifname, host);
591  source->priv->setAddress(ifname);
592  }
593  }
594  } else {
595  // should not happen
596  xsink->raiseException("SOCKET-ACCEPT-ERROR", "do not know how to accept connections with address family %d", sfamily);
597  rc = -1;
598  }
599  return rc;
600  }
601 
602  DLLLOCAL QoreHashNode* getEvent(int event, int source = QORE_SOURCE_SOCKET) const {
603  QoreHashNode* h = new QoreHashNode(autoTypeInfo);
604  if (event_arg) {
605  h->setKeyValue("arg", event_arg.refSelf(), nullptr);
606  }
607 
608  h->setKeyValue("event", event, nullptr);
609  h->setKeyValue("source", source, nullptr);
610  h->setKeyValue("id", (int64)this, nullptr);
611 
612  return h;
613  }
614 
615  DLLLOCAL void cleanup(ExceptionSink* xsink) {
616  if (event_queue) {
617  // close the socket before the delete message is put on the queue
618  // the socket would be closed anyway in the destructor
619  close_internal();
620 
621  event_queue->pushAndTakeRef(getEvent(QORE_EVENT_DELETED));
622 
623  // deref and remove event queue
624  event_queue->deref(xsink);
625  event_queue = nullptr;
626  }
627  if (warn_queue) {
628  warn_queue->deref(xsink);
629  warn_queue = nullptr;
630  if (warn_callback_arg) {
631  warn_callback_arg.discard(xsink);
632  warn_callback_arg.clear();
633  }
634  }
635  }
636 
637  DLLLOCAL void setEventQueue(ExceptionSink* xsink, Queue* q, QoreValue arg, bool with_data) {
638  if (event_queue) {
639  if (event_arg) {
640  event_arg.discard(xsink);
641  }
642  event_queue->deref(xsink);
643  }
644  event_queue = q;
645  event_arg = arg;
646  event_data = with_data;
647  }
648 
649  DLLLOCAL void do_start_ssl_event() {
650  if (event_queue) {
651  event_queue->pushAndTakeRef(getEvent(QORE_EVENT_START_SSL));
652  }
653  }
654 
655  DLLLOCAL void do_ssl_established_event() {
656  if (event_queue) {
657  QoreHashNode* h = getEvent(QORE_EVENT_SSL_ESTABLISHED);
658  h->setKeyValue("cipher", new QoreStringNode(ssl->getCipherName()), nullptr);
659  h->setKeyValue("cipher_version", new QoreStringNode(ssl->getCipherVersion()), nullptr);
660  event_queue->pushAndTakeRef(h);
661  }
662  }
663 
664  DLLLOCAL void do_connect_event(int af, const struct sockaddr* addr, const char* target, const char* service = nullptr, int prt = -1) {
665  if (event_queue) {
666  QoreHashNode* h = getEvent(QORE_EVENT_CONNECTING);
667  QoreStringNode* str = q_addr_to_string2(addr);
668  if (str) {
669  h->setKeyValue("address", str, nullptr);
670  } else {
671  h->setKeyValue("error", q_strerror(sock_get_error()), nullptr);
672  }
673  q_af_to_hash(af, *h, nullptr);
674  h->setKeyValue("target", new QoreStringNode(target), nullptr);
675  if (service)
676  h->setKeyValue("service", new QoreStringNode(service), nullptr);
677  if (prt != -1)
678  h->setKeyValue("port", prt, nullptr);
679  event_queue->pushAndTakeRef(h);
680  }
681  }
682 
683  DLLLOCAL void do_connected_event() {
684  if (event_queue) {
685  event_queue->pushAndTakeRef(getEvent(QORE_EVENT_CONNECTED));
686  }
687  }
688 
689  DLLLOCAL void do_data_event_intern(int event, int source, const QoreStringNode& str) const {
690  assert(event_queue && event_data && str.size());
691  ReferenceHolder<QoreHashNode> h(getEvent(event, source), nullptr);
692  h->setKeyValue("data", str.refSelf(), nullptr);
693  event_queue->pushAndTakeRef(h.release());
694  }
695 
696  DLLLOCAL void do_data_event(int event, int source, const QoreStringNode& str) const {
697  if (event_queue && event_data && str.size()) {
698  do_data_event_intern(event, source, str);
699  }
700  }
701 
702  DLLLOCAL void do_data_event(int event, int source, const BinaryNode& b) const {
703  if (event_queue && event_data && b.size()) {
704  ReferenceHolder<QoreHashNode> h(getEvent(event, source), nullptr);
705  h->setKeyValue("data", b.refSelf(), nullptr);
706  event_queue->pushAndTakeRef(h.release());
707  }
708  }
709 
710  DLLLOCAL void do_data_event(int event, int source, const void* data, size_t size) const {
711  if (event_queue && event_data && size) {
712  ReferenceHolder<QoreHashNode> h(getEvent(event, source), nullptr);
714  b->append(data, size);
715  h->setKeyValue("data", b.release(), nullptr);
716  event_queue->pushAndTakeRef(h.release());
717  }
718  }
719 
720  DLLLOCAL void do_header_event(int event, int source, const QoreHashNode& hdr) const {
721  if (event_queue && event_data && !hdr.empty()) {
722  ReferenceHolder<QoreHashNode> h(getEvent(event, source), nullptr);
723  h->setKeyValue("headers", hdr.refSelf(), nullptr);
724  event_queue->pushAndTakeRef(h.release());
725  }
726  }
727 
728  DLLLOCAL void do_chunked_read(int event, qore_size_t bytes, qore_size_t total_read, int source) {
729  if (event_queue) {
730  QoreHashNode* h = getEvent(event, source);
731  if (event == QORE_EVENT_HTTP_CHUNKED_DATA_RECEIVED)
732  h->setKeyValue("read", bytes, nullptr);
733  else
734  h->setKeyValue("size", bytes, nullptr);
735  h->setKeyValue("total_read", total_read, nullptr);
736  event_queue->pushAndTakeRef(h);
737  }
738  }
739 
740  DLLLOCAL void do_read_http_header(int event, const QoreHashNode* headers, int source) {
741  if (event_queue) {
742  QoreHashNode* h = getEvent(event, source);
743  h->setKeyValue("headers", headers->hashRefSelf(), nullptr);
744  event_queue->pushAndTakeRef(h);
745  }
746  }
747 
748  DLLLOCAL void do_send_http_message_event(const QoreString& str, const QoreHashNode* headers, int source) {
749  if (event_queue) {
750  QoreHashNode* h = getEvent(QORE_EVENT_HTTP_SEND_MESSAGE, source);
751  h->setKeyValue("message", new QoreStringNode(str), nullptr);
752  //printd(5, "do_send_http_message_event() str='%s' headers: %p (%d %s)\n", str.getBuffer(), headers, headers->getType(), headers->getTypeName());
753  h->setKeyValue("headers", headers->hashRefSelf(), nullptr);
754  event_queue->pushAndTakeRef(h);
755  }
756  }
757 
758  DLLLOCAL void do_close_event() {
759  if (event_queue) {
760  event_queue->pushAndTakeRef(getEvent(QORE_EVENT_CHANNEL_CLOSED));
761  }
762  }
763 
764  DLLLOCAL void do_read_event(size_t bytes_read, size_t total_read, size_t bufsize = 0, int source = QORE_SOURCE_SOCKET) {
765  // post bytes read on event queue, if any
766  if (event_queue) {
767  QoreHashNode* h = getEvent(QORE_EVENT_PACKET_READ, source);
768  h->setKeyValue("read", bytes_read, nullptr);
769  h->setKeyValue("total_read", total_read, nullptr);
770  // set total bytes to read and remaining bytes if bufsize > 0
771  if (bufsize > 0)
772  h->setKeyValue("total_to_read", bufsize, nullptr);
773  event_queue->pushAndTakeRef(h);
774  }
775  }
776 
777  DLLLOCAL void do_send_event(int bytes_sent, int total_sent, int bufsize) {
778  // post bytes sent on event queue, if any
779  if (event_queue) {
780  QoreHashNode* h = getEvent(QORE_EVENT_PACKET_SENT);
781  h->setKeyValue("sent", bytes_sent, nullptr);
782  h->setKeyValue("total_sent", total_sent, nullptr);
783  h->setKeyValue("total_to_send", bufsize, nullptr);
784  event_queue->pushAndTakeRef(h);
785  }
786  }
787 
788  DLLLOCAL void do_resolve_event(const char* host, const char* service = 0) {
789  // post bytes sent on event queue, if any
790  if (event_queue) {
791  QoreHashNode* h = getEvent(QORE_EVENT_HOSTNAME_LOOKUP);
792  if (host)
793  h->setKeyValue("name", new QoreStringNode(host), nullptr);
794  if (service)
795  h->setKeyValue("service", new QoreStringNode(service), nullptr);
796  event_queue->pushAndTakeRef(h);
797  }
798  }
799 
800  DLLLOCAL void do_resolved_event(const struct sockaddr* addr) {
801  // post bytes sent on event queue, if any
802  if (event_queue) {
803  QoreHashNode* h = getEvent(QORE_EVENT_HOSTNAME_RESOLVED);
804  QoreStringNode* str = q_addr_to_string2(addr);
805  if (str)
806  h->setKeyValue("address", str, nullptr);
807  else
808  h->setKeyValue("error", q_strerror(sock_get_error()), nullptr);
809  int prt = q_get_port_from_addr(addr);
810  if (prt > 0)
811  h->setKeyValue("port", prt, nullptr);
812  q_af_to_hash(addr->sa_family, *h, nullptr);
813  event_queue->pushAndTakeRef(h);
814  }
815  }
816 
817  DLLLOCAL int64 getObjectIDForEvents() const {
818  return (int64)this;
819  }
820 
821  DLLLOCAL int connectUNIX(const char* p, int sock_type, int protocol, ExceptionSink* xsink) {
822  assert(xsink);
823  assert(p);
824  QORE_TRACE("connectUNIX()");
825 
826 #ifdef _Q_WINDOWS
827  xsink->raiseException("SOCKET-CONNECTUNIX-ERROR", "UNIX sockets are not available under Windows");
828  return -1;
829 #else
830  // close socket if already open
831  close();
832 
833  printd(5, "qore_socket_private::connectUNIX(%s)\n", p);
834 
835  hashdecl sockaddr_un addr;
836 
837  addr.sun_family = AF_UNIX;
838  // copy path and terminate if necessary
839  strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1);
840  addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
841  if ((sock = socket(AF_UNIX, sock_type, protocol)) == QORE_SOCKET_ERROR) {
842  xsink->raiseErrnoException("SOCKET-CONNECT-ERROR", errno, "error connecting to UNIX socket: '%s'", p);
843  return -1;
844  }
845 
846  do_connect_event(AF_UNIX, (sockaddr*)&addr, p);
847  while (true) {
848  if (!::connect(sock, (const sockaddr *)&addr, sizeof(struct sockaddr_un)))
849  break;
850 
851  // try again if we were interrupted by a signal
852  if (sock_get_error() == EINTR)
853  continue;
854 
855  // otherwise close the socket and return an exception with the error code
856  // do not have to worry about windows API calls here; this is a UNIX-only function
857  close_and_reset();
858  qore_socket_error(xsink, "SOCKET-CONNECT-ERROR", "error in connect()", 0, p);
859 
860  return -1;
861  }
862 
863  // save file name for deleting when socket is closed
864  socketname = addr.sun_path;
865  sfamily = AF_UNIX;
866 
867  do_connected_event();
868 
869  return 0;
870 #endif // windows
871  }
872 
873  // socket must be open or -1 is returned and a Qore-language exception is raised
874  /* return values:
875  -1: error
876  0: timeout
877  > 0: I/O can continue
878  */
879  DLLLOCAL int asyncIoWait(int timeout_ms, bool read, bool write, const char* cname, const char* mname, ExceptionSink* xsink) const {
880  assert(xsink);
881  assert(read || write);
882  if (sock == QORE_INVALID_SOCKET) {
883  se_not_open(cname, mname, xsink);
884  return -1;
885  }
886 
887  return asyncIoWait(timeout_ms, read, write, xsink);
888  }
889 
890  DLLLOCAL int asyncIoWait(int timeout_ms, bool read, bool write, ExceptionSink* xsink) const {
891  assert(xsink);
892 #if defined HAVE_POLL
893  return poll_intern(xsink, timeout_ms, read, write);
894 #elif defined HAVE_SELECT
895  return select_intern(xsink, timeout_ms, read, write);
896 #else
897 #error no async socket operations supported
898 #endif
899  }
900 
901 #if defined HAVE_POLL
902  DLLLOCAL int poll_intern(ExceptionSink* xsink, int timeout_ms, bool read, bool write) const {
903  int rc;
904  short arg = 0;
905  if (read)
906  arg |= POLLIN;
907  if (write)
908  arg |= POLLOUT;
909  pollfd fds = {sock, arg, 0};
910  while (true) {
911  rc = poll(&fds, 1, timeout_ms);
912  if (rc == -1 && errno == EINTR)
913  continue;
914  break;
915  }
916  if (rc < 0)
917  qore_socket_error(xsink, "SOCKET-SELECT-ERROR", "poll(2) returned an error");
918  else if (!rc && ((fds.revents & POLLHUP) || (fds.revents & (POLLERR|POLLNVAL))))
919  rc = -1;
920 
921  return rc;
922  }
923 #elif defined HAVE_SELECT
924  DLLLOCAL int select_intern(ExceptionSink* xsink, int timeout_ms, bool read, bool write) const {
925  bool aborted = false;
926  int rc = select_intern(xsink, timeout_ms, read, write, aborted);
927  if (rc != QORE_SOCKET_ERROR && aborted)
928  rc = -1;
929  return rc;
930  }
931 
932  DLLLOCAL int select_intern(ExceptionSink* xsink, int timeout_ms, bool read, bool write, bool& aborted) const {
933  assert(xsink);
934  assert(!aborted);
935  // windows does not use FD_SETSIZE to limit the value of the highest socket descriptor in the set
936  // instead it has a maximum of 64 sockets in the set; we only need one anyway
937 #ifndef _Q_WINDOWS
938  // select is inherently broken since it can only handle descriptors < FD_SETSIZE, which is 1024 on Linux for example
939  if (sock >= FD_SETSIZE) {
940  xsink->raiseException("SOCKET-SELECT-ERROR", "fd is %d which is >= %d; contact the Qore developers to implement an alternative to select() on this platform", sock, FD_SETSIZE);
941  return -1;
942  }
943 #endif
944  hashdecl timeval tv;
945  int rc;
946  while (true) {
947  // to be safe, we set the file descriptor arg after each EINTR (required on Linux for example)
948  fd_set sfs, err;
949 
950  FD_ZERO(&sfs);
951  FD_ZERO(&err);
952  FD_SET(sock, &sfs);
953  FD_SET(sock, &err);
954 
955  tv.tv_sec = timeout_ms / 1000;
956  tv.tv_usec = (timeout_ms % 1000) * 1000;
957 
958  fd_set* readfd = read ? &sfs : 0;
959  fd_set* writefd = write ? &sfs : 0;
960 
961  rc = select(sock + 1, readfd, writefd, &err, &tv);
962  //printd(5, "select_intern() rc: %d err: %d\n", rc, FD_ISSET(sock, &err));
963  if (rc != QORE_SOCKET_ERROR) {
964  if (FD_ISSET(sock, &err))
965  aborted = true;
966  break;
967  }
968  if (sock_get_error() != EINTR)
969  break;
970  }
971  if (rc == QORE_SOCKET_ERROR) {
972  // do not close the socket here, even in case of EBADF, just return an error
973  rc = 0;
974  qore_socket_error(xsink, "SOCKET-SELECT-ERROR", "select(2) returned an error");
975  }
976 
977  return rc;
978  }
979 #endif
980 
981  DLLLOCAL bool tryReadSocketData(const char* mname, ExceptionSink* xsink) {
982  assert(xsink);
983  assert(!buflen);
984  if (!ssl) {
985  // issue #3564: see if any data is available on the socket
986  return asyncIoWait(0, true, false, "Socket", mname, xsink);
987  }
988  // select can return true if there is protocol negotiation data available,
989  // so we try to peek 1 byte of application data with a timeout of 0 with the SSL connection
990  int rc = ssl->doSSLRW(xsink, mname, rbuf, 1, 0, PEEK, false);
991  if (*xsink || (rc == QSE_TIMEOUT)) {
992  return false;
993  }
994  return rc > 0 ? true : false;
995  }
996 
997  DLLLOCAL bool isSocketDataAvailable(int timeout_ms, const char* mname, ExceptionSink* xsink) {
998  return asyncIoWait(timeout_ms, true, false, "Socket", mname, xsink);
999  }
1000 
1001  DLLLOCAL bool isDataAvailable(int timeout_ms, const char* mname, ExceptionSink* xsink) {
1002  if (buflen)
1003  return true;
1004  return isSocketDataAvailable(timeout_ms, mname, xsink);
1005  }
1006 
1007  DLLLOCAL bool isWriteFinished(int timeout_ms, const char* mname, ExceptionSink* xsink) {
1008  return asyncIoWait(timeout_ms, false, true, "Socket", mname, xsink);
1009  }
1010 
1011  DLLLOCAL int close_and_exit() {
1012  if (sock != QORE_INVALID_SOCKET)
1013  close_and_reset();
1014  return -1;
1015  }
1016 
1017  DLLLOCAL int connectINETTimeout(int timeout_ms, const struct sockaddr* ai_addr, qore_size_t ai_addrlen, ExceptionSink* xsink, bool only_timeout) {
1018  assert(xsink);
1019  PrivateQoreSocketTimeoutHelper toh(this, "connect");
1020 
1021  while (true) {
1022  if (!::connect(sock, ai_addr, ai_addrlen))
1023  return 0;
1024 
1025 #ifdef _Q_WINDOWS
1026  if (sock_get_error() != EAGAIN) {
1027  qore_socket_error(xsink, "SOCKET-CONNECT-ERROR", "error in connect()", 0, 0, 0, ai_addr);
1028  break;
1029  }
1030 #else
1031  // try again if we were interrupted by a signal
1032  if (errno == EINTR)
1033  continue;
1034 
1035  if (errno != EINPROGRESS)
1036  break;
1037 #endif
1038 
1039  //printd(5, "qore_socket_private::connectINETTimeout() timeout_ms: %d errno: %d\n", timeout_ms, errno);
1040 
1041  // check for timeout or connection with EINPROGRESS
1042  while (true) {
1043 #ifdef _Q_WINDOWS
1044  bool aborted = false;
1045  int rc = select_intern(xsink, timeout_ms, false, true, aborted);
1046 
1047  //printd(5, "qore_socket_private::connectINETTimeout() timeout_ms: %d rc: %d aborted: %d\n", timeout_ms, rc, aborted);
1048 
1049  // windows select() returns an error in the error socket set instead of an WSAECONNREFUSED error like UNIX,
1050  // so we simulate it here
1051  if (rc != QORE_SOCKET_ERROR && aborted) {
1052  qore_socket_error(xsink, "SOCKET-CONNECT-ERROR", "error in connect()", 0, 0, 0, ai_addr);
1053  return -1;
1054  }
1055 #else
1056  int rc = asyncIoWait(timeout_ms, false, true, "Socket", "connectINETTimeout", xsink);
1057 #endif
1058  if (*xsink)
1059  return -1;
1060 
1061  //printd(5, "asyncIoWait(%d) returned %d\n", timeout_ms, rc);
1062  if (rc == QORE_SOCKET_ERROR && sock_get_error() != EINTR) {
1063  if (!only_timeout)
1064  qore_socket_error(xsink, "SOCKET-CONNECT-ERROR", "error in asyncIoWait() with Socket::connect() with timeout", 0, 0, 0, ai_addr);
1065  return -1;
1066  } else if (rc > 0) {
1067  // socket selected for write
1068  socklen_t lon = sizeof(int);
1069  int val;
1070 
1071  if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (GETSOCKOPT_ARG_4)(&val), &lon) == QORE_SOCKET_ERROR) {
1072  if (!only_timeout)
1073  qore_socket_error(xsink, "SOCKET-CONNECT-ERROR", "error in getsockopt()", 0, 0, 0, ai_addr);
1074  return -1;
1075  }
1076 
1077  if (val) {
1078  if (only_timeout) {
1079  errno = val;
1080  return -1;
1081  }
1082  qore_socket_error_intern(val, xsink, "SOCKET-CONNECT-ERROR", "error in getsockopt()", 0, 0, 0, ai_addr);
1083  return -1;
1084  }
1085 
1086  // connected successfully within the timeout period
1087  return 0;
1088  } else {
1089  SimpleRefHolder<QoreStringNode> desc(new QoreStringNodeMaker("timeout in connection after %dms", timeout_ms));
1090  concat_target(*(*desc), ai_addr);
1091  xsink->raiseException("SOCKET-CONNECT-ERROR", desc.release());
1092  return -1;
1093  }
1094  }
1095  }
1096 
1097  return -1;
1098  }
1099 
1100  DLLLOCAL int sock_errno_err(const char* err, const char* desc, ExceptionSink* xsink) {
1101  sock = QORE_INVALID_SOCKET;
1102  qore_socket_error(xsink, err, desc);
1103  return -1;
1104  }
1105 
1106  DLLLOCAL int set_non_blocking(bool non_blocking, ExceptionSink* xsink) {
1107  assert(xsink);
1108 
1109  // ignore call when socket already closed
1110  if (sock == QORE_INVALID_SOCKET) {
1111  assert(!xsink || *xsink);
1112  return -1;
1113  }
1114 
1115 #ifdef _Q_WINDOWS
1116  u_long mode = non_blocking ? 1 : 0;
1117  int rc = ioctlsocket(sock, FIONBIO, &mode);
1118  if (check_windows_rc(rc))
1119  return sock_errno_err("SOCKET-CONNECT-ERROR", "error in ioctlsocket(FIONBIO)", xsink);
1120 #else
1121  int arg;
1122 
1123  // get socket descriptor status flags
1124  if ((arg = fcntl(sock, F_GETFL, 0)) < 0)
1125  return sock_errno_err("SOCKET-CONNECT-ERROR", "error in fcntl() getting socket descriptor status flag", xsink);
1126 
1127  if (non_blocking) // set non-blocking
1128  arg |= O_NONBLOCK;
1129  else // set blocking
1130  arg &= ~O_NONBLOCK;
1131 
1132  if (fcntl(sock, F_SETFL, arg) < 0)
1133  return sock_errno_err("SOCKET-CONNECT-ERROR", "error in fcntl() setting socket descriptor status flag", xsink);
1134 #endif
1135 
1136  return 0;
1137  }
1138 
1139  DLLLOCAL int connectINET(const char* host, const char* service, int timeout_ms, ExceptionSink* xsink, int family = AF_UNSPEC, int type = SOCK_STREAM, int protocol = 0) {
1140  assert(xsink);
1141  family = q_get_af(family);
1142  type = q_get_sock_type(type);
1143 
1144  QORE_TRACE("qore_socket_private::connectINET()");
1145 
1146  // close socket if already open
1147  close();
1148 
1149  printd(5, "qore_socket_private::connectINET(%s:%s, %dms)\n", host, service, timeout_ms);
1150 
1151  do_resolve_event(host, service);
1152 
1153  QoreAddrInfo ai;
1154  if (ai.getInfo(xsink, host, service, family, 0, type, protocol))
1155  return -1;
1156 
1157  hashdecl addrinfo *aip = ai.getAddrInfo();
1158 
1159  // emit all "resolved" events
1160  if (event_queue)
1161  for (struct addrinfo *p = aip; p; p = p->ai_next)
1162  do_resolved_event(p->ai_addr);
1163 
1164  int prt = q_get_port_from_addr(aip->ai_addr);
1165 
1166  for (struct addrinfo *p = aip; p; p = p->ai_next) {
1167  if (!connectINETIntern(host, service, p->ai_family, p->ai_addr, p->ai_addrlen, p->ai_socktype, p->ai_protocol, prt, timeout_ms, xsink, true))
1168  return 0;
1169  if (*xsink)
1170  break;
1171  }
1172 
1173  if (!*xsink)
1174  qore_socket_error(xsink, "SOCKET-CONNECT-ERROR", "error in connect()", 0, host, service);
1175  return -1;
1176  }
1177 
1178  DLLLOCAL int connectINETIntern(const char* host, const char* service, int ai_family, struct sockaddr* ai_addr, size_t ai_addrlen, int ai_socktype, int ai_protocol, int prt, int timeout_ms, ExceptionSink* xsink, bool only_timeout = false) {
1179  assert(xsink);
1180  printd(5, "qore_socket_private::connectINETIntern() host: %s service: %s family: %d timeout_ms: %d\n", host, service, ai_family, timeout_ms);
1181  if ((sock = socket(ai_family, ai_socktype, ai_protocol)) == QORE_INVALID_SOCKET) {
1182  xsink->raiseErrnoException("SOCKET-CONNECT-ERROR", errno, "cannot establish a connection to %s:%s", host, service);
1183  return -1;
1184  }
1185 
1186  //printd(5, "qore_socket_private::connectINETIntern(this: %p, host='%s', port: %d, timeout_ms: %d) sock: %d\n", this, host, port, timeout_ms, sock);
1187 
1188  int rc;
1189 
1190  // perform connect with timeout if a non-negative timeout was passed
1191  if (timeout_ms >= 0) {
1192  // set non-blocking
1193  if (set_non_blocking(true, xsink))
1194  return close_and_exit();
1195 
1196  do_connect_event(ai_family, ai_addr, host, service, prt);
1197 
1198  rc = connectINETTimeout(timeout_ms, ai_addr, ai_addrlen, xsink, only_timeout);
1199  //printd(5, "qore_socket_private::connectINETIntern() errno: %d rc: %d, xsink: %d\n", errno, rc, xsink && *xsink);
1200 
1201  // set blocking
1202  if (set_non_blocking(false, xsink))
1203  return close_and_exit();
1204  } else {
1205  do_connect_event(ai_family, ai_addr, host, service, prt);
1206 
1207  while (true) {
1208  rc = ::connect(sock, ai_addr, ai_addrlen);
1209 
1210  // try again if rc == -1 and errno == EINTR
1211  if (!rc || sock_get_error() != EINTR)
1212  break;
1213  }
1214  }
1215 
1216  if (rc < 0) {
1217  if (!only_timeout || errno == ETIMEDOUT)
1218  qore_socket_error(xsink, "SOCKET-CONNECT-ERROR", "error in connect()", 0, host, service);
1219 
1220  return close_and_exit();
1221  }
1222 
1223  sfamily = ai_family;
1224  stype = ai_socktype;
1225  sprot = ai_protocol;
1226  port = prt;
1227  //printd(5, "qore_socket_private::connectINETIntern(this: %p, host='%s', port: %d, timeout_ms: %d) success, rc: %d, sock: %d\n", this, host, port, timeout_ms, rc, sock);
1228 
1229  do_connected_event();
1230 
1231  // issue #3053: save hostname for SNI
1232  client_target = host;
1233  return 0;
1234  }
1235 
1236  DLLLOCAL int upgradeClientToSSLIntern(const char* mname, const char* sni_target_host, X509* cert, EVP_PKEY* pkey, int timeout_ms, ExceptionSink* xsink) {
1237  assert(!ssl);
1238  SSLSocketHelperHelper sshh(this, true);
1239 
1240  int rc;
1241  do_start_ssl_event();
1242  // issue #3053: send target hostname to support SNI
1243  if (!sni_target_host && !client_target.empty()) {
1244  sni_target_host = client_target.c_str();
1245  }
1246  if ((rc = ssl->setClient(mname, sni_target_host, sock, cert, pkey, xsink)) || ssl->connect(mname, timeout_ms, xsink)) {
1247  sshh.error();
1248  return rc ? rc : -1;
1249  }
1250  do_ssl_established_event();
1251 
1252  return 0;
1253  }
1254 
1255  DLLLOCAL int upgradeServerToSSLIntern(const char* mname, X509* cert, EVP_PKEY* pkey, int timeout_ms, ExceptionSink* xsink) {
1256  assert(!ssl);
1257  //printd(5, "qore_socket_private::upgradeServerToSSLIntern() this: %p mode: %d\n", this, ssl_verify_mode);
1258  SSLSocketHelperHelper sshh(this, true);
1259 
1260  do_start_ssl_event();
1261  if (ssl->setServer(mname, sock, cert, pkey, xsink) || ssl->accept(mname, timeout_ms, xsink)) {
1262  sshh.error();
1263  return -1;
1264  }
1265  do_ssl_established_event();
1266 
1267  return 0;
1268  }
1269 
1270  // returns 0 = success, -1 = error
1271  DLLLOCAL int openUNIX(int sock_type = SOCK_STREAM, int protocol = 0) {
1272  if (sock != QORE_INVALID_SOCKET)
1273  close();
1274 
1275  if ((sock = socket(AF_UNIX, sock_type, protocol)) == QORE_INVALID_SOCKET) {
1276  return -1;
1277  }
1278 
1279  sfamily = AF_UNIX;
1280  stype = sock_type;
1281  sprot = protocol;
1282  port = -1;
1283  return 0;
1284  }
1285 
1286  // returns 0 = success, -1 = error
1287  DLLLOCAL int openINET(int family = AF_INET, int sock_type = SOCK_STREAM, int protocol = 0) {
1288  if (sock != QORE_INVALID_SOCKET)
1289  close();
1290 
1291  if ((sock = socket(family, sock_type, protocol)) == QORE_INVALID_SOCKET)
1292  return -1;
1293 
1294  sfamily = family;
1295  stype = sock_type;
1296  sprot = protocol;
1297  port = -1;
1298  return 0;
1299  }
1300 
1301  DLLLOCAL int reuse(int opt) {
1302  //printf("qore_socket_private::reuse(%s)\n", opt ? "true" : "false");
1303  return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (SETSOCKOPT_ARG_4)&opt, sizeof(int));
1304  }
1305 
1306  // the only place where xsink is optional
1307  DLLLOCAL int bindIntern(struct sockaddr* ai_addr, size_t ai_addrlen, int prt, bool reuseaddr, ExceptionSink* xsink = 0) {
1308  reuse(reuseaddr);
1309 
1310  if ((::bind(sock, ai_addr, ai_addrlen)) == QORE_SOCKET_ERROR) {
1311  if (xsink)
1312  qore_socket_error(xsink, "SOCKET-BIND-ERROR", "error in bind()", 0, 0, 0, ai_addr);
1313  close();
1314  return -1;
1315  }
1316 
1317  // set port number
1318  if (prt)
1319  port = prt;
1320  else {
1321  // get port number
1322 #if defined(HPUX) && defined(__ia64) && defined(__LP64__)
1323  // on HPUX 64-bit the OS defines socklen_t to be 8 bytes, but the library expects a 32-bit value
1324  int len = ai_addrlen;
1325 #else
1326  socklen_t len = ai_addrlen;
1327 #endif
1328 
1329  if (getsockname(sock, ai_addr, &len))
1330  port = -1;
1331  else
1332  port = q_get_port_from_addr(ai_addr);
1333  }
1334  return 0;
1335  }
1336 
1337  // bind to UNIX domain socket file
1338  DLLLOCAL int bindUNIX(ExceptionSink* xsink, const char* name, int socktype = SOCK_STREAM, int protocol = 0) {
1339  assert(xsink);
1340 #ifdef _Q_WINDOWS
1341  xsink->raiseException("SOCKET-BINDUNIX-ERROR", "UNIX sockets are not available under Windows");
1342  return -1;
1343 #else
1344  close();
1345 
1346  // try to open socket if necessary
1347  if (openUNIX(socktype, protocol)) {
1348  xsink->raiseErrnoException("SOCKET-BIND-ERROR", errno, "error opening UNIX socket ('%s') for bind", name);
1349  return -1;
1350  }
1351 
1352  hashdecl sockaddr_un addr;
1353  addr.sun_family = AF_UNIX;
1354  // copy path and terminate if necessary
1355  strncpy(addr.sun_path, name, sizeof(addr.sun_path) - 1);
1356  addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
1357 
1358  if (bindIntern((sockaddr*)&addr, sizeof(struct sockaddr_un), -1, false, xsink))
1359  return -1;
1360 
1361  // save socket file name for deleting on close
1362  socketname = addr.sun_path;
1363  // delete UNIX domain socket on close
1364  del = true;
1365  return 0;
1366 #endif // windows
1367  }
1368 
1369  DLLLOCAL int bindINET(ExceptionSink* xsink, const char* name, const char* service, bool reuseaddr = true, int family = AF_UNSPEC, int socktype = SOCK_STREAM, int protocol = 0) {
1370  assert(xsink);
1371  family = q_get_af(family);
1372  socktype = q_get_sock_type(socktype);
1373 
1374  close();
1375 
1376  QoreAddrInfo ai;
1377  do_resolve_event(name, service);
1378  if (ai.getInfo(xsink, name, service, family, AI_PASSIVE, socktype, protocol))
1379  return -1;
1380 
1381  hashdecl addrinfo *aip = ai.getAddrInfo();
1382  // first emit all "resolved" events
1383  if (event_queue)
1384  for (struct addrinfo *p = aip; p; p = p->ai_next)
1385  do_resolved_event(p->ai_addr);
1386 
1387  // try to open socket if necessary
1388  if (openINET(aip->ai_family, aip->ai_socktype, protocol)) {
1389  qore_socket_error(xsink, "SOCKET-BINDINET-ERROR", "error opening socket for bind", 0, name, service);
1390  return -1;
1391  }
1392 
1393  int prt = q_get_port_from_addr(aip->ai_addr);
1394 
1395  int en = 0;
1396  // iterate through addresses and bind to the first interface possible
1397  for (struct addrinfo *p = aip; p; p = p->ai_next) {
1398  if (!bindIntern(p->ai_addr, p->ai_addrlen, prt, reuseaddr)) {
1399  //printd(5, "qore_socket_private::bindINET(family: %d) bound: name: %s service: %s f: %d st: %d p: %d\n", family, name ? name : "(null)", service ? service : "(null)", p->ai_family, p->ai_socktype, p->ai_protocol);
1400  return 0;
1401  }
1402 
1403  en = sock_get_raw_error();
1404  //printd(5, "qore_socket_private::bindINET() failed to bind: name: %s service: %s f: %d st: %d p: %d, errno: %d (%s)\n", name ? name : "(null)", service ? service : "(null)", p->ai_family, p->ai_socktype, p->ai_protocol, en, strerror(en));
1405  }
1406 
1407  // if no bind was possible, then raise an exception
1408  qore_socket_error_intern(en, xsink, "SOCKET-BIND-ERROR", "error binding on socket", 0, name, service);
1409  return -1;
1410  }
1411 
1412  // only called from qore-bound code - always with xsink
1413  DLLLOCAL QoreHashNode* getPeerInfo(ExceptionSink* xsink, bool host_lookup = true) const {
1414  assert(xsink);
1415  if (sock == QORE_INVALID_SOCKET) {
1416  se_not_open("Socket", "getPeerInfo", xsink);
1417  return 0;
1418  }
1419 
1420  hashdecl sockaddr_storage addr;
1421  socklen_t len = sizeof addr;
1422  if (getpeername(sock, (struct sockaddr*)&addr, &len)) {
1423  qore_socket_error(xsink, "SOCKET-GETPEERINFO-ERROR", "error in getpeername()");
1424  return 0;
1425  }
1426 
1427  return getAddrInfo(addr, len, host_lookup);
1428  }
1429 
1430  // only called from qore-bound code - always with xsink
1431  DLLLOCAL QoreHashNode* getSocketInfo(ExceptionSink* xsink, bool host_lookup = true) const {
1432  assert(xsink);
1433  if (sock == QORE_INVALID_SOCKET) {
1434  se_not_open("Socket", "getSocketInfo", xsink);
1435  return 0;
1436  }
1437 
1438  hashdecl sockaddr_storage addr;
1439 #if defined(HPUX) && defined(__ia64) && defined(__LP64__)
1440  // on HPUX 64-bit the OS defines socklen_t to be 8 bytes, but the library expects a 32-bit value
1441  int len = sizeof addr;
1442 #else
1443  socklen_t len = sizeof addr;
1444 #endif
1445 
1446  if (getsockname(sock, (struct sockaddr*)&addr, &len)) {
1447  qore_socket_error(xsink, "SOCKET-GETSOCKETINFO-ERROR", "error in getsockname()");
1448  return 0;
1449  }
1450 
1451  return getAddrInfo(addr, len, host_lookup);
1452  }
1453 
1454  DLLLOCAL QoreHashNode* getAddrInfo(const struct sockaddr_storage& addr, socklen_t len, bool host_lookup = true) const {
1455  QoreHashNode* h = new QoreHashNode(autoTypeInfo);
1456 
1457  if (addr.ss_family == AF_INET || addr.ss_family == AF_INET6) {
1458  if (host_lookup) {
1459  char host[NI_MAXHOST + 1];
1460 
1461  if (!getnameinfo((struct sockaddr*)&addr, qore_get_in_len((struct sockaddr*)&addr), host, sizeof(host), 0, 0, 0)) {
1462  QoreStringNode* hoststr = new QoreStringNode(host);
1463  h->setKeyValue("hostname", hoststr, 0);
1464  h->setKeyValue("hostname_desc", QoreAddrInfo::getAddressDesc(addr.ss_family, hoststr->getBuffer()), 0);
1465  }
1466  }
1467 
1468  // get ipv4 or ipv6 address
1469  char ifname[INET6_ADDRSTRLEN];
1470  if (inet_ntop(addr.ss_family, qore_get_in_addr((struct sockaddr*)&addr), ifname, sizeof(ifname))) {
1471  QoreStringNode* addrstr = new QoreStringNode(ifname);
1472  h->setKeyValue("address", addrstr, 0);
1473  h->setKeyValue("address_desc", QoreAddrInfo::getAddressDesc(addr.ss_family, addrstr->getBuffer()), 0);
1474  }
1475 
1476  int tport;
1477  if (addr.ss_family == AF_INET) {
1478  hashdecl sockaddr_in* s = (hashdecl sockaddr_in*)&addr;
1479  tport = ntohs(s->sin_port);
1480  } else {
1481  hashdecl sockaddr_in6* s = (hashdecl sockaddr_in6*)&addr;
1482  tport = ntohs(s->sin6_port);
1483  }
1484 
1485  h->setKeyValue("port", tport, 0);
1486  }
1487 #ifndef _Q_WINDOWS
1488  else if (addr.ss_family == AF_UNIX) {
1489  assert(!socketname.empty());
1490  QoreStringNode* addrstr = new QoreStringNode(socketname);
1491  h->setKeyValue("address", addrstr, 0);
1492  h->setKeyValue("address_desc", QoreAddrInfo::getAddressDesc(addr.ss_family, addrstr->getBuffer()), 0);
1493  }
1494 #endif
1495 
1496  h->setKeyValue("family", addr.ss_family, 0);
1497  h->setKeyValue("familystr", new QoreStringNode(QoreAddrInfo::getFamilyName(addr.ss_family)), 0);
1498 
1499  return h;
1500  }
1501 
1502  // set backwards-compatible object members on accept
1503  // to be (hopefully) deleted in a future version of qore
1504  DLLLOCAL void setAccept(QoreObject* o) {
1505  hashdecl sockaddr_storage addr;
1506 
1507  socklen_t len = sizeof addr;
1508  if (getpeername(sock, (struct sockaddr*)&addr, &len))
1509  return;
1510 
1511  if (addr.ss_family == AF_INET || addr.ss_family == AF_INET6) {
1512  // get ipv4 or ipv6 address
1513  char ifname[INET6_ADDRSTRLEN];
1514  if (inet_ntop(addr.ss_family, qore_get_in_addr((struct sockaddr *)&addr), ifname, sizeof(ifname))) {
1515  //printd(5, "inet_ntop() '%s' host: '%s'\n", ifname, host);
1516  o->setValue("source", new QoreStringNode(ifname), 0);
1517  }
1518 
1519  char host[NI_MAXHOST + 1];
1520  if (!getnameinfo((struct sockaddr *)&addr, qore_get_in_len((struct sockaddr *)&addr), host, sizeof(host), 0, 0, 0))
1521  o->setValue("source_host", new QoreStringNode(host), 0);
1522  }
1523 #ifndef _Q_WINDOWS
1524  else if (addr.ss_family == AF_UNIX) {
1525  QoreStringNode* astr = new QoreStringNode(enc);
1526  hashdecl sockaddr_un *addr_un = (hashdecl sockaddr_un *)&addr;
1527  astr->sprintf("UNIX socket: %s", addr_un->sun_path);
1528  o->setValue("source", astr, 0);
1529  o->setValue("source_host", new QoreStringNode("localhost"), 0);
1530  }
1531 #endif
1532  }
1533 
1534  // buffered reads for high performance
1535  DLLLOCAL qore_offset_t brecv(ExceptionSink* xsink, const char* meth, char*& buf, qore_size_t bs, int flags, int timeout, bool do_event = true) {
1536  assert(xsink);
1537  // must be checked if open/connected before this function is called
1538  assert(sock != QORE_INVALID_SOCKET);
1539  assert(meth);
1540 
1541  // always returned buffered data first
1542  if (buflen) {
1543  buf = rbuf + bufoffset;
1544  if (buflen <= bs) {
1545  bs = buflen;
1546  buflen = 0;
1547  bufoffset = 0;
1548  } else {
1549  buflen -= bs;
1550  bufoffset += bs;
1551  }
1552  return (qore_offset_t)bs;
1553  }
1554 
1555  // real socket reads are only done when the buffer is empty
1556 
1557  //printd(5, "qore_socket_private::brecv(buf: %p, bs: %d, flags: %d, timeout: %d, do_event: %d) this: %p ssl: %d\n", buf, (int)bs, flags, timeout, (int)do_event, this, ssl);
1558 
1559  qore_offset_t rc;
1560  if (!ssl) {
1561  if (timeout != -1 && !isDataAvailable(timeout, meth, xsink)) {
1562  if (*xsink)
1563  return -1;
1564  se_timeout("Socket", meth, timeout, xsink);
1565 
1566  return QSE_TIMEOUT;
1567  }
1568 
1569  while (true) {
1570 #ifdef DEBUG
1571  errno = 0;
1572 #endif
1573  rc = ::recv(sock, rbuf, DEFAULT_SOCKET_BUFSIZE, flags);
1574  if (rc == QORE_SOCKET_ERROR) {
1575  sock_get_error();
1576  if (errno == EINTR)
1577  continue;
1578 #ifdef ECONNRESET
1579  if (errno == ECONNRESET) {
1580  se_closed("Socket", meth, xsink);
1581  close();
1582  } else
1583 #endif
1584  qore_socket_error(xsink, "SOCKET-RECV-ERROR", "error in recv()", meth);
1585  break;
1586  }
1587  //printd(5, "qore_socket_private::brecv(%d, %p, %ld, %d) rc: %ld errno: %d\n", sock, buf, bs, flags, rc, errno);
1588  // try again if we were interrupted by a signal
1589  if (rc >= 0)
1590  break;
1591  }
1592  } else
1593  rc = ssl->read(meth, rbuf, DEFAULT_SOCKET_BUFSIZE, timeout, xsink);
1594 
1595  //printd(5, "qore_socket_private::brecv(%d, %p, %ld, %d) rc: %ld errno: %d\n", sock, buf, bs, flags, rc, errno);
1596  if (rc > 0) {
1597  buf = rbuf;
1598  assert(!buflen);
1599  assert(!bufoffset);
1600  if (rc > (qore_offset_t)bs) {
1601  buflen = rc - bs;
1602  bufoffset = bs;
1603  rc = bs;
1604  }
1605 
1606  // register event
1607  if (do_event)
1608  do_read_event(rc, rc);
1609  } else {
1610 #ifdef DEBUG
1611  buf = 0;
1612 #endif
1613  if (!rc)
1614  close();
1615  }
1616 
1617  return rc;
1618  }
1619 
1621  DLLLOCAL QoreStringNode* readHTTPData(ExceptionSink* xsink, const char* meth, int timeout, qore_offset_t& rc, bool exit_early = false) {
1622  assert(xsink);
1623  assert(meth);
1624  if (sock == QORE_INVALID_SOCKET) {
1625  se_not_open("Socket", meth, xsink);
1626  rc = QSE_NOT_OPEN;
1627  return 0;
1628  }
1629 
1630  PrivateQoreSocketThroughputHelper th(this, false);
1631 
1632  // state:
1633  // 0 = '\r' received
1634  // 1 = '\r\n' received
1635  // 2 = '\r\n\r' received
1636  // 3 = '\n' received
1637  // read in HHTP header until \r\n\r\n or \n\n from socket
1638  int state = -1;
1639  QoreStringNodeHolder hdr(new QoreStringNode(enc));
1640 
1641  qore_size_t count = 0;
1642 
1643  while (true) {
1644  char* buf;
1645  rc = brecv(xsink, meth, buf, 1, 0, timeout, false);
1646  //printd(5, "qore_socket_private::readHTTPData() this: %p Socket::%s(): rc: " QLLD " read char: %c (%03d) (old state: %d)\n", this, meth, rc, rc > 0 && buf[0] > 31 ? buf[0] : '?', rc > 0 ? buf[0] : 0, state);
1647  if (rc <= 0) {
1648  //printd(5, "qore_socket_private::readHTTPData(timeout: %d) hdr='%s' (len: %d), rc=" QSD ", errno: %d: '%s'\n", timeout, hdr->getBuffer(), hdr->strlen(), rc, errno, strerror(errno));
1649 
1650  if (!*xsink) {
1651  if (!count) {
1652  //printd(5, "qore_socket_private::readHTTPData() this: %p rc: %d count: %d (%d) timeout: %d\n", this, rc, count, hdr->size(), timeout);
1653  se_closed("Socket", meth, xsink);
1654  } else {
1655  xsink->raiseExceptionArg("SOCKET-HTTP-ERROR", hdr.release(), "socket closed on remote end while reading header data after reading " QSD " byte%s", count, count == 1 ? "" : "s");
1656  }
1657  }
1658  return 0;
1659  }
1660  char c = buf[0];
1661  if (++count == QORE_MAX_HEADER_SIZE) {
1662  xsink->raiseException("SOCKET-HTTP-ERROR", "header size cannot exceed " QSD " bytes", count);
1663  return 0;
1664  }
1665 
1666  // check if we can progress to the next state
1667  if (c == '\n') {
1668  if (state == -1) {
1669  state = 3;
1670  continue;
1671  }
1672  if (!state) {
1673  if (exit_early && hdr->empty())
1674  return 0;
1675  state = 1;
1676  continue;
1677  }
1678  assert(state > 0);
1679  break;
1680  } else if (c == '\r') {
1681  if (state == -1) {
1682  state = 0;
1683  continue;
1684  }
1685  if (!state)
1686  break;
1687  if (state == 1) {
1688  state = 2;
1689  continue;
1690  }
1691  }
1692 
1693  if (state != -1) {
1694  switch (state) {
1695  case 0: hdr->concat('\r'); break;
1696  case 1: hdr->concat("\r\n"); break;
1697  case 2: hdr->concat("\r\n\r"); break;
1698  case 3: hdr->concat('\n'); break;
1699  }
1700  state = -1;
1701  }
1702  hdr->concat(c);
1703  }
1704  hdr->concat('\n');
1705 
1706  //printd(5, "qore_socket_private::readHTTPData(timeout: %d) hdr='%s' (%d)\n", timeout, hdr->getBuffer(), hdr->size());
1707 
1708  th.finalize(hdr->size());
1709 
1710  return hdr.release();
1711  }
1712 
1713  DLLLOCAL QoreStringNode* recv(ExceptionSink* xsink, qore_offset_t bufsize, int timeout, qore_offset_t& rc, int source = QORE_SOURCE_SOCKET) {
1714  assert(xsink);
1715  if (sock == QORE_INVALID_SOCKET) {
1716  se_not_open("Socket", "recv", xsink);
1717  rc = QSE_NOT_OPEN;
1718  return 0;
1719  }
1720  if (in_op >= 0) {
1721  if (in_op == gettid()) {
1722  se_in_op("Socket", "recv", xsink);
1723  return 0;
1724  }
1725  se_in_op_thread("Socket", "recv", xsink);
1726  return 0;
1727  }
1728 
1729  PrivateQoreSocketThroughputHelper th(this, false);
1730 
1731  qore_size_t bs = bufsize > 0 && bufsize < DEFAULT_SOCKET_BUFSIZE ? bufsize : DEFAULT_SOCKET_BUFSIZE;
1732 
1733  QoreStringNodeHolder str(new QoreStringNode(enc));
1734 
1735  char* buf;
1736 
1737  while (true) {
1738  rc = brecv(xsink, "recv", buf, bs, 0, timeout, false);
1739 
1740  if (rc <= 0) {
1741  printd(5, "qore_socket_private::recv(" QSD ", %d) bs=" QSD ", br=" QSD ", rc=" QSD ", errno: %d (%s)\n", bufsize, timeout, bs, str->size(), rc, errno, strerror(errno));
1742  break;
1743  }
1744 
1745  str->concat(buf, rc);
1746 
1747  // register event
1748  if (source > 0) {
1749  do_read_event(rc, str->size(), bufsize, source);
1750  }
1751 
1752  if (bufsize > 0) {
1753  if (str->size() >= (qore_size_t)bufsize)
1754  break;
1755  if ((bufsize - str->size()) < bs)
1756  bs = bufsize - str->size();
1757  }
1758  }
1759 
1760  printd(5, "qore_socket_private::recv() received " QSD " byte(s), bufsize=" QSD ", strlen=" QSD " str='%s'\n", str->size(), bufsize, (str ? str->strlen() : 0), str ? str->getBuffer() : "n/a");
1761 
1762  // "fix" return code value if no error occurred
1763  if (rc >= 0)
1764  rc = str->size();
1765 
1766  th.finalize(str->size());
1767 
1768  if (*xsink) {
1769  return nullptr;
1770  }
1771 
1772  if (source > 0) {
1773  do_data_event(QORE_EVENT_SOCKET_DATA_READ, source, **str);
1774  }
1775  return str.release();
1776  }
1777 
1778  DLLLOCAL QoreStringNode* recvAll(ExceptionSink* xsink, int timeout, qore_offset_t& rc, int source = QORE_SOURCE_SOCKET) {
1779  assert(xsink);
1780  if (sock == QORE_INVALID_SOCKET) {
1781  se_not_open("Socket", "recv", xsink);
1782  rc = QSE_NOT_OPEN;
1783  return 0;
1784  }
1785  if (in_op >= 0) {
1786  if (in_op == gettid()) {
1787  se_in_op("Socket", "recv", xsink);
1788  return 0;
1789  }
1790  se_in_op_thread("Socket", "recv", xsink);
1791  return 0;
1792  }
1793 
1794  PrivateQoreSocketThroughputHelper th(this, false);
1795 
1796  QoreStringNodeHolder str(new QoreStringNode(enc));
1797 
1798  // perform first read with timeout
1799  char* buf;
1800  rc = brecv(xsink, "recv", buf, DEFAULT_SOCKET_BUFSIZE, 0, timeout, false);
1801  if (rc <= 0)
1802  return 0;
1803 
1804  str->concat(buf, rc);
1805 
1806  // register event
1807  do_read_event(rc, rc);
1808 
1809  // keep reading data until no more data is available without a timeout
1810  if (isDataAvailable(0, "recv", xsink)) {
1811  do {
1812  rc = brecv(xsink, "recv", buf, DEFAULT_SOCKET_BUFSIZE, 0, 0, false);
1813  //printd(5, "qore_socket_private::recv(to: %d) rc=" QSD " rd=" QSD "\n", timeout, rc, str->size());
1814  // if the remote end has closed the connection, return what we have
1815  if (!rc)
1816  break;
1817  if (rc < 0) {
1818  th.finalize(str->size());
1819  return 0;
1820  }
1821  str->concat(buf, rc);
1822 
1823  // register event
1824  do_read_event(rc, str->size());
1825  } while (isDataAvailable(0, "recv", xsink));
1826  }
1827 
1828  th.finalize(str->size());
1829 
1830  if (*xsink) {
1831  return nullptr;
1832  }
1833 
1834  rc = str->size();
1835  if (source > 0) {
1836  do_data_event(QORE_EVENT_SOCKET_DATA_READ, source, **str);
1837  }
1838  return str.release();
1839  }
1840 
1841  DLLLOCAL int recv(int fd, qore_offset_t size, int timeout_ms, ExceptionSink* xsink);
1842 
1843  DLLLOCAL BinaryNode* recvBinary(ExceptionSink* xsink, qore_offset_t bufsize, int timeout, qore_offset_t& rc, int source = QORE_SOURCE_SOCKET) {
1844  assert(xsink);
1845  if (sock == QORE_INVALID_SOCKET) {
1846  se_not_open("Socket", "recvBinary", xsink);
1847  rc = QSE_NOT_OPEN;
1848  return 0;
1849  }
1850  if (in_op >= 0) {
1851  if (in_op == gettid()) {
1852  se_in_op("Socket", "recvBinary", xsink);
1853  return 0;
1854  }
1855  se_in_op_thread("Socket", "recvBinary", xsink);
1856  return 0;
1857  }
1858 
1859  PrivateQoreSocketThroughputHelper th(this, false);
1860 
1861  qore_size_t bs = bufsize > 0 && bufsize < DEFAULT_SOCKET_BUFSIZE ? bufsize : DEFAULT_SOCKET_BUFSIZE;
1862 
1864 
1865  char* buf;
1866  while (true) {
1867  rc = brecv(xsink, "recvBinary", buf, bs, 0, timeout);
1868  if (rc <= 0)
1869  break;
1870 
1871  b->append(buf, rc);
1872 
1873  if (bufsize > 0) {
1874  if (b->size() >= (qore_size_t)bufsize)
1875  break;
1876  if ((bufsize - b->size()) < bs)
1877  bs = bufsize - b->size();
1878  }
1879  }
1880 
1881  th.finalize(b->size());
1882 
1883  if (*xsink)
1884  return nullptr;
1885 
1886  // "fix" return code value if no error occurred
1887  if (rc >= 0)
1888  rc = b->size();
1889 
1890  if (source > 0) {
1891  do_data_event(QORE_EVENT_SOCKET_DATA_READ, source, **b);
1892  }
1893  printd(5, "qore_socket_private::recvBinary() received " QSD " byte(s), bufsize=" QSD ", blen=" QSD "\n", b->size(), bufsize, b->size());
1894  return b.release();
1895  }
1896 
1897  DLLLOCAL BinaryNode* recvBinaryAll(ExceptionSink* xsink, int timeout, qore_offset_t& rc, int source = QORE_SOURCE_SOCKET) {
1898  assert(xsink);
1899  if (sock == QORE_INVALID_SOCKET) {
1900  se_not_open("Socket", "recvBinary", xsink);
1901  rc = QSE_NOT_OPEN;
1902  return 0;
1903  }
1904  if (in_op >= 0) {
1905  if (in_op == gettid()) {
1906  se_in_op("Socket", "recvBinary", xsink);
1907  return 0;
1908  }
1909  se_in_op_thread("Socket", "recvBinary", xsink);
1910  return 0;
1911  }
1912 
1913  PrivateQoreSocketThroughputHelper th(this, false);
1914 
1916 
1917  //printd(5, "QoreSocket::recvBinary(%d, " QSD ") this: %p\n", timeout, rc, this);
1918  // perform first read with timeout
1919  char* buf;
1920  rc = brecv(xsink, "recvBinary", buf, DEFAULT_SOCKET_BUFSIZE, 0, timeout, false);
1921  if (rc <= 0)
1922  return 0;
1923 
1924  b->append(buf, rc);
1925 
1926  // register event
1927  do_read_event(rc, rc);
1928 
1929  // keep reading data until no more data is available without a timeout
1930  if (isDataAvailable(0, "recvBinary", xsink)) {
1931  do {
1932  rc = brecv(xsink, "recvBinary", buf, DEFAULT_SOCKET_BUFSIZE, 0, 0, false);
1933  // if the remote end has closed the connection, return what we have
1934  if (!rc)
1935  break;
1936  if (rc < 0) {
1937  th.finalize(b->size());
1938  return 0;
1939  }
1940 
1941  b->append(buf, rc);
1942 
1943  // register event
1944  do_read_event(rc, b->size());
1945  } while (isDataAvailable(0, "recvBinary", xsink));
1946  }
1947 
1948  th.finalize(b->size());
1949 
1950  if (*xsink)
1951  return nullptr;
1952 
1953  if (source > 0) {
1954  do_data_event(QORE_EVENT_SOCKET_DATA_READ, source, **b);
1955  }
1956  rc = b->size();
1957  //printd(5, "qore_socket_private() this: %p b: %p size: %lld\n", this, b->getPtr(), rc);
1958  return b.release();
1959  }
1960 
1961  DLLLOCAL void recvToOutputStream(OutputStream *os, int64 size, int64 timeout, ExceptionSink *xsink, QoreThreadLock* l, int source = QORE_SOURCE_SOCKET) {
1962  if (sock == QORE_INVALID_SOCKET) {
1963  se_not_open("Socket", "recvToOutputStream", xsink);
1964  return;
1965  }
1966  if (in_op >= 0) {
1967  if (in_op == gettid()) {
1968  se_in_op("Socket", "recvToOutputStream", xsink);
1969  return;
1970  }
1971  se_in_op_thread("Socket", "recvToOutputStream", xsink);
1972  return;
1973  }
1974 
1975  qore_socket_op_helper oh(this);
1976 
1977  char* buf;
1978  qore_offset_t br = 0;
1979  while (size < 0 || br < size) {
1980  // calculate bytes needed
1981  int bn = size < 0 ? DEFAULT_SOCKET_BUFSIZE : QORE_MIN(size - br, DEFAULT_SOCKET_BUFSIZE);
1982 
1983  qore_offset_t rc = brecv(xsink, "recvToOutputStream", buf, bn, 0, timeout);
1984  if (rc < 0) {
1985  //error - already reported in xsink
1986  return;
1987  }
1988  if (rc == 0) {
1989  //eof
1990  if (size >= 0) {
1991  //not all size bytes were read
1992  xsink->raiseException("SOCKET-RECV-ERROR", "Unexpected end of stream");
1993  }
1994  return;
1995  }
1996 
1997  if (source > 0) {
1998  do_data_event(QORE_EVENT_SOCKET_DATA_READ, source, buf, rc);
1999  }
2000 
2001  // write buffer to the stream
2002  {
2003  AutoUnlocker al(l);
2004  os->write(buf, rc, xsink);
2005  if (*xsink) {
2006  return;
2007  }
2008  }
2009 
2010  br += rc;
2011  }
2012  }
2013 
2014  DLLLOCAL QoreStringNode* readHTTPHeaderString(ExceptionSink* xsink, int timeout, int source) {
2015  assert(xsink);
2016  qore_offset_t rc;
2017  QoreStringNodeHolder hdr(readHTTPData(xsink, "readHTTPHeaderString", timeout, rc));
2018  if (!hdr) {
2019  assert(*xsink);
2020  return 0;
2021  }
2022  assert(rc > 0);
2023  do_data_event(QORE_EVENT_HTTP_HEADERS_READ, source, **hdr);
2024  return hdr.release();
2025  }
2026 
2027  DLLLOCAL QoreHashNode* readHTTPHeader(ExceptionSink* xsink, QoreHashNode* info, int timeout,
2028  qore_offset_t& rc, int source, const char* headers_raw_key = "headers-raw") {
2029  assert(xsink);
2030  QoreStringNodeHolder hdr(readHTTPData(xsink, "readHTTPHeader", timeout, rc));
2031  if (!hdr) {
2032  assert(*xsink);
2033  return nullptr;
2034  }
2035  assert(rc > 0);
2036 
2037  const char* buf = hdr->getBuffer();
2038 
2039  char* p;
2040  if ((p = (char*)strstr(buf, "\r\n"))) {
2041  *p = '\0';
2042  p += 2;
2043  } else if ((p = (char*)strchr(buf, '\n'))) {
2044  *p = '\0';
2045  ++p;
2046  } else if ((p = (char*)strchr(buf, '\r'))) {
2047  *p = '\0';
2048  ++p;
2049  } else {
2050  // readHTTPData will only return a string that satisifies one of the above conditions,
2051  // however an embedded 0 could have been sent which would make the above searches invalid
2052  xsink->raiseException("SOCKET-HTTP-ERROR", "invalid header received with embedded nulls in Socket::readHTTPHeader()");
2053  return nullptr;
2054  }
2055 
2056  char* t1;
2057  if (!(t1 = (char*)strstr(buf, "HTTP/"))) {
2058  xsink->raiseExceptionArg("SOCKET-HTTP-ERROR", hdr.release(), "missing HTTP version string in first header line in Socket::readHTTPHeader()");
2059  return nullptr;
2060  }
2061 
2062  ReferenceHolder<QoreHashNode> h(new QoreHashNode(autoTypeInfo), xsink);
2063 
2064  // process header flags
2065  int flags = CHF_PROCESS;
2066 
2067  // get version
2068  {
2069  QoreStringNode* hv = new QoreStringNode(t1 + 5, 3, enc);
2070  h->setKeyValue("http_version", hv, nullptr);
2071  if (*hv == "1.1")
2072  flags |= CHF_HTTP11;
2073  }
2074 
2075  // if we are getting a response
2076  // key for info if applicable
2077  const char* info_key;
2078  if (t1 == buf) {
2079  char* t2 = (char*)strchr(buf + 8, ' ');
2080  if (t2) {
2081  t2++;
2082  if (isdigit(*(t2))) {
2083  h->setKeyValue("status_code", atoi(t2), nullptr);
2084  if (strlen(t2) > 4) {
2085  h->setKeyValue("status_message", new QoreStringNode(t2 + 4), nullptr);
2086  }
2087  }
2088  }
2089  // write the status line as the "response-uri" key in the info hash if present
2090  // NOTE: this is not a URI, so the name is not really appropriate
2091  info_key = "response-uri";
2092  } else { // get method and path
2093  char* t2 = (char*)strchr(buf, ' ');
2094  if (t2) {
2095  *t2 = '\0';
2096  h->setKeyValue("method", new QoreStringNode(buf), nullptr);
2097  t2++;
2098  t1 = strchr(t2, ' ');
2099  if (t1) {
2100  *t1 = '\0';
2101  //printd(5, "found path '%s'\n", t2);
2102  // the path is returned as-is with no decodings - use decode_url() to decode
2103  h->setKeyValue("path", new QoreStringNode(t2, enc), nullptr);
2104  }
2105  }
2106  info_key = "request-uri";
2107  flags |= CHF_REQUEST;
2108  }
2109 
2110  // write status line or request line to the info hash and raise a data event if applicable
2111  if (info || (event_queue && event_data)) {
2112  QoreStringNodeHolder status_line(new QoreStringNode(buf));
2113  if (info && event_queue && event_data) {
2114  status_line->ref();
2115  }
2116  if (event_queue && event_data) {
2117  do_data_event_intern(QORE_EVENT_SOCKET_DATA_READ, source, **status_line);
2118  }
2119  if (info) {
2120  info->setKeyValue(info_key, *status_line, nullptr);
2121  }
2122  status_line.release();
2123  }
2124 
2125  bool close = convertHeaderToHash(*h, p, flags, info, &http_exp_chunked_body, headers_raw_key);
2126  do_read_http_header(QORE_EVENT_HTTP_MESSAGE_RECEIVED, *h, source);
2127 
2128  // process header info
2129  if ((flags & CHF_REQUEST) && info)
2130  info->setKeyValue("close", close, 0);
2131 
2132  return h.release();
2133  }
2134 
2135  // info must be already referenced for the assignment, if present
2136  DLLLOCAL int runHeaderCallback(ExceptionSink* xsink, const char* cname, const char* mname, const ResolvedCallReferenceNode& callback, QoreThreadLock* l, const QoreHashNode* hdr, QoreHashNode* info, bool send_aborted = false, QoreObject* obj = nullptr) {
2137  assert(xsink);
2138  assert(obj);
2139  ReferenceHolder<QoreListNode> args(new QoreListNode(autoTypeInfo), xsink);
2140  QoreHashNode* arg = new QoreHashNode(autoTypeInfo);
2141  arg->setKeyValue("hdr", hdr ? hdr->refSelf() : nullptr, xsink);
2142  arg->setKeyValue("info", info, xsink);
2143  if (obj)
2144  arg->setKeyValue("obj", obj->refSelf(), xsink);
2145  arg->setKeyValue("send_aborted", send_aborted, xsink);
2146  args->push(arg, nullptr);
2147 
2148  ValueHolder rv(xsink);
2149  return runCallback(xsink, cname, mname, rv, callback, l, *args);
2150  }
2151 
2152  DLLLOCAL int runTrailerCallback(ExceptionSink* xsink, const char* cname, const char* mname, const ResolvedCallReferenceNode& callback, QoreThreadLock* l, ReferenceHolder<QoreHashNode>& hdr) {
2153  ValueHolder rv(xsink);
2154  if (runCallback(xsink, cname, mname, rv, callback, l, nullptr))
2155  return -1;
2156 
2157  switch (rv->getType()) {
2158  case NT_NOTHING:
2159  break;
2160  case NT_HASH: {
2161  hdr = rv.release().get<QoreHashNode>();
2162  break;
2163  }
2164  default:
2165  xsink->raiseException("HTTP-TRAILER-ERROR", "chunked callback returned type '%s'; expecting 'hash' or 'NOTHING'", rv->getTypeName());
2166  return -1;
2167  }
2168  return 0;
2169  }
2170 
2171  DLLLOCAL int runDataCallback(ExceptionSink* xsink, const char* cname, const char* mname, const ResolvedCallReferenceNode& callback, QoreThreadLock* l, const AbstractQoreNode* data, bool chunked) {
2172  assert(xsink);
2173  ReferenceHolder<QoreListNode> args(new QoreListNode(autoTypeInfo), xsink);
2174  QoreHashNode* arg = new QoreHashNode(autoTypeInfo);
2175  arg->setKeyValue("data", data->realCopy(), xsink);
2176  arg->setKeyValue("chunked", chunked, xsink);
2177  args->push(arg, nullptr);
2178 
2179  ValueHolder rv(xsink);
2180  return runCallback(xsink, cname, mname, rv, callback, l, *args);
2181  }
2182 
2183  DLLLOCAL int runCallback(ExceptionSink* xsink, const char* cname, const char* mname, ValueHolder& res, const ResolvedCallReferenceNode& callback, QoreThreadLock* l, const QoreListNode* args = nullptr) {
2184  assert(xsink);
2185  // FIXME: subtract callback execution time from socket performance measurement
2186 
2187  // unlock and execute callback
2188  {
2189  AutoUnlocker al(l);
2190  res = callback.execValue(args, xsink);
2191  }
2192 
2193  // check exception and socket status
2194  assert(xsink);
2195  if (*xsink)
2196  return -1;
2197 
2198  if (sock == QORE_INVALID_SOCKET) {
2199  se_not_open(cname, mname, xsink);
2200  return QSE_NOT_OPEN;
2201  }
2202 
2203  return 0;
2204  }
2205 
2206  DLLLOCAL int sendHttpChunkedWithCallback(ExceptionSink* xsink, const char* cname, const char* mname, const ResolvedCallReferenceNode& send_callback, QoreThreadLock& l, int source, int timeout_ms = -1, bool* aborted = nullptr) {
2207  assert(xsink);
2208  assert(!aborted || !(*aborted));
2209 
2210  if (sock == QORE_INVALID_SOCKET) {
2211  se_not_open(cname, mname, xsink);
2212  return QSE_NOT_OPEN;
2213  }
2214  if (in_op >= 0) {
2215  if (in_op == gettid()) {
2216  se_in_op(cname, mname, xsink);
2217  return 0;
2218  }
2219  se_in_op_thread(cname, mname, xsink);
2220  return 0;
2221  }
2222 
2223  PrivateQoreSocketThroughputHelper th(this, true);
2224 
2225  // set the non-blocking flag (for use with non-ssl connections)
2226  bool nb = (timeout_ms >= 0);
2227  // set non-blocking I/O (and restore on exit) if we have a timeout and a non-ssl connection
2228  OptionalNonBlockingHelper onbh(*this, !ssl && nb, xsink);
2229  if (*xsink)
2230  return -1;
2231 
2232  qore_socket_op_helper oh(this);
2233 
2234  qore_offset_t rc;
2235  int64 total = 0;
2236  bool done = false;
2237 
2238  while (!done) {
2239  // if we have response data already, then we assume an error and abort
2240  if (aborted) {
2241  bool data_available = tryReadSocketData(mname, xsink);
2242  //printd(5, "qore_socket_private::sendHttpChunkedWithCallback() this: %p aborted: %p iDA: %d\n", this, aborted, data_available);
2243  if (data_available || *xsink) {
2244  *aborted = true;
2245  return *xsink ? -1 : 0;
2246  }
2247  }
2248 
2249  // FIXME: subtract callback execution time from socket performance measurement
2250  ValueHolder res(xsink);
2251  rc = runCallback(xsink, cname, mname, res, send_callback, &l);
2252  if (rc)
2253  return rc;
2254 
2255  //printd(5, "qore_socket_private::sendHttpChunkedWithCallback() this: %p res: %s\n", this, get_type_name(*res));
2256 
2257  // check callback return val
2258  QoreString buf;
2259  // do not copy data here; set references and send the data directly
2260  const char* data_ptr = nullptr;
2261  size_t data_size = 0;
2262 
2263  switch (res->getType()) {
2264  case NT_STRING: {
2265  const QoreStringNode* str = res->get<const QoreStringNode>();
2266  if (str->empty()) {
2267  done = true;
2268  break;
2269  }
2270  buf.sprintf("%x\r\n", (int)str->size());
2271  data_ptr = str->c_str();
2272  data_size = str->size();
2273  //buf.concat(str->c_str(), str->size());
2274  break;
2275  }
2276 
2277  case NT_BINARY: {
2278  const BinaryNode* b = res->get<const BinaryNode>();
2279  if (b->empty()) {
2280  done = true;
2281  break;
2282  }
2283  buf.sprintf("%x\r\n", (int)b->size());
2284  data_ptr = static_cast<const char*>(b->getPtr());
2285  data_size = b->size();
2286  //buf.concat((const char*)b->getPtr(), b->size());
2287  break;
2288  }
2289 
2290  case NT_HASH: {
2291  buf.concat("0\r\n");
2292 
2293  const QoreHashNode* h = res->get<const QoreHashNode>();
2294  ConstHashIterator hi(h);
2295  while (hi.next()) {
2296  const QoreValue v = hi.get();
2297  const char* key = hi.getKey();
2298 
2299  //printd(5, "qore_socket_private::sendHttpChunkedWithCallback() this: %p trailer %s\n", this, key);
2300 
2301  if (v.getType() == NT_LIST) {
2302  ConstListIterator li(v.get<const QoreListNode>());
2303  while (li.next())
2304  do_header(key, buf, li.getValue());
2305  } else
2306  do_header(key, buf, v);
2307  }
2308  // fall through to next case
2309  }
2310 
2311  case NT_NOTHING:
2312  case NT_NULL:
2313  done = true;
2314  break;
2315 
2316  default:
2317  xsink->raiseException("SOCKET-CALLBACK-ERROR", "HTTP chunked data callback returned type '%s'; expecting one of: 'string', 'binary', 'hash', 'nothing' (or 'NULL')", res->getTypeName());
2318  return -1;
2319  }
2320 
2321  // send chunk buffer data
2322  if (!buf.empty()) {
2323  rc = sendIntern(xsink, cname, mname, buf.c_str(), buf.size(), timeout_ms, total, true);
2324  }
2325 
2326  if (!*xsink) {
2327  assert(rc >= 0);
2328  // send actual data, if available
2329  if (data_ptr && data_size) {
2330  rc = sendIntern(xsink, cname, mname, data_ptr, data_size, timeout_ms, total, true);
2331  }
2332 
2333  if (!*xsink) {
2334  assert(rc >= 0);
2335  if (buf.empty() && (!data_ptr || !data_size)) {
2336  buf.set("0\r\n\r\n");
2337  } else {
2338  buf.set("\r\n");
2339  }
2340  rc = sendIntern(xsink, cname, mname, buf.c_str(), buf.size(), timeout_ms, total, true);
2341  }
2342  }
2343 
2344  if (!*xsink) {
2345  // do events
2346  switch (res->getType()) {
2347  case NT_STRING: {
2348  const QoreStringNode* str = res->get<const QoreStringNode>();
2349  if (!str->empty()) {
2350  do_data_event(QORE_EVENT_HTTP_CHUNKED_DATA_SENT, source, *str);
2351  }
2352  break;
2353  }
2354 
2355  case NT_BINARY: {
2356  const BinaryNode* b = res->get<const BinaryNode>();
2357  if (!b->empty()) {
2358  do_data_event(QORE_EVENT_HTTP_CHUNKED_DATA_SENT, source, *b);
2359  }
2360  break;
2361  }
2362 
2363  case NT_HASH: {
2364  const QoreHashNode* h = res->get<const QoreHashNode>();
2365  do_header_event(QORE_EVENT_HTTP_FOOTERS_SENT, source, *h);
2366  break;
2367  }
2368  }
2369  }
2370 
2371  if (rc < 0) {
2372  // if we have a socket I/O error, but also data to be read on the socket, then clear the exception and return 0
2373  if (aborted && *xsink) {
2374  bool data_available = tryReadSocketData(mname, xsink);
2375  //printd(5, "qore_socket_private::sendHttpChunkedWithCallback() this: %p aborted: %p iDA: %d\n", this, aborted, data_available);
2376  if (data_available) {
2377  *aborted = true;
2378  return *xsink ? -1 : 0;
2379  }
2380  }
2381 
2382  //printd(5, "qore_socket_private::sendHttpChunkedWithCallback() this: %p rc: %d sock: %d xsink: %d\n", this, rc, sock, xsink->isException());
2383  }
2384 
2385  //printd(5, "qore_socket_private::sendHttpChunkedWithCallback() this: %p sent: %s\n", this, buf.getBuffer());
2386 
2387  if (rc < 0 || sock == QORE_INVALID_SOCKET)
2388  break;
2389  }
2390 
2391  th.finalize(total);
2392 
2393  return rc < 0 || sock == QORE_INVALID_SOCKET ? -1 : 0;
2394  }
2395 
2396  DLLLOCAL int sendIntern(ExceptionSink* xsink, const char* cname, const char* mname, const char* buf, qore_size_t size, int timeout_ms, int64& total, bool stream = false) {
2397  assert(xsink);
2398  qore_offset_t rc;
2399  qore_size_t bs = 0;
2400 
2401  // set the non-blocking flag (for use with non-ssl connections)
2402  bool nb = (timeout_ms >= 0);
2403 
2404  while (true) {
2405  if (ssl) {
2406  // SSL_MODE_ENABLE_PARTIAL_WRITE is enabled so we can get finer-grained socket events for do_send_event() below
2407  rc = ssl->write(mname, buf + bs, size - bs, timeout_ms, xsink);
2408  } else {
2409  while (true) {
2410  rc = ::send(sock, buf + bs, size - bs, 0);
2411  //printd(5, "qore_socket_private::send() this: %p Socket::%s() buf: %p size: " QLLD " timeout_ms: %d ssl: %p nb: %d bs: " QLLD " rc: " QLLD "\n", this, mname, buf, size, timeout_ms, ssl, nb, bs, rc);
2412  // try again if we were interrupted by a signal
2413  if (rc >= 0)
2414  break;
2415  sock_get_error();
2416  // check that the send finishes before the timeout if we are using non-blocking I/O
2417  if (nb && (errno == EAGAIN
2418 #ifdef EWOULDBLOCK
2419  || errno == EWOULDBLOCK
2420 #endif
2421  )) {
2422  if (!isWriteFinished(timeout_ms, mname, xsink)) {
2423  if (*xsink)
2424  return -1;
2425  se_timeout("Socket", mname, timeout_ms, xsink);
2426  rc = QSE_TIMEOUT;
2427  break;
2428  }
2429  continue;
2430  }
2431  if (errno != EINTR) {
2432  //printd(5, "qore_socket_private::send() bs: %ld rc: " QSD " len: " QSD " (total: " QSD ") errno: %d sock: %d\n", bs, rc, size - bs, size, errno, sock);
2433  xsink->raiseErrnoException("SOCKET-SEND-ERROR", errno, "error while executing %s::%s()", cname, mname);
2434 
2435  // do not close the socket even if we have EPIPE or ECONNRESET in case there is data to be read when streaming
2436 #ifdef EPIPE
2437  if (!stream && errno == EPIPE)
2438  close();
2439 #endif
2440 #ifdef ECONNRESET
2441  if (!stream && errno == ECONNRESET)
2442  close();
2443 #endif
2444  break;
2445  }
2446  }
2447  }
2448 
2449  total += rc;
2450 
2451  //printd(5, "qore_socket_private::send() bs: %ld rc: " QSD " len: " QSD " (total: " QSD ") errno: %d\n", bs, rc, size - bs, size, errno);
2452  if (rc < 0 || sock == QORE_INVALID_SOCKET)
2453  break;
2454 
2455  bs += rc;
2456 
2457  do_send_event(rc, bs, size);
2458 
2459  if (bs >= size)
2460  break;
2461  }
2462 
2463  return rc;
2464  }
2465 
2466  DLLLOCAL int send(int fd, qore_offset_t size, int timeout_ms, ExceptionSink* xsink);
2467 
2468  DLLLOCAL int send(ExceptionSink* xsink, const char* cname, const char* mname, const char* buf, qore_size_t size, int timeout_ms = -1, int source = QORE_SOURCE_SOCKET) {
2469  assert(xsink);
2470  if (sock == QORE_INVALID_SOCKET) {
2471  se_not_open(cname, mname, xsink);
2472 
2473  return QSE_NOT_OPEN;
2474  }
2475  if (in_op >= 0) {
2476  if (in_op == gettid()) {
2477  se_in_op(cname, mname, xsink);
2478  return 0;
2479  }
2480  se_in_op_thread(cname, mname, xsink);
2481  return 0;
2482  }
2483  if (!size) {
2484  return 0;
2485  }
2486 
2487  PrivateQoreSocketThroughputHelper th(this, true);
2488 
2489  // set the non-blocking flag (for use with non-ssl connections)
2490  bool nb = (timeout_ms >= 0);
2491  // set non-blocking I/O (and restore on exit) if we have a timeout and a non-ssl connection
2492  OptionalNonBlockingHelper onbh(*this, !ssl && nb, xsink);
2493  if (*xsink)
2494  return -1;
2495 
2496  int64 total = 0;
2497  qore_offset_t rc = sendIntern(xsink, cname, mname, buf, size, timeout_ms, total);
2498  th.finalize(total);
2499 
2500  if (rc > 0 && source > 0) {
2501  do_data_event(QORE_EVENT_SOCKET_DATA_SENT, source, buf, size);
2502  }
2503 
2504  return rc < 0 || sock == QORE_INVALID_SOCKET ? rc : 0;
2505  }
2506 
2507  DLLLOCAL void sendFromInputStream(InputStream *is, int64 size, int64 timeout, ExceptionSink *xsink, QoreThreadLock* l) {
2508  if (sock == QORE_INVALID_SOCKET) {
2509  se_not_open("Socket", "sendFromInputStream", xsink);
2510  return;
2511  }
2512  if (in_op >= 0) {
2513  if (in_op == gettid()) {
2514  se_in_op("Socket", "sendFromInputStream", xsink);
2515  return;
2516  }
2517  se_in_op_thread("Socket", "sendFromInputStream", xsink);
2518  return;
2519  }
2520 
2521  qore_socket_op_helper oh(this);
2522 
2523  PrivateQoreSocketThroughputHelper th(this, true);
2524 
2525  // set the non-blocking flag (for use with non-ssl connections)
2526  bool nb = (timeout >= 0);
2527  // set non-blocking I/O (and restore on exit) if we have a timeout and a non-ssl connection
2528  OptionalNonBlockingHelper onbh(*this, !ssl && nb, xsink);
2529  if (*xsink)
2530  return;
2531 
2532  char buf[DEFAULT_SOCKET_BUFSIZE];
2533  int64 sent = 0;
2534  int64 total = 0;
2535  while (size < 0 || sent < size) {
2536  int64 toRead = size < 0 ? DEFAULT_SOCKET_BUFSIZE : QORE_MIN(size - sent, DEFAULT_SOCKET_BUFSIZE);
2537  int64 r;
2538  {
2539  AutoUnlocker al(l);
2540  r = is->read(buf, toRead, xsink);
2541  if (*xsink) {
2542  return;
2543  }
2544  }
2545  if (r == 0) {
2546  //eof
2547  if (size >= 0) {
2548  //not all size bytes were sent
2549  xsink->raiseException("SOCKET-SEND-ERROR", "Unexpected end of stream");
2550  return;
2551  }
2552  break;
2553  }
2554 
2555  qore_offset_t rc = sendIntern(xsink, "Socket", "sendFromInputStream", buf, r, timeout, total);
2556  if (rc < 0) {
2557  return;
2558  }
2559  do_data_event(QORE_EVENT_SOCKET_DATA_SENT, QORE_SOURCE_SOCKET, buf, r);
2560  sent += r;
2561  }
2562  th.finalize(total);
2563  }
2564 
2565  DLLLOCAL void sendHttpChunkedBodyFromInputStream(InputStream* is, size_t max_chunk_size, int timeout, ExceptionSink* xsink, QoreThreadLock* l, const ResolvedCallReferenceNode* trailer_callback) {
2566  if (sock == QORE_INVALID_SOCKET) {
2567  se_not_open("Socket", "sendHttpChunkedBodyFromInputStream", xsink);
2568  return;
2569  }
2570  if (in_op >= 0) {
2571  if (in_op == gettid()) {
2572  se_in_op("Socket", "sendHttpChunkedBodyFromInputStream", xsink);
2573  return;
2574  }
2575  se_in_op_thread("Socket", "sendHttpChunkedBodyFromInputStream", xsink);
2576  return;
2577  }
2578 
2579  qore_socket_op_helper oh(this);
2580 
2581  PrivateQoreSocketThroughputHelper th(this, true);
2582 
2583  // set the non-blocking flag (for use with non-ssl connections)
2584  bool nb = (timeout >= 0);
2585  // set non-blocking I/O (and restore on exit) if we have a timeout and a non-ssl connection
2586  OptionalNonBlockingHelper onbh(*this, !ssl && nb, xsink);
2587  if (*xsink)
2588  return;
2589 
2591  // reserve enough space for the maximum size of the buffer + HTTP overhead
2592  buf->preallocate(max_chunk_size);
2593  int64 total = 0;
2594  while (true) {
2595  int64 r;
2596  {
2597  AutoUnlocker al(l);
2598  r = is->read((void*)buf->getPtr(), sizeof(max_chunk_size), xsink);
2599  if (*xsink)
2600  return;
2601  }
2602 
2603  // send HTTP chunk prelude with chunk size
2604  QoreString str;
2605  str.sprintf("%x\r\n", (int)r);
2606  int rc = sendIntern(xsink, "Socket", "sendHttpChunkedBodyFromInputStream", str.c_str(), str.size(), timeout, total, true);
2607  if (rc < 0)
2608  return;
2609 
2610  bool trailers = false;
2611 
2612  // send chunk data, if any
2613  if (r) {
2614  rc = sendIntern(xsink, "Socket", "sendHttpChunkedBodyFromInputStream", (const char*)buf->getPtr(), r, timeout, total, true);
2615  if (rc < 0)
2616  return;
2617  do_data_event(QORE_EVENT_HTTP_CHUNKED_DATA_SENT, QORE_SOURCE_SOCKET, buf->getPtr(), r);
2618  } else if (trailer_callback) {
2619  // get and send chunk trailers, if any
2621 
2622  if (runTrailerCallback(xsink, "Socket", "sendHttpChunkedBodyFromInputStream", *trailer_callback, l, h))
2623  return;
2624  if (h) {
2625  str.clear();
2626  do_headers(str, *h, 0, false);
2627 
2628  rc = sendIntern(xsink, "Socket", "sendHttpChunkedBodyFromInputStream", str.c_str(), str.size(), timeout, total, true);
2629  if (rc < 0)
2630  return;
2631 
2632  do_header_event(QORE_EVENT_HTTP_FOOTERS_SENT, QORE_SOURCE_SOCKET, **h);
2633  trailers = true;
2634  }
2635  }
2636 
2637  // close chunk if we sent no trailers
2638  if (!trailers) {
2639  str.set("\r\n");
2640  rc = sendIntern(xsink, "Socket", "sendHttpChunkedBodyFromInputStream", str.c_str(), str.size(), timeout, total, true);
2641  if (rc < 0)
2642  return;
2643  }
2644 
2645  if (!r) {
2646  // end of stream
2647  break;
2648  }
2649  }
2650  th.finalize(total);
2651  }
2652 
2653  DLLLOCAL void sendHttpChunkedBodyTrailer(const QoreHashNode* headers, int timeout, ExceptionSink* xsink) {
2654  if (sock == QORE_INVALID_SOCKET) {
2655  se_not_open("Socket", "sendHttpChunkedBodyTrailer", xsink);
2656  return;
2657  }
2658  if (in_op >= 0) {
2659  if (in_op == gettid()) {
2660  se_in_op("Socket", "sendHttpChunkedBodyTrailer", xsink);
2661  return;
2662  }
2663  se_in_op_thread("Socket", "sendHttpChunkedBodyTrailer", xsink);
2664  return;
2665  }
2666 
2667  QoreString buf;
2668  if (!headers) {
2669  ConstHashIterator hi(headers);
2670 
2671  while (hi.next()) {
2672  const QoreValue v = hi.get();
2673  const char* key = hi.getKey();
2674 
2675  if (v.getType() == NT_LIST) {
2676  ConstListIterator li(v.get<const QoreListNode>());
2677  while (li.next())
2678  do_header(key, buf, li.getValue());
2679  }
2680  else
2681  do_header(key, buf, v);
2682  }
2683  }
2684  buf.concat("\r\n");
2685  int64 total;
2686  sendIntern(xsink, "Socket", "sendHttpChunkedBodyTrailer", buf.getBuffer(), buf.size(), timeout, total, true);
2687  if (!*xsink) {
2688  do_header_event(QORE_EVENT_HTTP_FOOTERS_SENT, QORE_SOURCE_SOCKET, *headers);
2689  }
2690  }
2691 
2692  DLLLOCAL int sendHttpMessage(ExceptionSink* xsink, QoreHashNode* info, const char* cname, const char* mname,
2693  const char* method, const char* path, const char* http_version, const QoreHashNode* headers,
2694  const QoreStringNode* body, const void *data, qore_size_t size,
2695  const ResolvedCallReferenceNode* send_callback, InputStream* input_stream, size_t max_chunk_size,
2696  const ResolvedCallReferenceNode* trailer_callback, int source, int timeout_ms = -1,
2697  QoreThreadLock* l = nullptr, bool* aborted = nullptr) {
2698  // prepare header string
2699  QoreString hdr(enc);
2700 
2701  hdr.sprintf("%s %s HTTP/%s", method, path && path[0] ? path : "/", http_version);
2702 
2703  // write request-uri key if info hash is non-null
2704  if (info) {
2705  info->setKeyValue("request-uri", new QoreStringNode(hdr), nullptr);
2706  }
2707 
2708  return sendHttpMessageCommon(xsink, hdr, info, cname, mname, headers, body, data, size, send_callback,
2709  input_stream, max_chunk_size, trailer_callback, source, timeout_ms, l, aborted);
2710  }
2711 
2712  DLLLOCAL int sendHttpResponse(ExceptionSink* xsink, QoreHashNode* info, const char* cname, const char* mname,
2713  int code, const char* desc, const char* http_version, const QoreHashNode* headers, const QoreStringNode* body,
2714  const void *data, qore_size_t size, const ResolvedCallReferenceNode* send_callback, InputStream* input_stream,
2715  size_t max_chunk_size, const ResolvedCallReferenceNode* trailer_callback, int source, int timeout_ms = -1,
2716  QoreThreadLock* l = nullptr, bool* aborted = nullptr) {
2717  // prepare header string
2718  QoreString hdr(enc);
2719 
2720  // write HTTP response status line
2721  hdr.sprintf("HTTP/%s %03d %s", http_version, code, desc);
2722 
2723  // write the status line as the "response-uri" key if info hash is non-null
2724  // NOTE: this is not a URI, so the name is not really appropriate
2725  if (info) {
2726  info->setKeyValue("response-uri", new QoreStringNode(hdr), nullptr);
2727  }
2728 
2729  return sendHttpMessageCommon(xsink, hdr, info, cname, mname, headers, body, data, size, send_callback,
2730  input_stream, max_chunk_size, trailer_callback, source, timeout_ms, l, aborted);
2731  }
2732 
2733  DLLLOCAL int sendHttpMessageCommon(ExceptionSink* xsink, QoreString& hdr, QoreHashNode* info, const char* cname,
2734  const char* mname, const QoreHashNode* headers, const QoreStringNode* body, const void *data,
2735  qore_size_t size, const ResolvedCallReferenceNode* send_callback, InputStream* input_stream,
2736  size_t max_chunk_size, const ResolvedCallReferenceNode* trailer_callback, int source, int timeout_ms = -1,
2737  QoreThreadLock* l = nullptr, bool* aborted = nullptr) {
2738  assert(xsink);
2739  assert(!(data && send_callback));
2740  assert(!(data && input_stream));
2741  assert(!(send_callback && input_stream));
2742 
2743  // send event
2744  do_send_http_message_event(hdr, headers, source);
2745 
2746  // add headers
2747  hdr.concat("\r\n");
2748  // insert headers
2749  do_headers(hdr, headers, size && data ? size : 0);
2750 
2751  //printd(5, "qore_socket_private::sendHttpMessage() hdr: %s\n", hdr.c_str());
2752 
2753  // send URI and headers
2754  int rc;
2755  if ((rc = send(xsink, cname, mname, hdr.c_str(), hdr.size(), timeout_ms, -1)))
2756  return rc;
2757 
2758  // header message sent above with do_sent_http_message_event()
2759  if (size && data) {
2760  int rc = send(xsink, cname, mname, (char*)data, size, timeout_ms, -1);
2761  if (!rc) {
2762  if (body) {
2763  do_data_event(QORE_EVENT_SOCKET_DATA_SENT, source, *body);
2764  } else {
2765  do_data_event(QORE_EVENT_SOCKET_DATA_SENT, source, data, size);
2766  }
2767  }
2768  return rc;
2769  } else if (send_callback) {
2770  assert(l);
2771  assert(!aborted || !(*aborted));
2772  return sendHttpChunkedWithCallback(xsink, cname, mname, *send_callback, *l, source, timeout_ms, aborted);
2773  } else if (input_stream) {
2774  assert(l);
2775  sendHttpChunkedBodyFromInputStream(input_stream, max_chunk_size, timeout_ms, xsink, l, trailer_callback);
2776  return *xsink ? -1 : 0;
2777  }
2778 
2779  return 0;
2780  }
2781 
2782  DLLLOCAL QoreHashNode* readHttpChunkedBodyBinary(int timeout, ExceptionSink* xsink, const char* cname, int source, const ResolvedCallReferenceNode* recv_callback = nullptr, QoreThreadLock* l = nullptr, QoreObject* obj = nullptr, OutputStream* os = nullptr) {
2783  assert(xsink);
2784 
2785  if (sock == QORE_INVALID_SOCKET) {
2786  se_not_open(cname, "readHTTPChunkedBodyBinary", xsink);
2787  return 0;
2788  }
2789  if (in_op >= 0) {
2790  if (in_op == gettid()) {
2791  se_in_op(cname, "readHTTPChunkedBodyBinary", xsink);
2792  return 0;
2793  }
2794  se_in_op_thread(cname, "readHTTPChunkedBodyBinary", xsink);
2795  return 0;
2796  }
2797 
2798  // reset "expecting HTTP chunked body" flag
2799  if (http_exp_chunked_body)
2800  http_exp_chunked_body = false;
2801 
2802  qore_socket_op_helper oh(this);
2803 
2804  SimpleRefHolder<BinaryNode> b(os ? nullptr : new BinaryNode);
2805  QoreString str; // for reading the size of each chunk
2806 
2807  qore_offset_t rc;
2808  // read the size then read the data and append to buffer
2809  while (true) {
2810  // state = 0, nothing
2811  // state = 1, \r received
2812  int state = 0;
2813  while (true) {
2814  char* buf;
2815  rc = brecv(xsink, "readHTTPChunkedBodyBinary", buf, 1, 0, timeout, false);
2816  if (rc <= 0) {
2817  if (!*xsink) {
2818  assert(!rc);
2819  se_closed(cname, "readHTTPChunkedBodyBinary", xsink);
2820  }
2821  return 0;
2822  }
2823 
2824  char c = buf[0];
2825 
2826  if (!state && c == '\r')
2827  state = 1;
2828  else if (state && c == '\n')
2829  break;
2830  else {
2831  if (state) {
2832  state = 0;
2833  str.concat('\r');
2834  }
2835  str.concat(c);
2836  }
2837  }
2838  // DEBUG
2839  //printd(5, "QoreSocket::readHTTPChunkedBodyBinary(): got chunk size (" QSD " bytes) string: %s\n", str.strlen(), str.getBuffer());
2840 
2841  // terminate string at ';' char if present
2842  char* p = (char*)strchr(str.getBuffer(), ';');
2843  if (p)
2844  *p = '\0';
2845  long size = strtol(str.c_str(), 0, 16);
2846  do_chunked_read(QORE_EVENT_HTTP_CHUNK_SIZE, size, str.size(), source);
2847 
2848  if (!size)
2849  break;
2850 
2851  if (size < 0) {
2852  xsink->raiseException("READ-HTTP-CHUNK-ERROR", "negative value given for chunk size (%ld)", size);
2853  return 0;
2854  }
2855 
2856  // prepare string for chunk
2857  //str.allocate(size + 1);
2858 
2859  qore_offset_t bs = size < DEFAULT_SOCKET_BUFSIZE ? size : DEFAULT_SOCKET_BUFSIZE;
2860  qore_offset_t br = 0; // bytes received
2861  while (true) {
2862  char* buf;
2863  rc = brecv(xsink, "readHTTPChunkedBodyBinary", buf, bs, 0, timeout, false);
2864  //printd(5, "qore_socket_private::readHTTPChunkedBodyBinary() str: '%s' bs: %lld rc: %lld b: %p (%lld) recv_callback: %p\n", str.c_str(), bs, rc, *b, b->size(), recv_callback);
2865  if (rc <= 0) {
2866  if (!*xsink) {
2867  assert(!rc);
2868  se_closed(cname, "readHTTPChunkedBodyBinary", xsink);
2869  }
2870  return nullptr;
2871  }
2872 
2873  do_data_event(QORE_EVENT_HTTP_CHUNKED_DATA_READ, source, buf, (size_t)rc);
2874 
2875  if (os) {
2876  AutoUnlocker al(l);
2877  os->write(buf, rc, xsink);
2878  if (*xsink)
2879  return nullptr;
2880  } else {
2881  b->append(buf, rc);
2882  }
2883  br += rc;
2884 
2885  if (br >= size)
2886  break;
2887  if (size - br < bs)
2888  bs = size - br;
2889  }
2890 
2891  // DEBUG
2892  //printd(5, "QoreSocket::readHTTPChunkedBodyBinary(): received binary chunk: size: %d br=" QSD " total=" QSD "\n", size, br, b->size());
2893 
2894  // read crlf after chunk
2895  // FIXME: bytes read are not checked if they equal CRLF
2896  br = 0;
2897  while (br < 2) {
2898  char* buf;
2899  rc = brecv(xsink, "readHTTPChunkedBodyBinary", buf, 2 - br, 0, timeout, false);
2900  if (rc <= 0) {
2901  if (!*xsink) {
2902  assert(!rc);
2903  se_closed(cname, "readHTTPChunkedBodyBinary", xsink);
2904  }
2905  return nullptr;
2906  }
2907  br += rc;
2908  }
2909 
2910  do_chunked_read(QORE_EVENT_HTTP_CHUNKED_DATA_RECEIVED, size, size + 2, source);
2911 
2912  if (recv_callback && !os) {
2913  if (runDataCallback(xsink, cname, "readHTTPChunkedBodyBinary", *recv_callback, l, *b, true))
2914  return nullptr;
2915  if (b)
2916  b->clear();
2917  }
2918 
2919  // ensure string is blanked for next read
2920  str.clear();
2921  }
2922 
2923  // read footers or nothing
2924  QoreStringNodeHolder hdr(readHTTPData(xsink, "readHTTPChunkedBodyBinary", timeout, rc, true));
2925  if (*xsink)
2926  return nullptr;
2927 
2928  ReferenceHolder<QoreHashNode> h(new QoreHashNode(autoTypeInfo), xsink);
2929  if (!recv_callback && !os) {
2930  h->setKeyValue("body", b.release(), xsink);
2931  }
2932 
2933  ReferenceHolder<QoreHashNode> info(xsink);
2934 
2935  if (hdr) {
2936  if (hdr->strlen() >= 2 && hdr->strlen() <= 4)
2937  return recv_callback ? 0 : h.release();
2938 
2939  if (recv_callback) {
2940  info = new QoreHashNode(autoTypeInfo);
2941  }
2942  convertHeaderToHash(*h, (char*)hdr->c_str(), 0, *info, nullptr, "response-headers-raw");
2943  do_read_http_header(QORE_EVENT_HTTP_FOOTERS_RECEIVED, *h, source);
2944  }
2945 
2946  if (recv_callback) {
2947  runHeaderCallback(xsink, cname, "readHTTPChunkedBodyBinary", *recv_callback, l, h->empty() ? nullptr : *h,
2948  info.release(), false, obj);
2949  return 0;
2950  }
2951 
2952  return h.release();
2953  }
2954 
2955  // receive a message in HTTP chunked format
2956  DLLLOCAL QoreHashNode* readHttpChunkedBody(int timeout, ExceptionSink* xsink, const char* cname, int source, const ResolvedCallReferenceNode* recv_callback = 0, QoreThreadLock* l = 0, QoreObject* obj = 0) {
2957  assert(xsink);
2958 
2959  if (sock == QORE_INVALID_SOCKET) {
2960  se_not_open(cname, "readHTTPChunkedBody", xsink);
2961  return 0;
2962  }
2963  if (in_op >= 0) {
2964  if (in_op == gettid()) {
2965  se_in_op(cname, "readHTTPChunkedBody", xsink);
2966  return 0;
2967  }
2968  se_in_op_thread(cname, "readHTTPChunkedBody", xsink);
2969  return 0;
2970  }
2971 
2972  // reset "expecting HTTP chunked body" flag
2973  if (http_exp_chunked_body)
2974  http_exp_chunked_body = false;
2975 
2976  qore_socket_op_helper oh(this);
2977 
2978  QoreStringNodeHolder buf(new QoreStringNode(enc));
2979  QoreString str; // for reading the size of each chunk
2980 
2981  qore_offset_t rc;
2982  // read the size then read the data and append to buf
2983  while (true) {
2984  // state = 0, nothing
2985  // state = 1, \r received
2986  int state = 0;
2987  while (true) {
2988  char* tbuf;
2989  rc = brecv(xsink, "readHTTPChunkedBody", tbuf, 1, 0, timeout, false);
2990  if (rc <= 0) {
2991  if (!*xsink) {
2992  assert(!rc);
2993  se_closed(cname, "readHTTPChunkedBody", xsink);
2994  }
2995  return 0;
2996  }
2997 
2998  char c = tbuf[0];
2999 
3000  if (!state && c == '\r')
3001  state = 1;
3002  else if (state && c == '\n')
3003  break;
3004  else {
3005  if (state) {
3006  state = 0;
3007  str.concat('\r');
3008  }
3009  str.concat(c);
3010  }
3011  }
3012  // DEBUG
3013  //printd(5, "got chunk size (" QSD " bytes) string: %s\n", str.strlen(), str.getBuffer());
3014 
3015  // terminate string at ';' char if present
3016  char* p = (char*)strchr(str.getBuffer(), ';');
3017  if (p)
3018  *p = '\0';
3019  qore_offset_t size = strtol(str.getBuffer(), 0, 16);
3020  do_chunked_read(QORE_EVENT_HTTP_CHUNK_SIZE, size, str.strlen(), source);
3021 
3022  if (!size)
3023  break;
3024 
3025  if (size < 0) {
3026  xsink->raiseException("READ-HTTP-CHUNK-ERROR", "negative value given for chunk size (%ld)", size);
3027  return 0;
3028  }
3029  // ensure string is blanked for next read
3030  str.clear();
3031 
3032  // prepare string for chunk
3033  //buf->allocate((unsigned)(buf->strlen() + size + 1));
3034 
3035  // read chunk directly into string buffer
3036  qore_offset_t bs = size < DEFAULT_SOCKET_BUFSIZE ? size : DEFAULT_SOCKET_BUFSIZE;
3037  qore_offset_t br = 0; // bytes received
3038  str.clear();
3039  while (true) {
3040  char* tbuf;
3041  rc = brecv(xsink, "readHTTPChunkedBody", tbuf, bs, 0, timeout, false);
3042  if (rc <= 0) {
3043  if (!*xsink) {
3044  assert(!rc);
3045  se_closed(cname, "readHTTPChunkedBody", xsink);
3046  }
3047  return 0;
3048  }
3049 
3050  br += rc;
3051  buf->concat(tbuf, rc);
3052 
3053  do_data_event(QORE_EVENT_HTTP_CHUNKED_DATA_READ, source, tbuf, (size_t)rc);
3054 
3055  if (br >= size)
3056  break;
3057  if (size - br < bs)
3058  bs = size - br;
3059  }
3060 
3061  // DEBUG
3062  //printd(5, "got chunk (" QSD " bytes): %s\n", br, buf->getBuffer() + buf->strlen() - size);
3063 
3064  // read crlf after chunk
3065  // FIXME: bytes read are not checked if they equal CRLF
3066  br = 0;
3067  while (br < 2) {
3068  char* tbuf;
3069  rc = brecv(xsink, "readHTTPChunkedBody", tbuf, 2 - br, 0, timeout, false);
3070  if (rc <= 0) {
3071  if (!*xsink) {
3072  assert(!rc);
3073  se_closed(cname, "readHTTPChunkedBody", xsink);
3074  }
3075  return nullptr;
3076  }
3077  br += rc;
3078  }
3079 
3080  do_chunked_read(QORE_EVENT_HTTP_CHUNKED_DATA_RECEIVED, size, size + 2, source);
3081 
3082  if (recv_callback) {
3083  if (runDataCallback(xsink, cname, "readHTTPChunkedBody", *recv_callback, l, *buf, true))
3084  return nullptr;
3085  buf->clear();
3086  }
3087  }
3088 
3089  // read footers or nothing
3090  QoreStringNodeHolder hdr(readHTTPData(xsink, "readHTTPChunkedBody", timeout, rc, true));
3091  if (*xsink)
3092  return nullptr;
3093 
3094  //printd(5, "chunked body encoding: %s\n", buf->getEncoding()->getCode());
3095  ReferenceHolder<QoreHashNode> h(new QoreHashNode(autoTypeInfo), xsink);
3096  if (!recv_callback) {
3097  h->setKeyValue("body", buf.release(), xsink);
3098  }
3099 
3100  ReferenceHolder<QoreHashNode> info(xsink);
3101 
3102  if (hdr) {
3103  if (hdr->strlen() >= 2 && hdr->strlen() <= 4)
3104  return recv_callback ? 0 : h.release();
3105 
3106  if (recv_callback) {
3107  info = new QoreHashNode(autoTypeInfo);
3108  }
3109  convertHeaderToHash(*h, (char*)hdr->c_str(), 0, *info, nullptr, "response-headers-raw");
3110  do_read_http_header(QORE_EVENT_HTTP_FOOTERS_RECEIVED, *h, source);
3111  }
3112 
3113  if (recv_callback) {
3114  runHeaderCallback(xsink, cname, "readHTTPChunkedBody", *recv_callback, l, h->empty() ? nullptr : *h,
3115  info.release(), false, obj);
3116  return 0;
3117  }
3118 
3119  return h.release();
3120  }
3121 
3122  DLLLOCAL static void do_accept_encoding(char* t, QoreHashNode& info) {
3123  ReferenceHolder<QoreListNode> l(new QoreListNode(autoTypeInfo), 0);
3124 
3125  char* a = t;
3126  bool ok = true;
3127  while (*a) {
3128  if (ok) {
3129  ok = false;
3131  while (*a && *a != ';' && *a != ',')
3132  str->concat(*(a++));
3133  str->trim();
3134  if (!str->empty())
3135  l->push(str.release(), nullptr);
3136  continue;
3137  }
3138  else if (*a == ',')
3139  ok = true;
3140 
3141  ++a;
3142  }
3143 
3144  if (!l->empty())
3145  info.setKeyValue("accept-encoding", l.release(), 0);
3146  }
3147 
3148  DLLLOCAL bool do_accept_charset(char* t, QoreHashNode& info) {
3149  bool acceptcharset = false;
3150 
3151  // see if we have "*" or utf8 or utf-8, in which case set it
3152  // otherwise set the first charset in the list
3153  char* a = t;
3154  char* div = 0;
3155  bool utf8 = false;
3156  bool ok = true;
3157  while (*a) {
3158  if (ok) {
3159  if (*a == '*') {
3160  utf8 = true;
3161  break;
3162  }
3163  ok = false;
3164  if (*a == 'u' || *a == 'U') {
3165  ++a;
3166  if (*a == 't' || *a == 'T') {
3167  ++a;
3168  if (*a == 'f' || *a == 'F') {
3169  ++a;
3170  if (*a == '-')
3171  ++a;
3172  if (*a == '8') {
3173  utf8 = true;
3174  break;
3175  }
3176  }
3177  }
3178  continue;
3179  }
3180  } else if (*a == ',') {
3181  if (!div)
3182  div = a;
3183  ok = true;
3184  } else if (*a == ';') {
3185  if (!div)
3186  div = a;
3187  }
3188 
3189  ++a;
3190  }
3191  if (utf8) {
3192  info.setKeyValue("accept-charset", new QoreStringNode("utf8"), 0);
3193  acceptcharset = true;
3194  } else {
3196  if (div)
3197  ac->concat(t, div - t);
3198  else
3199  ac->concat(t);
3200  ac->trim();
3201  if (!ac->empty()) {
3202  info.setKeyValue("accept-charset", ac.release(), 0);
3203  acceptcharset = true;
3204  }
3205  }
3206 
3207  return acceptcharset;
3208  }
3209 
3210  // returns true if the connection should be closed, false if not
3211  DLLLOCAL bool convertHeaderToHash(QoreHashNode* h, char* p, int flags = 0, QoreHashNode* info = nullptr,
3212  bool* chunked = nullptr, const char* headers_raw_key = "headers-raw") {
3213  bool close = !(flags & CHF_HTTP11);
3214  // socket encoding
3215  const char* senc = nullptr;
3216  // accept-charset
3217  bool acceptcharset = false;
3218 
3219  QoreHashNode* raw_hdr = nullptr;
3220  if (info) {
3221  info->setKeyValue(headers_raw_key, raw_hdr = new QoreHashNode(autoTypeInfo), nullptr);
3222  }
3223 
3224  // raw key for setting raw headers
3225  std::string raw_key;
3226 
3227  while (*p) {
3228  char* buf = p;
3229 
3230  if ((p = strstr(buf, "\r\n"))) {
3231  *p = '\0';
3232  p += 2;
3233  } else if ((p = strchr(buf, '\n'))) {
3234  *p = '\0';
3235  p++;
3236  } else if ((p = strchr(buf, '\r'))) {
3237  *p = '\0';
3238  p++;
3239  } else
3240  break;
3241  char* t = strchr(buf, ':');
3242  if (!t)
3243  break;
3244  *t = '\0';
3245  t++;
3246  while (t && qore_isblank(*t))
3247  t++;
3248  if (raw_hdr) {
3249  raw_key = buf;
3250  }
3251  strtolower(buf);
3252  //printd(5, "setting %s = '%s'\n", buf, t);
3253 
3254  ReferenceHolder<> val(new QoreStringNode(t), nullptr);
3255 
3256  if (flags & CHF_PROCESS) {
3257  if (!strcmp(buf, "connection")) {
3258  if (flags & CHF_HTTP11) {
3259  if (strcasestr(t, "close"))
3260  close = true;
3261  } else {
3262  if (strcasestr(t, "keep-alive"))
3263  close = false;
3264  }
3265  } else if (!strcmp(buf, "content-type")) {
3266  char* a = strcasestr(t, "charset=");
3267  if (a) {
3268  // find end
3269  char* e = strchr(a + 8, ';');
3270 
3271  QoreString cs;
3272  if (e)
3273  cs.concat(a + 8, e - a - 8);
3274  else
3275  cs.concat(a + 8);
3276  cs.trim();
3277  senc = cs.getBuffer();
3278  //printd(5, "got encoding '%s' from request\n", senc);
3279  enc = QEM.findCreate(senc);
3280 
3281  if (info) {
3282  qore_size_t len = cs.size();
3283  info->setKeyValue("charset", new QoreStringNode(cs.giveBuffer(), len, len + 1, QCS_DEFAULT), nullptr);
3284  }
3285 
3286  if (info) {
3288  // remove any whitespace and ';' before charset=
3289  if (a != t) {
3290  do {
3291  --a;
3292  } while (a > t && (*a == ' ' || *a == ';'));
3293  }
3294 
3295  if (a == t) {
3296  if (e)
3297  ct->concat(e + 1);
3298  } else {
3299  ct->concat(t, a - t + 1);
3300  if (e)
3301  ct->concat(e);
3302  }
3303  ct->trim();
3304  if (!ct->empty())
3305  info->setKeyValue("body-content-type", ct.release(), nullptr);
3306  }
3307  } else {
3308  enc = QEM.findCreate(assume_http_encoding.c_str());
3309  if (info) {
3310  info->setKeyValue("charset", new QoreStringNode(assume_http_encoding), nullptr);
3311  info->setKeyValue("body-content-type", val->refSelf(), nullptr);
3312  }
3313  }
3314  } else if (chunked && !strcmp(buf, "transfer-encoding") && !strcasecmp(t, "chunked")) {
3315  *chunked = true;
3316  } else if (info) {
3317  if (!strcmp(buf, "accept-charset"))
3318  acceptcharset = do_accept_charset(t, *info);
3319  else if ((flags & CHF_REQUEST) && !strcmp(buf, "accept-encoding"))
3320  do_accept_encoding(t, *info);
3321  }
3322  }
3323 
3324  ReferenceHolder<> val_copy(nullptr);
3325  if (raw_hdr && val) {
3326  val_copy = val->realCopy();
3327  }
3328 
3329  // see if header exists, and if so make it a list and add value to the list
3330  hash_assignment_priv ha(*h, buf);
3331  if (!(*ha).isNothing()) {
3332  QoreListNode* l;
3333  if ((*ha).getType() == NT_LIST) {
3334  l = (*ha).get<QoreListNode>();
3335  } else {
3336  l = new QoreListNode(autoTypeInfo);
3337  l->push(ha.swap(l), nullptr);
3338  }
3339  l->push(val.release(), nullptr);
3340  } else // otherwise set header normally
3341  ha.assign(val.release(), 0);
3342 
3343  // set raw headers if applicable
3344  if (raw_hdr) {
3345  hash_assignment_priv ha(*raw_hdr, raw_key);
3346  if (!(*ha).isNothing()) {
3347  QoreListNode* l;
3348  if ((*ha).getType() == NT_LIST) {
3349  l = (*ha).get<QoreListNode>();
3350  } else {
3351  l = new QoreListNode(autoTypeInfo);
3352  l->push(ha.swap(l), nullptr);
3353  }
3354  l->push(val_copy.release(), nullptr);
3355  } else // otherwise set header normally
3356  ha.assign(val_copy.release(), nullptr);
3357  }
3358  }
3359 
3360  if ((flags & CHF_PROCESS)) {
3361  if (!senc)
3362  enc = QEM.findCreate(assume_http_encoding.c_str());
3363  // according to RFC-2616 section 14.2, "If no Accept-Charset header is present, the default is that any character set is acceptable" so we will use utf-8
3364  if (info && !acceptcharset)
3365  info->setKeyValue("accept-charset", new QoreStringNode("utf8"), nullptr);
3366  }
3367 
3368  return close;
3369  }
3370 
3371  DLLLOCAL int recvix(const char* meth, int len, void* targ, int timeout_ms, ExceptionSink* xsink) {
3372  assert(xsink);
3373  if (sock == QORE_INVALID_SOCKET) {
3374  se_not_open("Socket", meth, xsink);
3375  return QSE_NOT_OPEN;
3376  }
3377  if (in_op >= 0) {
3378  if (in_op == gettid()) {
3379  se_in_op("Socket", meth, xsink);
3380  return 0;
3381  }
3382  se_in_op_thread("Socket", meth, xsink);
3383  return 0;
3384  }
3385 
3386  PrivateQoreSocketThroughputHelper th(this, false);
3387 
3388  char* buf;
3389  qore_offset_t br = 0;
3390  while (true) {
3391  qore_offset_t rc = brecv(xsink, meth, buf, len - br, 0, timeout_ms);
3392  if (rc <= 0) {
3393  do_read_error(rc, meth, timeout_ms, xsink);
3394  return (int)rc;
3395  }
3396 
3397  memcpy(targ, buf, rc);
3398 
3399  br += rc;
3400  if (br >= len)
3401  break;
3402  }
3403 
3404  th.finalize(br);
3405  do_data_event(QORE_EVENT_SOCKET_DATA_READ, QORE_SOURCE_SOCKET, targ, br);
3406  return (int)br;
3407  }
3408 
3409  DLLLOCAL void clearWarningQueue(ExceptionSink* xsink) {
3410  if (warn_queue) {
3411  if (warn_callback_arg) {
3412  warn_callback_arg.discard(xsink);
3413  warn_callback_arg = QoreValue();
3414  }
3415  warn_queue->deref(xsink);
3416  warn_queue = nullptr;
3417  tl_warning_us = 0;
3418  tp_warning_bs = 0.0;
3419  tp_us_min = 0;
3420  }
3421  }
3422 
3423  DLLLOCAL void setWarningQueue(ExceptionSink* xsink, int64 warning_ms, int64 warning_bs, Queue* wq, QoreValue arg, int64 min_ms = 1000) {
3424  ReferenceHolder<Queue> qholder(wq, xsink);
3425  ValueHolder holder(arg, xsink);
3426  if (warning_ms <= 0 && warning_bs <= 0) {
3427  xsink->raiseException("SOCKET-SETWARNINGQUEUE-ERROR", "Socket::setWarningQueue() at least one of warning ms argument: " QLLD " and warning B/s argument: " QLLD " must be greater than zero; to clear, call Socket::clearWarningQueue() with no arguments", warning_ms, warning_bs);
3428  return;
3429  }
3430 
3431  if (warning_ms < 0)
3432  warning_ms = 0;
3433  if (warning_bs < 0)
3434  warning_bs = 0;
3435 
3436  if (warn_queue) {
3437  warn_queue->deref(xsink);
3438  warn_callback_arg.discard(xsink);
3439  }
3440 
3441  warn_queue = qholder.release();
3442  warn_callback_arg = holder.release();
3443  tl_warning_us = (int64)warning_ms * 1000;
3444  tp_warning_bs = warning_bs;
3445  tp_us_min = min_ms * 1000;
3446  }
3447 
3448  DLLLOCAL void getUsageInfo(QoreHashNode& h, qore_socket_private& s) const {
3449  if (warn_queue) {
3450  h.setKeyValue("arg", warn_callback_arg.refSelf(), 0);
3451  h.setKeyValue("timeout", tl_warning_us, 0);
3452  h.setKeyValue("min_throughput", (int64)tp_warning_bs, 0);
3453  h.setKeyValue("min_throughput_us", (int64)tp_us_min, 0);
3454  }
3455 
3456  h.setKeyValue("bytes_sent", tp_bytes_sent + s.tp_bytes_sent, 0);
3457  h.setKeyValue("bytes_recv", tp_bytes_recv + s.tp_bytes_sent, 0);
3458  h.setKeyValue("us_sent", tp_us_sent + s.tp_us_sent, 0);
3459  h.setKeyValue("us_recv", tp_us_recv + s.tp_us_recv, 0);
3460  }
3461 
3462  DLLLOCAL void getUsageInfo(QoreHashNode& h) const {
3463  if (warn_queue) {
3464  h.setKeyValue("arg", warn_callback_arg.refSelf(), 0);
3465  h.setKeyValue("timeout", tl_warning_us, 0);
3466  h.setKeyValue("min_throughput", (int64)tp_warning_bs, 0);
3467  h.setKeyValue("min_throughput_us", (int64)tp_us_min, 0);
3468  }
3469 
3470  h.setKeyValue("bytes_sent", tp_bytes_sent, 0);
3471  h.setKeyValue("bytes_recv", tp_bytes_recv, 0);
3472  h.setKeyValue("us_sent", tp_us_sent, 0);
3473  h.setKeyValue("us_recv", tp_us_recv, 0);
3474  }
3475 
3476  DLLLOCAL QoreHashNode* getUsageInfo() const {
3477  QoreHashNode* h = new QoreHashNode(autoTypeInfo);
3478  getUsageInfo(*h);
3479  return h;
3480  }
3481 
3482  DLLLOCAL void clearStats() {
3483  tp_bytes_sent = 0;
3484  tp_bytes_recv = 0;
3485  tp_us_sent = 0;
3486  tp_us_recv = 0;
3487  }
3488 
3489  DLLLOCAL void doTimeoutWarning(const char* op, int64 dt) {
3490  assert(warn_queue);
3491  assert(dt > tl_warning_us);
3492 
3493  QoreHashNode* h = new QoreHashNode(autoTypeInfo);
3494 
3495  h->setKeyValue("type", new QoreStringNode("SOCKET-OPERATION-WARNING"), 0);
3496  h->setKeyValue("operation", new QoreStringNode(op), 0);
3497  h->setKeyValue("us", dt, 0);
3498  h->setKeyValue("timeout", tl_warning_us, 0);
3499  if (warn_callback_arg)
3500  h->setKeyValue("arg", warn_callback_arg.refSelf(), 0);
3501 
3502  warn_queue->pushAndTakeRef(h);
3503  }
3504 
3505  DLLLOCAL void doThroughputWarning(bool send, int64 bytes, int64 dt, double bs) {
3506  assert(warn_queue);
3507  assert(bs < tp_warning_bs);
3508 
3509  QoreHashNode* h = new QoreHashNode(autoTypeInfo);
3510 
3511  h->setKeyValue("type", new QoreStringNode("SOCKET-THROUGHPUT-WARNING"), 0);
3512  h->setKeyValue("dir", new QoreStringNode(send ? "send" : "recv"), 0);
3513  h->setKeyValue("bytes", bytes, 0);
3514  h->setKeyValue("us", dt, 0);
3515  h->setKeyValue("bytes_sec", bs, 0);
3516  h->setKeyValue("threshold", (int64)tp_warning_bs, 0);
3517  if (warn_callback_arg)
3518  h->setKeyValue("arg", warn_callback_arg.refSelf(), 0);
3519 
3520  warn_queue->pushAndTakeRef(h);
3521  }
3522 
3523  DLLLOCAL bool pendingHttpChunkedBody() const {
3524  return http_exp_chunked_body && sock != QORE_INVALID_SOCKET;
3525  }
3526 
3527  DLLLOCAL void setSslVerifyMode(int mode) {
3528  //printd(5, "qore_socket_private::setSslVerifyMode() this: %p mode: %d\n", this, mode);
3529  ssl_verify_mode = mode;
3530  if (ssl)
3531  ssl->setVerifyMode(ssl_verify_mode, ssl_accept_all_certs, client_target);
3532  }
3533 
3534  DLLLOCAL void acceptAllCertificates(bool accept_all = true) {
3535  ssl_accept_all_certs = accept_all;
3536  if (ssl)
3537  ssl->setVerifyMode(ssl_verify_mode, ssl_accept_all_certs, client_target);
3538  }
3539 
3540  DLLLOCAL void setSslErrorString(QoreStringNode* err_str) {
3541  if (ssl_err_str) {
3542  ssl_err_str->concat("; ");
3543  ssl_err_str->concat(err_str);
3544  err_str->deref();
3545  } else {
3546  ssl_err_str = err_str;
3547  }
3548  }
3549 
3550  DLLLOCAL static void getUsageInfo(const QoreSocket& sock, QoreHashNode& h, const QoreSocket& s) {
3551  sock.priv->getUsageInfo(h, *s.priv);
3552  }
3553 
3554  DLLLOCAL static qore_socket_private* get(QoreSocket& sock) {
3555  return sock.priv;
3556  }
3557 
3558  DLLLOCAL static const qore_socket_private* get(const QoreSocket& sock) {
3559  return sock.priv;
3560  }
3561 
3562  DLLLOCAL static void captureRemoteCert(X509_STORE_CTX* x509_ctx);
3563 };
3564 
3565 #endif
const qore_type_t NT_BINARY
type value for BinaryNode
Definition: node_types.h:49
DLLEXPORT const char * c_str() const
returns the string&#39;s buffer; this data should not be changed
Qore&#39;s arbitrary-precision number value type, dynamically-allocated only, reference counted...
Definition: QoreNumberNode.h:51
defines string encoding functions in Qore
Definition: QoreEncoding.h:83
a helper class for getting socket origination information
Definition: QoreSocket.h:73
DLLEXPORT bool empty() const
returns true if the string is empty, false if not
This is the hash or associative list container type in Qore, dynamically allocated only...
Definition: QoreHashNode.h:50
DLLEXPORT const QoreEncoding * QCS_DEFAULT
the default encoding for the Qore library
DLLEXPORT AbstractQoreNode * raiseExceptionArg(const char *err, QoreValue arg, const char *fmt,...)
appends a Qore-language exception to the list, and sets the &#39;arg&#39; member (this object takes over the ...
DLLEXPORT void set(const char *str, const QoreEncoding *new_qorecharset=QCS_DEFAULT)
copies the c-string passed and sets the value of the string and its encoding
DLLEXPORT int gettid() noexcept
returns the current TID number
const qore_type_t NT_LIST
type value for QoreListNode
Definition: node_types.h:50
DLLEXPORT bool empty() const
returns true if empty
DLLEXPORT char * giveBuffer()
returns the character buffer and leaves the QoreString empty, the caller owns the memory returned (mu...
DLLEXPORT int sprintf(const char *fmt,...)
this will concatentate a formatted string to the existing string according to the format string and t...
The base class for all value and parse types in Qore expression trees.
Definition: AbstractQoreNode.h:54
const qore_type_t NT_NOTHING
type value for QoreNothingNode
Definition: node_types.h:42
DLLEXPORT int setKeyValue(const char *key, QoreValue value, ExceptionSink *xsink)
sets the value of "key" to "value"
virtual int64 read(void *ptr, int64 limit, ExceptionSink *xsink)=0
Reads up to `limit` bytes from the input stream.
size_t qore_size_t
used for sizes (same range as a pointer)
Definition: common.h:73
DLLEXPORT int getInfo(ExceptionSink *xsink, const char *node, const char *service, int family=Q_AF_UNSPEC, int flags=0, int socktype=Q_SOCK_STREAM, int protocol=0)
get address info with the given parameters, if any errors occur, a Qore-language exception is thrown ...
DLLEXPORT QoreHashNode * hashRefSelf() const
returns "this" with an incremented reference count
DLLLOCAL T * release()
releases the pointer to the caller
Definition: ReferenceHolder.h:91
DLLLOCAL hashdecl addrinfo * getAddrInfo() const
returns the hashdecl addrinfo * being managed (may by 0)
Definition: QoreNet.h:159
DLLEXPORT AbstractQoreNode * raiseException(const char *err, const char *fmt,...)
appends a Qore-language exception to the list
DLLEXPORT AbstractQoreNode * raiseErrnoException(const char *err, int en, const char *fmt,...)
appends a Qore-language exception to the list and appends the result of strerror(errno) to the descri...
provides an interface to getaddrinfo
Definition: QoreNet.h:132
DLLEXPORT int push(QoreValue val, ExceptionSink *xsink)
adds a value to the list
const qore_type_t NT_NULL
type value for QoreNullNode
Definition: node_types.h:48
Interface for private data of output streams.
Definition: OutputStream.h:44
DLLEXPORT AbstractQoreNode * refSelf() const
returns "this" with an incremented reference count
Qore&#39;s string type supported by the QoreEncoding class.
Definition: QoreString.h:81
Qore&#39;s string value type, reference counted, dynamically-allocated only.
Definition: QoreStringNode.h:50
virtual void write(const void *ptr, int64 count, ExceptionSink *xsink)=0
Writes bytes to the output stream.
DLLEXPORT const char * getBuffer() const
returns the string&#39;s buffer; this data should not be changed
static DLLEXPORT const char * getFamilyName(int address_family)
returns the name of the address family as a string (ie AF_INET = "ipv4", etc)
DLLEXPORT void concat(const QoreString *str, ExceptionSink *xsink)
concatenates a string and converts encodings if necessary
This is the list container type in Qore, dynamically allocated only, reference counted.
Definition: QoreListNode.h:52
DLLEXPORT QoreEncodingManager QEM
the QoreEncodingManager object
DLLEXPORT qore_size_t strlen() const
returns number of bytes in the string (not including the null pointer)
const qore_type_t NT_HASH
type value for QoreHashNode
Definition: node_types.h:51
static DLLEXPORT const QoreEncoding * findCreate(const char *name)
finds an encoding if it exists (also looks up against alias names) and creates a new one if it doesn&#39;...
virtual DLLLOCAL QoreValue execValue(const QoreListNode *args, ExceptionSink *xsink) const =0
pure virtual function for executing the function reference
The main value class in Qore, designed to be passed by value.
Definition: QoreValue.h:262
const qore_type_t NT_STRING
type value for QoreStringNode
Definition: node_types.h:45
provides access to sockets using Qore data structures
Definition: QoreSocket.h:126
virtual DLLEXPORT AbstractQoreNode * realCopy() const =0
returns a copy of the object; the caller owns the reference count
the implementation of Qore&#39;s object data type, reference counted, dynamically-allocated only ...
Definition: QoreObject.h:61
DLLEXPORT bool empty() const
returns true if the hash has no members, false if not
DLLEXPORT void clear()
frees any managed memory and sets the size to 0
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:46
Interface for private data of input streams.
Definition: InputStream.h:44
const qore_type_t NT_FLOAT
type value for floating-point values (QoreValue only)
Definition: node_types.h:44
DLLEXPORT QoreStringNode * q_strerror(int errnum)
returns the error string as a QoreStringNode
const qore_type_t NT_BOOLEAN
type value for bools (QoreValue only)
Definition: node_types.h:47
static void strtolower(char *str)
convert a string to lower-case in place
Definition: QoreLib.h:267
const qore_type_t NT_INT
type value for integers (QoreValue only)
Definition: node_types.h:43
DLLEXPORT void deref(ExceptionSink *xsink)
decrements the reference count and calls derefImpl() if there_can_be_only_one is false, otherwise does nothing
long long int64
64bit integer type, cannot use int64_t here since it breaks the API on some 64-bit systems due to equ...
Definition: common.h:260
DLLEXPORT const void * getPtr() const
returns the pointer to the data
intptr_t qore_offset_t
used for offsets that could be negative
Definition: common.h:76
DLLEXPORT void trim(const char *chars=0)
remove leading and trailing whitespace or other characters
base class for resolved call references
Definition: CallReferenceNode.h:105
#define QORE_MIN(a, b)
macro to return the minimum of 2 numbers
Definition: QoreLib.h:535
DLLEXPORT qore_size_t size() const
returns the number of members in the hash, executes in constant time
constant iterator class for QoreHashNode, to be only created on the stack
Definition: QoreHashNode.h:563
provides a mutually-exclusive thread lock
Definition: QoreThreadLock.h:47
static DLLEXPORT QoreStringNode * getAddressDesc(int address_family, const char *addr)
returns a descriptive string for the address family and an address string (ie AF_INET6, "::1" = "ipv6[::1]")
const qore_type_t NT_NUMBER
type value for QoreNumberNode
Definition: node_types.h:53
DLLEXPORT void setValue(const char *key, QoreValue val, ExceptionSink *xsink)
sets the value of the given member to the given value
DLLEXPORT qore_size_t size() const
returns the number of bytes in the object
DLLEXPORT void clear()
reset string to zero length; memory is not deallocated; string encoding does not change ...
DLLEXPORT qore_size_t size() const
returns number of bytes in the string (not including the null pointer)
holds an object and dereferences it in the destructor
Definition: QoreValue.h:452
DLLEXPORT QoreValue refSelf() const
references the contained value if type == QV_Node, returns itself
DLLEXPORT void append(const void *nptr, qore_size_t size)
resizes the object and appends a copy of the data passed to the object
DLLEXPORT void discard(ExceptionSink *xsink)
dereferences any contained AbstractQoreNode pointer and sets to 0; does not modify other values ...
provides a safe and exception-safe way to release and re-acquire locks in Qore, only to be used on th...
Definition: QoreThreadLock.h:187
holds arbitrary binary data
Definition: BinaryNode.h:41
For use on the stack only: iterates through elements of a const QoreListNode.
Definition: QoreListNode.h:566