/* * format up an HTTP response and return it */ DBUF *server_respond (int code, char *fmt, ...) { int l, len, avail; va_list ap; char *ch; DBUF *b; char buf[4096]; /* * note our response body is limited to 4096 bytes - only small * response is needed or allowed. */ len = sprintf (buf, "<html><body>"); avail = 4096 - (len * 2 + 4); va_start (ap, fmt); l = vsnprintf (buf + len, avail, fmt, ap); va_end (ap); if ((l > 0) && (l < avail)) len += l; len += sprintf (buf + len, "</body></html>"); /* * if we are no longer running, notify the client that we are * closing this connection. */ b = dbuf_alloc (); dbuf_printf (b, "Status: %d\r\n" "Content-Length: %d\r\n" "Connection: %s\r\n\r\n%s", code, len, phineas_running () ? "Keep-alive" : "Close", buf); debug ("response:\n%s\n", dbuf_getbuf (b)); return (b); }
/* * Poll all queues... * a thread, expected to be started from the TASKQ. Note you must * re-register processors once this task exits. * * Note we expect sender_xml to have QueueInfo embedded! */ int qpoller_task (void *parm) { int i, poll_interval, num_queues; QPOLLER *p; QPOLLERJOB *j; TASKQ *q; XML *xml = (XML *) parm; info ("Queue Poller starting\n"); num_queues = xml_count (xml, QP_QUEUE); if ((poll_interval = xml_get_int (xml, QP_INFO".PollInterval")) < 1) poll_interval = 5; poll_interval *= 1000; if ((i = xml_get_int (xml, QP_INFO".MaxThreads")) < 1) i = 1; q = task_allocq (i, poll_interval); debug ("%d queues %d interval\n", num_queues, poll_interval); while (phineas_running ()) { for (i = 0; i < num_queues; i++) { qpoller_poll (xml, i, q); } sleep (poll_interval); } debug ("Queue Poller shutting down...\n"); task_stop (q); task_freeq (q); while ((j = QpollerJobs) != NULL) { QpollerJobs = j->next; free (j); } while ((p = Qpoller) != NULL) { Qpoller = p->next; free (p); } info ("Queue Poller exiting\n"); return (0); }
/* * Listen for incoming connections until told to stop */ int server_listen (XML *xml, NETCON *conn, NETCON *ssl, SSL_CTX *ctx, int threads) { char *ch; TASKQ *t; struct timeval timeout; fd_set fds; if ((conn == NULL) && (ssl == NULL)) return (-1); t = task_allocq (threads, 2); timeout.tv_sec = 2; timeout.tv_usec = 0; /* * Keep servicing requests until they stop coming in AND we are * no longer running. This insures a nice shutdown, although a * naughty client could keep us from shutting down by flooding us * with requests. We could add a counter here to prevent that. */ while (1) { FD_ZERO (&fds); if (conn != NULL) FD_SET (conn->sock, &fds); if (ssl != NULL) FD_SET (ssl->sock, &fds); if (select (2, &fds, NULL, NULL, &timeout) <= 0) { if (phineas_running ()) continue; break; } if ((conn != NULL) && FD_ISSET (conn->sock, &fds)) server_accept (xml, conn, NULL, t); if ((ssl != NULL) && FD_ISSET (ssl->sock, &fds)) server_accept (xml, ssl, ctx, t); } task_stop (t); task_freeq (t); return (0); }
/* * send a message * return non-zero if message not sent successful with completed * queue info for status and transport */ int ebxml_send (XML*xml, QUEUEROW *r, MIME *msg) { DBUF *b; NETCON *conn; char host[MAX_PATH]; /* need buffers for redirect */ char path[MAX_PATH]; int port, route, timeout, delay, retry; SSL_CTX *ctx; char *rname, /* route name */ *content, /* message content */ buf[MAX_PATH]; /* format up the message */ if ((content = mime_format (msg)) == NULL) { queue_field_set (r, "PROCESSINGSTATUS", "done"); queue_field_set (r, "TRANSPORTSTATUS", "failed"); queue_field_set (r, "TRANSPORTERRORCODE", "failed formatting message"); return (-1); } debug ("Send content:\n%s\n", content); /* * get connection info from the record route */ if ((route = cfg_route_index (xml, queue_field_get (r, "ROUTEINFO"))) < 0) { queue_field_set (r, "PROCESSINGSTATUS", "done"); queue_field_set (r, "TRANSPORTSTATUS", "failed"); queue_field_set (r, "TRANSPORTERRORCODE", "bad route"); return (-1); } rname = cfg_route (xml, route, "Name"); ctx = ebxml_route_ctx (xml, route); strcpy (host, cfg_route (xml, route, "Host")); port = atoi (cfg_route (xml, route, "Port")); if ((retry = atoi (cfg_route (xml, route, "Retry"))) == 0) retry = cfg_retries (xml); timeout = atoi (cfg_route (xml, route, "Timeout")); delay = cfg_delay (xml); strcpy (path, cfg_route (xml, route, "Path")); sendmsg: info ("Sending ebXML %s:%d to %s\n", r->queue->name, r->rowid, rname); debug ("opening connection socket on port=%d retrys=%d timeout=%d\n", port, retry, timeout); if ((conn = net_open (host, port, 0, ctx)) == NULL) { error ("failed opening connection to %s:%d\n", host, port); goto retrysend; } /* set read timeout if given */ if (timeout) { net_timeout (conn, timeout * 1000); timeout <<= 1; /* bump each try */ } delay = 0; /* connection OK, don't delay */ queue_field_set (r, "MESSAGESENTTIME", ptime (NULL, buf)); sprintf (buf, "POST %s HTTP/1.1\r\n", path); // ch = ebxml_beautify (ch); /* all set... send the message */ debug ("sending message...\n"); net_write (conn, buf, strlen (buf)); net_write (conn, content, strlen (content)); debug ("reading response...\n"); b = ebxml_receive (conn); debug ("closing socket...\n"); net_close (conn); /* no reply? */ if (b == NULL) { warn ("Send response timed out or closed for %s\n", rname); retrysend: /* retry with a wait, or.. */ if (retry-- && phineas_running ()) { if (delay) { info ("Retrying send to %s in %d seconds\n", rname, delay); sleep (delay * 1000); delay <<= 1; } else /* reset connection delay */ delay = cfg_delay (xml); goto sendmsg; } if (ctx != NULL) /* give up! */ SSL_CTX_free (ctx); free (content); queue_field_set (r, "PROCESSINGSTATUS", "done"); queue_field_set (r, "TRANSPORTSTATUS", "failed"); queue_field_set (r, "TRANSPORTERRORCODE", "retries exhausted"); return (-1); } debug ("reply was %d bytes\n%.*s\n", dbuf_size (b), dbuf_size (b), dbuf_getbuf (b)); /* * handle redirects... * note this assumes the same SSL context should be used */ if (ebxml_redirect (dbuf_getbuf (b), host, &port, path)) { dbuf_free (b); goto sendmsg; } if (ctx != NULL) SSL_CTX_free (ctx); if (ebxml_parse_reply (dbuf_getbuf (b), r)) { queue_field_set (r, "PROCESSINGSTATUS", "done"); queue_field_set (r, "TRANSPORTSTATUS", "failed"); queue_field_set (r, "TRANSPORTERRORCODE", "garbled reply"); } debug ("send completed\n"); dbuf_free (b); free (content); return (0); }