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