/* * format a mime message - this is the recursive on, not intended * for external use. */ int mime_format_part (MIME *m, DBUF *b) { MIME *n; char boundary[100]; int bl; mime_size (m); dbuf_printf (b, "%s", m->headers); dbuf_write (b, m->body, m->len); if ((bl = mime_getBoundary (m, boundary, 100)) < 1) return (dbuf_size (b)); boundary[bl++] = '\n'; for (n = m->next; n != NULL; n = n->next) { debug ("adding next part at %d\n", dbuf_size (b)); dbuf_write (b, boundary, bl); mime_format_part (n, b); } debug ("adding final boundary at %d\n", dbuf_size (b)); dbuf_write (b, boundary, bl - 1); dbuf_write (b, "--\n", 4); debug ("final size is %d\n", dbuf_size (b)); debug ("actual size is %d (%d/%d)\n", strlen (dbuf_getbuf(b)), strlen (m->headers), strlen (dbuf_getbuf(b)) - strlen (m->headers)); return (dbuf_size (b)); }
/* * 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); }
/* * TASK to handle an incoming request */ int server_request (void *parm) { SERVERPARM *s; DBUF *req, *res; char *curl; s = (SERVERPARM *) parm; curl = xml_get_text (s->xml, "Phineas.Console.Url"); res = NULL; while ((req = server_receive (s->conn)) != NULL) { debug ("received %d bytes\n", dbuf_size (req)); if (dbuf_size (req) == 0) { dbuf_free (req); net_close (s->conn); return (-1); } dbuf_putc (req, 0); /* * log the request, but filter out GET requests for the console... * noise */ if (!(*curl && strstarts (dbuf_getbuf (req) + 4, curl))) server_logrequest (s->conn, dbuf_size (req), dbuf_getbuf (req)); if ((res = server_response (s->xml, dbuf_getbuf (req))) == NULL) { res = server_respond (500, "<h3>Failure processing ebXML request</h3>"); } server_header (res); net_write (s->conn, dbuf_getbuf (res), dbuf_size (res)); dbuf_free (res); dbuf_free (req); } net_close (s->conn); debug ("request completed\n"); return (0); }
/* * set any needed header info */ int server_header (DBUF *b) { char *ch, *status; int l, code = 200; char buf[120]; /* find the end of the current header and get return code */ if (strstarts (ch = dbuf_getbuf (b), "Status:")) code = atoi (ch + 8); l = 0; while (1) { if (l > dbuf_size (b) - 2) { l = 0; break; } if (ch[l++] != '\n') continue; if (ch[l] == '\r') l++; if (ch[l] == '\n') break; if (strstarts (ch + l, "Status:")) code = atoi (ch + l + 8); } if (code < 300) status = "OK"; else if (code == 401) status = "Authorization Required"; else if (code < 500) status = "NOT FOUND"; else status = "SERVER ERROR"; l = sprintf (buf, "HTTP/1.1 %d %s\r\n%s", code, status, l ? "" : "\r\n"); debug ("inserting header %s", buf); dbuf_insert (b, 0, buf, l); 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); }
/* * Receive a reply message */ DBUF *ebxml_receive (NETCON *conn) { long n; int e; char c, *ch; DBUF *b; b = dbuf_alloc (); n = 0; /* * read to end of message request header - empty line */ while ((e = net_read (conn, &c, 1)) == 1) { dbuf_putc (b, c); if (c == '\n') { if (n++ == 1) break; } else if (c != '\r') n = 0; } if (e != 1) { if (e == 0) error ("Acknowledgment header read failed or connection closed\n"); else error ("Timed out reading acknowledgment header\n"); dbuf_free (b); return (NULL); } ch = strnstr (dbuf_getbuf (b), "Content-Length: ", dbuf_size (b)); if (ch == NULL) { error ("Acknowledgment header missing Content-Length\n"); dbuf_free (b); return (NULL); } n = atol (ch + 16); debug ("expecting %d bytes\n", n); readbytes: while (n--) { if ((e = net_read (conn, &c, 1)) != 1) { if (e == 0) error ("Acknowledgment content read failed or connection closed"); else error ("Timed out reading acknowledgment content\n"); // note we'll take what we get and hope it's enough... break; } dbuf_putc (b, c); } if (n = net_available (conn)) { warn ("Found %d unread bytes...\n", n); goto readbytes; } debug ("returning %d bytes\n", dbuf_size (b)); return (b); }
/* * Receive an incoming message */ DBUF *server_receive (NETCON *conn) { long n, sz; char c, *ch; DBUF *b; b = dbuf_alloc (); n = 0; /* * read to end of message request header - empty line */ while (net_read (conn, &c, 1) == 1) { dbuf_putc (b, c); if (c == '\n') { if (n++ == 1) break; } else if (c != '\r') n = 0; } if (n != 2) { debug ("no header - %d read\n", dbuf_size (b)); if (dbuf_size (b)) { dbuf_printf (b, "%s", n == 1 ? "\r\n" : "\r\n\r\n"); warn ("can't find end of request header: %.*s", dbuf_size (b), dbuf_getbuf (b)); } n = 0; } else { ch = strnstr (dbuf_getbuf (b), "Content-Length: ", dbuf_size (b)); if (ch == NULL) n = 0; else n = atol (ch + 16); } debug ("expecting %d bytes\n", n); readbytes: while (n--) { if ((sz = net_read (conn, &c, 1)) != 1) { if (sz == 0) error ("Read failed or connection closed\n"); else error ("Read timed out\n"); // note we'll take what we get and hope it's enough... break; } dbuf_putc (b, c); } if (n = net_available (conn)) { warn ("Found %d unread bytes...\n", n); goto readbytes; } debug ("returning %d bytes\n", dbuf_size (b)); return (b); }