3 * Copyright (c) 1987-2021 by the citadel.org team
5 * This program is open source software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License, version 3.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
15 * Uncomment this to log all communications with the Citadel server
20 #include "webserver.h"
22 long MaxRead = -1; /* should we do READ scattered or all at once? */
25 * register the timeout
27 RETSIGTYPE timeout(int signum) {
28 syslog(LOG_WARNING, "Connection timed out; unable to reach citserver\n");
29 /* no exit here, since we need to server the connection unreachable thing. exit(3); */
34 * Client side - connect to a unix domain socket
36 int connect_to_citadel(char *sockpath) {
37 struct sockaddr_un addr;
40 memset(&addr, 0, sizeof(addr));
41 addr.sun_family = AF_UNIX;
42 strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
44 s = socket(AF_UNIX, SOCK_STREAM, 0);
46 syslog(LOG_WARNING, "Can't create socket [%s]: %s\n", sockpath, strerror(errno));
50 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
51 syslog(LOG_WARNING, "Can't connect [%s]: %s\n", sockpath, strerror(errno));
60 * input string from pipe
62 int serv_getln(char *strbuf, int bufsize) {
66 StrBuf_ServGetln(WC->MigrateReadLineBuf);
67 len = StrLength(WC->MigrateReadLineBuf);
70 memcpy(strbuf, ChrPtr(WC->MigrateReadLineBuf), len);
71 FlushStrBuf(WC->MigrateReadLineBuf);
74 syslog(LOG_DEBUG, "%3d<<<%s\n", WC->serv_sock, strbuf);
80 int StrBuf_ServGetln(StrBuf * buf) {
81 const char *ErrStr = NULL;
88 rc = StrBufTCP_read_buffered_line_fast(buf, WC->ReadBuf, &WC->ReadPos, &WC->serv_sock, 5, 1, &ErrStr);
90 syslog(LOG_INFO, "StrBuf_ServGetln(): Server connection broken: %s\n", (ErrStr) ? ErrStr : "");
91 wc_backtrace(LOG_INFO);
92 if (WC->serv_sock > 0)
101 if (WC->ReadPos != NULL)
102 pos = WC->ReadPos - ChrPtr(WC->ReadBuf);
103 syslog(LOG_DEBUG, "%3d<<<[%ld]%s\n", WC->serv_sock, pos, ChrPtr(buf));
109 int StrBuf_ServGetBLOBBuffered(StrBuf * buf, long BlobSize) {
113 rc = StrBufReadBLOBBuffered(buf, WC->ReadBuf, &WC->ReadPos, &WC->serv_sock, 1, BlobSize, NNN_TERM, &ErrStr);
115 syslog(LOG_INFO, "StrBuf_ServGetBLOBBuffered(): Server connection broken: %s\n", (ErrStr) ? ErrStr : "");
116 wc_backtrace(LOG_INFO);
117 if (WC->serv_sock > 0)
118 close(WC->serv_sock);
119 WC->serv_sock = (-1);
125 syslog(LOG_DEBUG, "%3d<<<BLOB: %d bytes\n", WC->serv_sock, StrLength(buf));
131 int StrBuf_ServGetBLOB(StrBuf * buf, long BlobSize) {
136 rc = StrBufReadBLOB(buf, &WC->serv_sock, 1, BlobSize, &ErrStr);
138 syslog(LOG_INFO, "StrBuf_ServGetBLOB(): Server connection broken: %s\n", (ErrStr) ? ErrStr : "");
139 wc_backtrace(LOG_INFO);
140 if (WC->serv_sock > 0)
141 close(WC->serv_sock);
142 WC->serv_sock = (-1);
148 syslog(LOG_DEBUG, "%3d<<<BLOB: %d bytes\n", WC->serv_sock, StrLength(buf));
155 void FlushReadBuf(void) {
160 len = StrLength(WC->ReadBuf);
161 if ((len > 0) && (WC->ReadPos != NULL) && (WC->ReadPos != StrBufNOTNULL)) {
162 pch = ChrPtr(WC->ReadBuf);
164 if (WC->ReadPos != pche) {
166 "ERROR: somebody didn't eat his soup! Remaing Chars: %ld [%s]\n", (long) (pche - WC->ReadPos), pche);
168 "--------------------------------------------------------------------------------\n"
170 "--------------------------------------------------------------------------------\n", pch);
171 AppendImportantMessage(HKEY
172 ("Suppenkasper alert! watch your webcit logfile and get connected to your favourite opensource Crew."));
176 FlushStrBuf(WC->ReadBuf);
184 * send binary to server
185 * buf the buffer to write to citadel server
186 * nbytes how many bytes to send to citadel server
188 int serv_write(const char *buf, int nbytes) {
189 int bytes_written = 0;
193 while (bytes_written < nbytes) {
194 retval = write(WC->serv_sock, &buf[bytes_written], nbytes - bytes_written);
196 const char *ErrStr = strerror(errno);
197 syslog(LOG_INFO, "serv_write(): Server connection broken: %s\n", (ErrStr) ? ErrStr : "");
198 if (WC->serv_sock > 0)
199 close(WC->serv_sock);
200 WC->serv_sock = (-1);
205 bytes_written = bytes_written + retval;
212 * send line to server
213 * string the line to send to the citadel server
215 int serv_puts(const char *string) {
217 syslog(LOG_DEBUG, "%3d>>>%s\n", WC->serv_sock, string);
221 if (!serv_write(string, strlen(string)))
223 return serv_write("\n", 1);
227 * send line to server
228 * string the line to send to the citadel server
230 int serv_putbuf(const StrBuf * string) {
232 syslog(LOG_DEBUG, "%3d>>>%s\n", WC->serv_sock, ChrPtr(string));
236 if (!serv_write(ChrPtr(string), StrLength(string)))
238 return serv_write("\n", 1);
243 * convenience function to send stuff to the server
244 * format the formatstring
245 * ... the entities to insert into format
247 int serv_printf(const char *format, ...) {
255 va_start(arg_ptr, format);
256 vsnprintf(buf, sizeof buf, format, arg_ptr);
262 rc = serv_write(buf, len);
264 syslog(LOG_DEBUG, ">>>%s", buf);
271 * Read binary data from server into memory using a series of server READ commands.
272 * returns the read content as StrBuf
274 int serv_read_binary(StrBuf * Ret, size_t total_len, StrBuf * Buf) {
275 size_t bytes_read = 0;
276 size_t this_block = 0;
284 while ((bytes_read < total_len) && (ServerRc == 6)) {
286 if (WC->serv_sock == -1) {
291 serv_printf("READ " SIZE_T_FMT "|" SIZE_T_FMT, bytes_read, total_len - bytes_read);
292 if ((rc = StrBuf_ServGetln(Buf) > 0) && (ServerRc = GetServerStatus(Buf, NULL), ServerRc == 6)) {
295 StrBufCutLeft(Buf, 4);
296 this_block = StrTol(Buf);
297 rc = StrBuf_ServGetBLOBBuffered(Ret, this_block);
299 syslog(LOG_INFO, "Server connection broken during download\n");
300 wc_backtrace(LOG_INFO);
301 if (WC->serv_sock > 0)
302 close(WC->serv_sock);
303 WC->serv_sock = (-1);
312 return StrLength(Ret);
316 int client_write(StrBuf * ThisBuf) {
317 const char *ptr, *eptr;
323 ptr = ChrPtr(ThisBuf);
324 count = StrLength(ThisBuf);
327 fdflags = fcntl(WC->Hdr->http_sock, F_GETFL);
329 while ((ptr < eptr) && (WC->Hdr->http_sock != -1)) {
330 if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
332 FD_SET(WC->Hdr->http_sock, &wset);
333 if (select(WC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) {
334 syslog(LOG_INFO, "client_write: Socket select failed (%s)\n", strerror(errno));
339 if ((WC->Hdr->http_sock == -1) || ((res = write(WC->Hdr->http_sock, ptr, count)), (res == -1))) {
340 syslog(LOG_INFO, "client_write: Socket write failed (%s)\n", strerror(errno));
341 wc_backtrace(LOG_INFO);
351 int read_serv_chunk(StrBuf * Buf, size_t total_len, size_t *bytes_read) {
355 serv_printf("READ " SIZE_T_FMT "|" SIZE_T_FMT, *bytes_read, total_len - (*bytes_read));
356 if ((rc = StrBuf_ServGetln(Buf) > 0) && (ServerRc = GetServerStatus(Buf, NULL), ServerRc == 6)) {
357 size_t this_block = 0;
362 StrBufCutLeft(Buf, 4);
363 this_block = StrTol(Buf);
364 rc = StrBuf_ServGetBLOBBuffered(WC->WBuf, this_block);
366 syslog(LOG_INFO, "Server connection broken during download\n");
367 wc_backtrace(LOG_INFO);
368 if (WC->serv_sock > 0)
369 close(WC->serv_sock);
370 WC->serv_sock = (-1);
380 static inline int send_http(StrBuf * Buf) {
383 return client_write_ssl(Buf);
386 return client_write(Buf);
390 * Read binary data from server into memory using a series of server READ commands.
391 * returns the read content as StrBuf
393 void serv_read_binary_to_http(StrBuf * MimeType, size_t total_len, int is_static, int detect_mime) {
395 size_t bytes_read = 0;
397 int client_con_state = 0;
400 const char *Err = NULL;
401 StrBuf *BufHeader = NULL;
406 IOBuffer WriteBuffer;
411 if (WC->Hdr->HaveRange) {
412 WC->Hdr->HaveRange++;
413 WC->Hdr->TotalBytes = total_len;
414 /* open range? or beyound file border? correct the numbers. */
415 if ((WC->Hdr->RangeTil == -1) || (WC->Hdr->RangeTil >= total_len))
416 WC->Hdr->RangeTil = total_len - 1;
417 bytes_read = WC->Hdr->RangeStart;
418 total_len = WC->Hdr->RangeTil;
421 chunked = total_len > SIZ * 10; /* TODO: disallow for HTTP / 1.0 */
424 BufHeader = NewStrBuf();
427 if ((detect_mime != 0) && (bytes_read != 0)) {
428 /* need to read first chunk to detect mime, though the client doesn't care */
429 size_t bytes_read = 0;
432 ServerRc = read_serv_chunk(Buf, total_len, &bytes_read);
435 FreeStrBuf(&BufHeader);
439 CT = GuessMimeType(SKEY(WC->WBuf));
440 FlushStrBuf(WC->WBuf);
441 StrBufPlain(MimeType, CT, -1);
442 CheckGZipCompressionAllowed(SKEY(MimeType));
447 memset(&WriteBuffer, 0, sizeof(IOBuffer));
448 if (chunked && !DisableGzip && WC->Hdr->HR.gzip_ok) {
450 SC = StrBufNewStreamContext(eZLibEncode, &Err);
452 syslog(LOG_ERR, "Error while initializing stream context: %s", Err);
457 memset(&ReadBuffer, 0, sizeof(IOBuffer));
458 ReadBuffer.Buf = WC->WBuf;
460 WriteBuffer.Buf = NewStrBufPlain(NULL, SIZ * 2);;
461 pBuf = WriteBuffer.Buf;
468 http_transmit_headers(ChrPtr(MimeType), is_static, chunked, is_gzip);
470 if (send_http(WC->HBuf) < 0) {
472 FreeStrBuf(&WriteBuffer.Buf);
473 FreeStrBuf(&BufHeader);
474 if (StrBufDestroyStreamContext(eZLibEncode, &SC, &Err) && Err) {
475 syslog(LOG_ERR, "Error while destroying stream context: %s", Err);
481 while ((bytes_read < total_len) && (ServerRc == 6) && (client_con_state == 0)) {
483 if (WC->serv_sock == -1) {
484 FlushStrBuf(WC->WBuf);
486 FreeStrBuf(&WriteBuffer.Buf);
487 FreeStrBuf(&BufHeader);
488 StrBufDestroyStreamContext(eZLibEncode, &SC, &Err);
489 if (StrBufDestroyStreamContext(eZLibEncode, &SC, &Err) && Err) {
490 syslog(LOG_ERR, "Error while destroying stream context: %s", Err);
495 ServerRc = read_serv_chunk(Buf, total_len, &bytes_read);
503 CT = GuessMimeType(SKEY(WC->WBuf));
504 StrBufPlain(MimeType, CT, -1);
506 CheckGZipCompressionAllowed(SKEY(MimeType));
507 is_gzip = WC->Hdr->HR.gzip_ok;
509 http_transmit_headers(ChrPtr(MimeType), is_static, chunked, is_gzip);
511 client_con_state = send_http(WC->HBuf);
515 int done = (bytes_read == total_len);
516 while ((IOBufferStrLength(&ReadBuffer) > 0) && (client_con_state == 0)) {
520 rc = StrBufStreamTranscode(eZLibEncode, &WriteBuffer, &ReadBuffer, NULL, -1, SC, done,
523 if (StrLength(pBuf) > 0) {
524 StrBufPrintf(BufHeader, "%s%x\r\n", (first) ? "" : "\r\n", StrLength(pBuf));
526 client_con_state = send_http(BufHeader);
527 if (client_con_state == 0) {
528 client_con_state = send_http(pBuf);
532 } while ((rc == 1) && (StrLength(pBuf) > 0));
534 FlushStrBuf(WC->WBuf);
537 if ((chunked) && (client_con_state == 0)) {
538 StrBufPrintf(BufHeader, "%s%x\r\n", (first) ? "" : "\r\n", StrLength(pBuf));
540 client_con_state = send_http(BufHeader);
543 if (client_con_state == 0)
544 client_con_state = send_http(pBuf);
550 if (SC && StrBufDestroyStreamContext(eZLibEncode, &SC, &Err) && Err) {
551 syslog(LOG_ERR, "Error while destroying stream context: %s", Err);
553 FreeStrBuf(&WriteBuffer.Buf);
554 if ((chunked) && (client_con_state == 0)) {
555 StrBufPlain(BufHeader, HKEY("\r\n0\r\n\r\n"));
556 if (send_http(BufHeader) < 0) {
558 FreeStrBuf(&BufHeader);
562 FreeStrBuf(&BufHeader);
566 int ClientGetLine(ParsedHttpHdrs * Hdr, StrBuf * Target) {
569 const char *pch, *pchs;
570 int rlen, len, retval = 0;
574 if (StrLength(Hdr->ReadBuf) > 0) {
575 pchs = ChrPtr(Hdr->ReadBuf);
576 pch = strchr(pchs, '\n');
580 if (len > 0 && (*(pch - 1) == '\r'))
582 StrBufSub(Target, Hdr->ReadBuf, 0, len - rlen);
583 StrBufCutLeft(Hdr->ReadBuf, len + 1);
588 while (retval == 0) {
590 pchs = ChrPtr(Hdr->ReadBuf);
592 pch = strchr(pchs, '\n');
594 retval = client_read_sslbuffer(Hdr->ReadBuf, SLEEPING);
595 pchs = ChrPtr(Hdr->ReadBuf);
596 pch = strchr(pchs, '\n');
607 if ((retval > 0) && (pch != NULL)) {
610 if (len > 0 && (*(pch - 1) == '\r'))
612 StrBufSub(Target, Hdr->ReadBuf, 0, len - rlen);
613 StrBufCutLeft(Hdr->ReadBuf, len + 1);
622 return StrBufTCP_read_buffered_line_fast(Target, Hdr->ReadBuf, &Hdr->Pos, &Hdr->http_sock, 5, 1, &Error);
627 * This is a generic function to set up a master socket for listening on
628 * a TCP port. The server shuts down if the bind fails. (IPv4/IPv6 version)
630 * ip_addr IP address to bind
631 * port_number port number to bind
632 * queue_len number of incoming connections to allow in the queue
634 int webcit_tcp_server(const char *ip_addr, int port_number, int queue_len) {
635 const char *ipv4broadcast = "0.0.0.0";
638 struct sockaddr_in6 sin6;
639 struct sockaddr_in sin4;
644 memset(&sin6, 0, sizeof(sin6));
645 memset(&sin4, 0, sizeof(sin4));
646 sin6.sin6_family = AF_INET6;
647 sin4.sin_family = AF_INET;
649 if ((ip_addr == NULL) /* any IPv6 */
650 ||(IsEmptyStr(ip_addr))
651 || (!strcmp(ip_addr, "*"))
655 sin6.sin6_addr = in6addr_any;
657 else if (!strcmp(ip_addr, "0.0.0.0")) { /* any IPv4 */
659 sin4.sin_addr.s_addr = INADDR_ANY;
661 else if ((strchr(ip_addr, '.')) && (!strchr(ip_addr, ':'))) { /* specific IPv4 */
663 if (inet_pton(AF_INET, ip_addr, &sin4.sin_addr) <= 0) {
664 syslog(LOG_WARNING, "Error binding to [%s] : %s\n", ip_addr, strerror(errno));
665 return (-WC_EXIT_BIND);
668 else { /* specific IPv6 */
670 if (inet_pton(AF_INET6, ip_addr, &sin6.sin6_addr) <= 0) {
671 syslog(LOG_WARNING, "Error binding to [%s] : %s\n", ip_addr, strerror(errno));
672 return (-WC_EXIT_BIND);
676 if (port_number == 0) {
677 syslog(LOG_WARNING, "Cannot start: no port number specified.\n");
678 return (-WC_EXIT_BIND);
680 sin6.sin6_port = htons((u_short) port_number);
681 sin4.sin_port = htons((u_short) port_number);
683 p = getprotobyname("tcp");
685 s = socket(((ip_version == 6) ? PF_INET6 : PF_INET), SOCK_STREAM, (p->p_proto));
687 if (IsDefault && (errno == EAFNOSUPPORT)) {
689 ip_addr = ipv4broadcast;
692 syslog(LOG_WARNING, "Can't create a listening socket: %s\n", strerror(errno));
693 return (-WC_EXIT_BIND);
695 /* Set some socket options that make sense. */
697 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
699 if (ip_version == 6) {
700 b = bind(s, (struct sockaddr *) &sin6, sizeof(sin6));
703 b = bind(s, (struct sockaddr *) &sin4, sizeof(sin4));
707 syslog(LOG_ERR, "Can't bind: %s\n", strerror(errno));
709 return (-WC_EXIT_BIND);
712 if (listen(s, queue_len) < 0) {
713 syslog(LOG_ERR, "Can't listen: %s\n", strerror(errno));
715 return (-WC_EXIT_BIND);
722 * Create a Unix domain socket and listen on it
723 * sockpath - file name of the unix domain socket
724 * queue_len - Number of incoming connections to allow in the queue
726 int webcit_uds_server(char *sockpath, int queue_len) {
727 struct sockaddr_un addr;
730 int actual_queue_len;
732 actual_queue_len = queue_len;
733 if (actual_queue_len < 5)
734 actual_queue_len = 5;
736 i = unlink(sockpath);
737 if ((i != 0) && (errno != ENOENT)) {
738 syslog(LOG_WARNING, "webcit: can't unlink %s: %s\n", sockpath, strerror(errno));
739 return (-WC_EXIT_BIND);
742 memset(&addr, 0, sizeof(addr));
743 addr.sun_family = AF_UNIX;
744 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
746 s = socket(AF_UNIX, SOCK_STREAM, 0);
748 syslog(LOG_WARNING, "webcit: Can't create a unix domain socket: %s\n", strerror(errno));
749 return (-WC_EXIT_BIND);
752 if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
753 syslog(LOG_WARNING, "webcit: Can't bind: %s\n", strerror(errno));
755 return (-WC_EXIT_BIND);
758 if (listen(s, actual_queue_len) < 0) {
759 syslog(LOG_WARNING, "webcit: Can't listen: %s\n", strerror(errno));
761 return (-WC_EXIT_BIND);
764 chmod(sockpath, 0777);
770 * Read data from the client socket.
772 * sock socket fd to read from
773 * buf buffer to read into
774 * bytes number of bytes to read
775 * timeout Number of seconds to wait before timing out
777 * Possible return values:
778 * 1 Requested number of bytes has been read.
779 * 0 Request timed out.
780 * -1 Connection is broken, or other error.
782 int client_read_to(ParsedHttpHdrs * Hdr, StrBuf * Target, int bytes, int timeout) {
791 baselen = StrLength(Target);
793 if (Hdr->Pos == NULL) {
794 Hdr->Pos = ChrPtr(Hdr->ReadBuf);
797 if (StrLength(Hdr->ReadBuf) > 0) {
798 bufremain = StrLength(Hdr->ReadBuf) - (Hdr->Pos - ChrPtr(Hdr->ReadBuf));
800 if (bytes < bufremain)
802 StrBufAppendBufPlain(Target, Hdr->Pos, bufremain, 0);
803 StrBufCutLeft(Hdr->ReadBuf, bufremain);
806 if (bytes > bufremain) {
807 while ((StrLength(Hdr->ReadBuf) + StrLength(Target) < bytes + baselen) && (retval >= 0))
808 retval = client_read_sslbuffer(Hdr->ReadBuf, timeout);
810 StrBufAppendBuf(Target, Hdr->ReadBuf, 0); /* todo: Buf > bytes? */
814 syslog(LOG_INFO, "client_read_ssl() failed\n");
822 retval = StrBufReadBLOBBuffered(Target, Hdr->ReadBuf, &Hdr->Pos, &Hdr->http_sock, 1, bytes, O_TERM, &Error);
824 syslog(LOG_INFO, "client_read() failed: %s\n", Error);
825 wc_backtrace(LOG_DEBUG);
834 * Begin buffering HTTP output so we can transmit it all in one write operation later.
836 void begin_burst(void) {
837 if (WC->WBuf == NULL) {
838 WC->WBuf = NewStrBufPlain(NULL, 32768);
844 * Finish buffering HTTP output. [Compress using zlib and] output with a Content-Length: header.
846 long end_burst(void) {
847 const char *ptr, *eptr;
853 if (!DisableGzip && (WC->Hdr->HR.gzip_ok)) {
854 if (CompressBuffer(WC->WBuf) > 0)
855 hprintf("Content-encoding: gzip\r\n");
857 syslog(LOG_ALERT, "Compression failed: %d [%s] sending uncompressed\n", errno, strerror(errno));
858 wc_backtrace(LOG_INFO);
862 if (WC->WFBuf != NULL) {
863 WildFireSerializePayload(WC->WFBuf, WC->HBuf, &WC->Hdr->nWildfireHeaders, NULL);
864 FreeStrBuf(&WC->WFBuf);
867 if (WC->Hdr->HR.prohibit_caching)
868 hprintf("Pragma: no-cache\r\nCache-Control: no-store\r\nExpires:-1\r\n");
869 hprintf("Content-length: %d\r\n\r\n", StrLength(WC->WBuf));
871 ptr = ChrPtr(WC->HBuf);
872 count = StrLength(WC->HBuf);
877 client_write_ssl(WC->HBuf);
878 client_write_ssl(WC->WBuf);
883 if (WC->Hdr->http_sock == -1) {
886 fdflags = fcntl(WC->Hdr->http_sock, F_GETFL);
888 while ((ptr < eptr) && (WC->Hdr->http_sock != -1)) {
889 if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
891 FD_SET(WC->Hdr->http_sock, &wset);
892 if (select(WC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) {
893 syslog(LOG_DEBUG, "client_write: Socket select failed (%s)\n", strerror(errno));
898 if ((WC->Hdr->http_sock == -1) || (res = write(WC->Hdr->http_sock, ptr, count)) == -1) {
899 syslog(LOG_DEBUG, "client_write: Socket write failed (%s)\n", strerror(errno));
900 wc_backtrace(LOG_INFO);
907 ptr = ChrPtr(WC->WBuf);
908 count = StrLength(WC->WBuf);
911 while ((ptr < eptr) && (WC->Hdr->http_sock != -1)) {
912 if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
914 FD_SET(WC->Hdr->http_sock, &wset);
915 if (select(WC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) {
916 syslog(LOG_INFO, "client_write: Socket select failed (%s)\n", strerror(errno));
921 if ((WC->Hdr->http_sock == -1) || (res = write(WC->Hdr->http_sock, ptr, count)) == -1) {
922 syslog(LOG_INFO, "client_write: Socket write failed (%s)\n", strerror(errno));
923 wc_backtrace(LOG_INFO);
930 return StrLength(WC->WBuf);
935 * lingering_close() a`la Apache. see
936 * http://httpd.apache.org/docs/2.0/misc/fin_wait_2.html for rationale
938 int lingering_close(int fd) {
942 struct timeval tv, start;
944 gettimeofday(&start, NULL);
950 gettimeofday(&tv, NULL);
951 tv.tv_sec = SLEEPING - (tv.tv_sec - start.tv_sec);
952 tv.tv_usec = start.tv_usec - tv.tv_usec;
953 if (tv.tv_usec < 0) {
955 tv.tv_usec += 1000000;
959 i = select(fd + 1, &set, NULL, NULL, &tv);
960 } while (i == -1 && errno == EINTR);
965 i = read(fd, buf, sizeof buf);
966 } while (i != 0 && (i != -1 || errno == EINTR));
971 void HttpNewModule_TCPSOCKETS(ParsedHttpHdrs * httpreq) {
973 httpreq->ReadBuf = NewStrBufPlain(NULL, SIZ * 4);
976 void HttpDetachModule_TCPSOCKETS(ParsedHttpHdrs * httpreq) {
978 FlushStrBuf(httpreq->ReadBuf);
979 ReAdjustEmptyBuf(httpreq->ReadBuf, 4 * SIZ, SIZ);
982 void HttpDestroyModule_TCPSOCKETS(ParsedHttpHdrs * httpreq) {
984 FreeStrBuf(&httpreq->ReadBuf);
988 void SessionNewModule_TCPSOCKETS(wcsession * sess) {
989 sess->CLineBuf = NewStrBuf();
990 sess->MigrateReadLineBuf = NewStrBuf();
993 void SessionDestroyModule_TCPSOCKETS(wcsession * sess) {
994 FreeStrBuf(&sess->CLineBuf);
995 FreeStrBuf(&sess->ReadBuf);
997 sess->ReadPos = NULL;
998 FreeStrBuf(&sess->MigrateReadLineBuf);
999 if (sess->serv_sock > 0) {
1000 syslog(LOG_DEBUG, "Closing socket %d", sess->serv_sock);
1001 close(sess->serv_sock);
1003 sess->serv_sock = -1;