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