Bug Summary

File:d/driver.c
Warning:line 2500, column 21
Duplicate code detected
Note:line 2626, column 25
Similar code here

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name driver.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/isvv/naviserver/nsd -resource-dir /usr/local/lib/clang/15.0.0 -D _FORTIFY_SOURCE=2 -D NDEBUG -D SYSTEM_MALLOC -I ../include -I /usr/include/tcl8.6 -D HAVE_CONFIG_H -internal-isystem /usr/local/lib/clang/15.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/11/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -std=c99 -fdebug-compilation-dir=/home/isvv/naviserver/nsd -ferror-limit 19 -stack-protector 2 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-checker alpha -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-07-23-130959-11103-1 -x c driver.c
1/*
2 * The contents of this file are subject to the Mozilla Public License
3 * Version 1.1 (the "License"); you may not use this file except in
4 * compliance with the License. You may obtain a copy of the License at
5 * http://mozilla.org/.
6 *
7 * Software distributed under the License is distributed on an "AS IS"
8 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9 * the License for the specific language governing rights and limitations
10 * under the License.
11 *
12 * The Original Code is AOLserver Code and related documentation
13 * distributed by AOL.
14 *
15 * The Initial Developer of the Original Code is America Online,
16 * Inc. Portions created by AOL are Copyright (C) 1999 America Online,
17 * Inc. All Rights Reserved.
18 *
19 * Alternatively, the contents of this file may be used under the terms
20 * of the GNU General Public License (the "GPL"), in which case the
21 * provisions of GPL are applicable instead of those above. If you wish
22 * to allow use of your version of this file only under the terms of the
23 * GPL and not to allow others to use your version of this file under the
24 * License, indicate your decision by deleting the provisions above and
25 * replace them with the notice and other provisions required by the GPL.
26 * If you do not delete the provisions above, a recipient may use your
27 * version of this file under either the License or the GPL.
28 */
29
30/*
31 * driver.c --
32 *
33 * Connection I/O for loadable socket drivers.
34 */
35
36
37#define Ns_LogAccessDebug_DEFINED_ALREADY
38#include "nsd.h"
39NS_EXPORT__attribute__ ((visibility ("default"))) Ns_LogSeverity Ns_LogAccessDebug;
40
41/*
42 * The following are valid driver state flags.
43 */
44
45#define DRIVER_STARTED1u 1u
46#define DRIVER_STOPPED2u 2u
47#define DRIVER_SHUTDOWN4u 4u
48#define DRIVER_FAILED8u 8u
49
50
51/*
52 * Constants for SockState return and reason codes.
53 */
54
55typedef enum {
56 SOCK_READY = 0,
57 SOCK_MORE = 1,
58 SOCK_SPOOL = 2,
59 SOCK_ERROR = -1,
60 SOCK_CLOSE = -2,
61 SOCK_CLOSETIMEOUT = -3,
62 SOCK_READTIMEOUT = -4,
63 SOCK_WRITETIMEOUT = -5,
64 SOCK_READERROR = -6,
65 SOCK_WRITEERROR = -7,
66 SOCK_SHUTERROR = -8,
67 SOCK_BADREQUEST = -9,
68 SOCK_ENTITYTOOLARGE = -10,
69 SOCK_BADHEADER = -11,
70 SOCK_TOOMANYHEADERS = -12,
71 SOCK_QUEUEFULL = -13
72} SockState;
73
74/*
75 * Subset for spooler states
76 */
77typedef enum {
78 SPOOLER_CLOSE = SOCK_CLOSE,
79 SPOOLER_OK = SOCK_READY,
80 SPOOLER_READERROR = SOCK_READERROR,
81 SPOOLER_WRITEERROR = SOCK_WRITEERROR,
82 SPOOLER_CLOSETIMEOUT = SOCK_CLOSETIMEOUT
83} SpoolerState;
84
85typedef struct {
86 SpoolerState spoolerState;
87 SockState sockState;
88} SpoolerStateMap;
89
90/*
91 * ServerMap maintains Host header to server mappings.
92 */
93typedef struct ServerMap {
94 NsServer *servPtr;
95 NS_TLS_SSL_CTXSSL_CTX *ctx;
96 char location[1];
97} ServerMap;
98
99/*
100 * The following maintains the spooler state mapping
101 */
102static const SpoolerStateMap spoolerStateMap[] = {
103 {SPOOLER_CLOSE, SOCK_CLOSE},
104 {SPOOLER_READERROR, SOCK_READERROR},
105 {SPOOLER_WRITEERROR, SOCK_WRITEERROR},
106 {SPOOLER_CLOSETIMEOUT, SOCK_CLOSETIMEOUT},
107 {SPOOLER_OK, SOCK_READY}
108};
109/*
110 * The following structure manages polling. The PollIn macro is
111 * used for the common case of checking for readability.
112 */
113
114typedef struct PollData {
115 unsigned int nfds; /* Number of fds being monitored. */
116 unsigned int maxfds; /* Max fds (will grow as needed). */
117 struct pollfd *pfds; /* Dynamic array of poll structs. */
118 Ns_Time timeout; /* Min timeout, if any, for next spin. */
119} PollData;
120
121#define PollIn(ppd, i)(((ppd)->pfds[(i)].revents & 0x001) == 0x001 ) (((ppd)->pfds[(i)].revents & POLLIN0x001) == POLLIN0x001 )
122#define PollOut(ppd, i)(((ppd)->pfds[(i)].revents & 0x004) == 0x004) (((ppd)->pfds[(i)].revents & POLLOUT0x004) == POLLOUT0x004)
123#define PollHup(ppd, i)(((ppd)->pfds[(i)].revents & 0x010) == 0x010) (((ppd)->pfds[(i)].revents & POLLHUP0x010) == POLLHUP0x010)
124
125/*
126 * Collected informationof writer threads for per pool rates, necessary for
127 * per pool bandwidth management.
128 */
129typedef struct ConnPoolInfo {
130 size_t threadSlot;
131 int currentPoolRate;
132 int deltaPercentage;
133} ConnPoolInfo;
134
135/*
136 * The following structure maintains writer socket
137 */
138typedef struct WriterSock {
139 struct WriterSock *nextPtr;
140 struct Sock *sockPtr;
141 struct SpoolerQueue *queuePtr;
142 struct Conn *connPtr;
143 SpoolerState status;
144 int err;
145 int refCount;
146 unsigned int flags;
147 Tcl_WideInt nsent;
148 size_t size;
149 NsWriterStreamState doStream;
150 int fd;
151 char *headerString;
152 struct ConnPool *poolPtr;
153
154 union {
155 struct {
156 struct iovec *bufs; /* incoming bufs to be sent */
157 int nbufs;
158 int bufIdx;
159 struct iovec sbufs[UIO_SMALLIOV8]; /* scratch bufs for handling partial sends */
160 int nsbufs;
161 int sbufIdx;
162 struct iovec preallocated_bufs[UIO_SMALLIOV8];
163 struct FileMap fmap;
164 } mem;
165
166 struct {
167 size_t maxsize;
168 size_t bufsize;
169 off_t bufoffset;
170 size_t toRead;
171 unsigned char *buf;
172 Ns_FileVec *bufs;
173 int nbufs;
174 int currentbuf;
175 Ns_Mutex fdlock;
176 } file;
177 } c;
178
179 char *clientData;
180 Ns_Time startTime;
181 int rateLimit;
182 int currentRate;
183 ConnPoolInfo *infoPtr;
184 bool_Bool keep;
185
186} WriterSock;
187
188/*
189 * Async writer definitions
190 */
191
192typedef struct AsyncWriter {
193 Ns_Mutex lock; /* Lock around writer queues */
194 SpoolerQueue *firstPtr; /* List of writer threads */
195} AsyncWriter;
196
197/*
198 * AsyncWriteData is similar to WriterSock
199 */
200typedef struct AsyncWriteData {
201 struct AsyncWriteData *nextPtr;
202 char *data;
203 int fd;
204 Tcl_WideInt nsent;
205 size_t size;
206 size_t bufsize;
207 const char *buf;
208} AsyncWriteData;
209
210static AsyncWriter *asyncWriter = NULL((void*)0);
211
212#define DriverGetPort(drvPtr,n)(unsigned short)((int)(intptr_t)((drvPtr)->ports.data[(n)]
))
(unsigned short)PTR2INT((drvPtr)->ports.data[(n)])((int)(intptr_t)((drvPtr)->ports.data[(n)]))
213
214/*
215 * Static functions defined in this file.
216 */
217
218static Ns_ThreadProc DriverThread;
219static Ns_ThreadProc SpoolerThread;
220static Ns_ThreadProc WriterThread;
221static Ns_ThreadProc AsyncWriterThread;
222
223static Tcl_ObjCmdProc WriterListObjCmd;
224static Tcl_ObjCmdProc WriterSizeObjCmd;
225static Tcl_ObjCmdProc WriterStreamingObjCmd;
226static Tcl_ObjCmdProc WriterSubmitObjCmd;
227static Tcl_ObjCmdProc WriterSubmitFileObjCmd;
228
229static Tcl_ObjCmdProc AsyncLogfileWriteObjCmd;
230static Tcl_ObjCmdProc AsyncLogfileOpenObjCmd;
231static Tcl_ObjCmdProc AsyncLogfileCloseObjCmd;
232
233static Ns_ReturnCode DriverWriterFromObj(Tcl_Interp *interp, Tcl_Obj *driverObj,
234 const Ns_Conn *conn, DrvWriter **wrPtrPtr)
235 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(4)__attribute__((__nonnull__(4)));
236
237static NS_SOCKETint DriverListen(Driver *drvPtr, const char *bindaddr, unsigned short port)
238 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
239static NS_DRIVER_ACCEPT_STATUS DriverAccept(Sock *sockPtr, NS_SOCKETint sock)
240 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
241static bool_Bool DriverKeep(Sock *sockPtr)
242 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
243static void DriverClose(Sock *sockPtr)
244 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
245static Ns_ReturnCode DriverInit(const char *server, const char *moduleName, const char *threadName,
246 const Ns_DriverInitData *init,
247 NsServer *servPtr, const char *path,
248 const char *bindaddrs,
249 const char *defserver)
250 NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3))) NS_GNUC_NONNULL(4)__attribute__((__nonnull__(4))) NS_GNUC_NONNULL(6)__attribute__((__nonnull__(6)))
251 NS_GNUC_NONNULL(7)__attribute__((__nonnull__(7)));
252static bool_Bool DriverModuleInitialized(const char *module)
253 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
254static const ServerMap *DriverLookupHost(Tcl_DString *hostDs, Driver *drvPtr)
255 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
256
257static size_t PortsParse(Ns_DList *dlPtr, const char *listString, const char *path)
258 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3)));
259static char *PortsPrint(Tcl_DString *dsPtr, const Ns_DList *dlPtr)
260 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
261
262static void SockSetServer(Sock *sockPtr)
263 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
264static SockState SockAccept(Driver *drvPtr, NS_SOCKETint sock, Sock **sockPtrPtr, const Ns_Time *nowPtr)
265 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
266static Ns_ReturnCode SockQueue(Sock *sockPtr, const Ns_Time *timePtr)
267 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
268
269static Sock *SockNew(Driver *drvPtr)
270 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_RETURNS_NONNULL;
271static void SockRelease(Sock *sockPtr, SockState reason, int err)
272 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
273
274static void SockError(Sock *sockPtr, SockState reason, int err)
275 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
276static void SockSendResponse(Sock *sockPtr, int statusCode, const char *errMsg, const char *headers)
277 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3)));
278static void SockTrigger(NS_SOCKETint sock);
279static void SockTimeout(Sock *sockPtr, const Ns_Time *nowPtr, const Ns_Time *timeout)
280 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
281static void SockClose(Sock *sockPtr, int keep)
282 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
283static SockState SockRead(Sock *sockPtr, int spooler, const Ns_Time *timePtr)
284 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
285static SockState SockParse(Sock *sockPtr)
286 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
287static void SockPoll(Sock *sockPtr, short type, PollData *pdata)
288 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3)));
289static int SockSpoolerQueue(Driver *drvPtr, Sock *sockPtr)
290 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
291static void SpoolerQueueStart(SpoolerQueue *queuePtr, Ns_ThreadProc *proc)
292 NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
293static void SpoolerQueueStop(SpoolerQueue *queuePtr, const Ns_Time *timeoutPtr, const char *name)
294 NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3)));
295static void PollCreate(PollData *pdata)
296 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
297static void PollFree(PollData *pdata)
298 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
299static void PollReset(PollData *pdata)
300 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
301static NS_POLL_NFDS_TYPEnfds_t PollSet(PollData *pdata, NS_SOCKETint sock, short type, const Ns_Time *timeoutPtr)
302 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
303static int PollWait(const PollData *pdata, int timeout)
304 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
305static SockState ChunkedDecode(Request *reqPtr, bool_Bool update)
306 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
307static WriterSock *WriterSockRequire(const Conn *connPtr)
308 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
309static void WriterSockRelease(WriterSock *wrSockPtr)
310 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
311static SpoolerState WriterReadFromSpool(WriterSock *curPtr)
312 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
313static SpoolerState WriterSend(WriterSock *curPtr, int *err)
314 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
315
316static Ns_ReturnCode WriterSetupStreamingMode(Conn *connPtr, const struct iovec *bufs, int nbufs, int *fdPtr)
317 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(4)__attribute__((__nonnull__(4)));
318static void WriterSockFileVecCleanup(const WriterSock *wrSockPtr)
319 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
320static int WriterGetMemunitFromDict(Tcl_Interp *interp, Tcl_Obj *dictObj, Tcl_Obj *keyObj,
321 const Ns_ObjvValueRange *rangePtr, Tcl_WideInt *valuePtr)
322 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3))) NS_GNUC_NONNULL(5)__attribute__((__nonnull__(5)));
323
324static void AsyncWriterRelease(AsyncWriteData *wdPtr)
325 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
326
327static void WriteWarningRaw(const char *msg, int fd, size_t wantWrite, ssize_t written)
328 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
329static const char *GetSockStateName(SockState sockState);
330
331static size_t EndOfHeader(Sock *sockPtr)
332 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
333static Request *RequestNew(void)
334 NS_GNUC_RETURNS_NONNULL;
335static void RequestFree(Sock *sockPtr)
336 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
337static void LogBuffer(Ns_LogSeverity severity, const char *msg, const char *buffer, size_t len)
338 NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3)));
339
340static void ServerMapEntryAdd(Tcl_DString *dsPtr, const char *host,
341 NsServer *servPtr, Driver *drvPtr,
342 NS_TLS_SSL_CTXSSL_CTX *ctx,
343 bool_Bool addDefaultMapEntry)
344 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3))) NS_GNUC_NONNULL(4)__attribute__((__nonnull__(4)));
345
346static Driver *LookupDriver(Tcl_Interp *interp, const char* protocol, const char *driverName)
347 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
348
349static void WriterPerPoolRates(WriterSock *writePtr, Tcl_HashTable *pools)
350 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
351
352static ConnPoolInfo *WriterGetInfoPtr(WriterSock *curPtr, Tcl_HashTable *pools)
353 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
354
355/*
356 * Global variables defined in this file.
357 */
358
359Ns_LogSeverity Ns_LogTaskDebug;
360Ns_LogSeverity Ns_LogRequestDebug;
361Ns_LogSeverity Ns_LogConnchanDebug;
362Ns_LogSeverity Ns_LogUrlspaceDebug;
363Ns_LogSeverity Ns_LogTimeoutDebug;
364Ns_LogSeverity Ns_LogNsSetDebug;
365/* See also Ns_LogAccessDebug defined and exported above. */
366
367bool_Bool NsWriterBandwidthManagement = NS_FALSE0;
368
369static Ns_LogSeverity WriterDebug; /* Severity at which to log verbose debugging. */
370static Ns_LogSeverity DriverDebug; /* Severity at which to log verbose debugging. */
371static Ns_Mutex reqLock = NULL((void*)0); /* Lock for allocated Request structure pool */
372static Ns_Mutex writerlock = NULL((void*)0); /* Lock updating streaming information in the writer */
373static Request *firstReqPtr = NULL((void*)0); /* Allocated request structures kept in a pool */
374static Driver *firstDrvPtr = NULL((void*)0); /* First in list of all drivers */
375
376#define Push(x, xs)((x)->nextPtr = (xs), (xs) = (x)) ((x)->nextPtr = (xs), (xs) = (x))
377
378
379/*
380 *----------------------------------------------------------------------
381 *
382 * WriteWarningRaw --
383 *
384 * Write a warning message to stderr. This function is for cases, where
385 * writing to Ns_Log can't be used (e.g. in the AsyncWriter, which is
386 * used for writing also to the system log).
387 *
388 * Results:
389 * None.
390 *
391 * Side effects:
392 * Line to stderr.
393 *
394 *----------------------------------------------------------------------
395 */
396static void
397WriteWarningRaw(const char *msg, int fd, size_t wantWrite, ssize_t written)
398{
399 fprintf(stderr, "%s: Warning: wanted to write %" PRIuz__fprintf_chk (stderr, 2 - 1, "%s: Warning: wanted to write %"
"zu" " bytes, wrote %ld to file descriptor %d\n", msg, wantWrite
, (long)written, fd)
400 " bytes, wrote %ld to file descriptor %d\n",__fprintf_chk (stderr, 2 - 1, "%s: Warning: wanted to write %"
"zu" " bytes, wrote %ld to file descriptor %d\n", msg, wantWrite
, (long)written, fd)
401 msg, wantWrite, (long)written, fd)__fprintf_chk (stderr, 2 - 1, "%s: Warning: wanted to write %"
"zu" " bytes, wrote %ld to file descriptor %d\n", msg, wantWrite
, (long)written, fd)
;
402}
403
404
405/*
406 *----------------------------------------------------------------------
407 *
408 * GetSockStateName --
409 *
410 * Return human readable names for StockState values.
411 *
412 * Results:
413 * string
414 *
415 * Side effects:
416 * None.
417 *
418 *----------------------------------------------------------------------
419 */
420static const char *
421GetSockStateName(SockState sockState)
422{
423 int sockStateInt = (int)sockState;
424 static const char *sockStateStrings[] = {
425 "SOCK_READY",
426 "SOCK_MORE",
427 "SOCK_SPOOL",
428 "SOCK_ERROR",
429 "SOCK_CLOSE",
430 "SOCK_CLOSETIMEOUT",
431 "SOCK_READTIMEOUT",
432 "SOCK_WRITETIMEOUT",
433 "SOCK_READERROR",
434 "SOCK_WRITEERROR",
435 "SOCK_SHUTERROR",
436 "SOCK_BADREQUEST",
437 "SOCK_ENTITYTOOLARGE",
438 "SOCK_BADHEADER",
439 "SOCK_TOOMANYHEADERS",
440 "SOCK_QUEUEFULL",
441 NULL((void*)0)
442 };
443
444 if (sockStateInt < 0) {
445 sockStateInt = (- sockStateInt) + 2;
446 }
447 assert(sockStateInt < Ns_NrElements(sockStateStrings))((void) (0));
448 return sockStateStrings[sockStateInt];
449}
450
451
452/*
453 *----------------------------------------------------------------------
454 *
455 * NsInitDrivers --
456 *
457 * Init drivers system.
458 *
459 * Results:
460 * None.
461 *
462 * Side effects:
463 * None.
464 *
465 *----------------------------------------------------------------------
466 */
467
468void
469NsInitDrivers(void)
470{
471 DriverDebug = Ns_CreateLogSeverity("Debug(ns:driver)");
472 WriterDebug = Ns_CreateLogSeverity("Debug(writer)");
473 Ns_LogTaskDebug = Ns_CreateLogSeverity("Debug(task)");
474 Ns_LogRequestDebug = Ns_CreateLogSeverity("Debug(request)");
475 Ns_LogConnchanDebug = Ns_CreateLogSeverity("Debug(connchan)");
476 Ns_LogUrlspaceDebug = Ns_CreateLogSeverity("Debug(urlspace)");
477 Ns_LogAccessDebug = Ns_CreateLogSeverity("Debug(access)");
478 Ns_LogTimeoutDebug = Ns_CreateLogSeverity("Debug(timeout)");
479 Ns_LogNsSetDebug = Ns_CreateLogSeverity("Debug(nsset)");
480 Ns_MutexInit(&reqLock);
481 Ns_MutexInit(&writerlock);
482 Ns_MutexSetName2(&reqLock, "ns:driver", "requestpool");
483 Ns_MutexSetName2(&writerlock, "ns:writer", "stream");
484}
485
486
487/*
488 *----------------------------------------------------------------------
489 *
490 * DriverModuleInitialized --
491 *
492 * Check if a driver with the specified name is already initialized.
493 *
494 * Results:
495 * Boolean
496 *
497 * Side effects:
498 * None.
499 *
500 *----------------------------------------------------------------------
501 */
502static bool_Bool
503DriverModuleInitialized(const char *module)
504{
505 Driver *drvPtr;
506 bool_Bool found = NS_FALSE0;
507
508 NS_NONNULL_ASSERT(module != NULL)((void) (0));
509
510 for (drvPtr = firstDrvPtr; drvPtr != NULL((void*)0); drvPtr = drvPtr->nextPtr) {
511
512 if (strcmp(drvPtr->moduleName, module) == 0) {
513 found = NS_TRUE1;
514 Ns_Log(Notice, "Driver %s is already initialized", module);
515 break;
516 }
517 }
518
519 return found;
520}
521
522
523/*
524 *----------------------------------------------------------------------
525 *
526 * Ns_DriverInit --
527 *
528 * Initialize a driver.
529 *
530 * Results:
531 * NS_OK if initialized, NS_ERROR if config or other error.
532 *
533 * Side effects:
534 * Listen socket will be opened later in NsStartDrivers.
535 *
536 *----------------------------------------------------------------------
537 */
538
539Ns_ReturnCode
540Ns_DriverInit(const char *server, const char *module, const Ns_DriverInitData *init)
541{
542 Ns_ReturnCode status = NS_OK;
543 NsServer *servPtr = NULL((void*)0);
544 bool_Bool alreadyInitialized = NS_FALSE0;
545
546 NS_NONNULL_ASSERT(module != NULL)((void) (0));
547 NS_NONNULL_ASSERT(init != NULL)((void) (0));
548
549 /*
550 * If a server is provided, servPtr must be set.
551 */
552 if (server != NULL((void*)0)) {
553 servPtr = NsGetServer(server);
554 if (unlikely(servPtr == NULL)(__builtin_expect((servPtr == ((void*)0)), 0))) {
555 Ns_Log(Bug, "cannot lookup server structure for server: %s", module);
556 status = NS_ERROR;
557 }
558 } else {
559 alreadyInitialized = DriverModuleInitialized(module);
560 }
561
562 /*
563 * Check versions of drivers.
564 */
565 if (status == NS_OK && init->version < NS_DRIVER_VERSION_44) {
566 Ns_Log(Warning, "%s: driver version is too old (version %d), Version 4 is recommended",
567 module, init->version);
568 }
569#ifdef HAVE_IPV61
570 if (status == NS_OK && init->version < NS_DRIVER_VERSION_33) {
571 Ns_Log(Error, "%s: driver version is too old (version %d) and does not support IPv6",
572 module, init->version);
573 status = NS_ERROR;
574 }
575#endif
576 if (status == NS_OK && init->version < NS_DRIVER_VERSION_22) {
577 Ns_Log(Error, "%s: version field of driver is invalid: %d",
578 module, init->version);
579 status = NS_ERROR;
580 }
581
582 if (!alreadyInitialized && status == NS_OK) {
583 const char *path, *host, *address, *defserver;
584 bool_Bool noHostNameGiven;
585 int nrDrivers, nrBindaddrs = 0, result;
586 Ns_Set *set = NULL((void*)0);
587 Tcl_Obj *bindaddrsObj, **objv;
588 bool_Bool hostDuplicated = NS_FALSE0;
589
590 if (init->path != NULL((void*)0)) {
591 path = init->path;
592 set = Ns_ConfigCreateSection(path);
593 } else {
594 path = Ns_ConfigSectionPath(&set, server, module, (char *)0L);
595 }
596 assert(path != NULL)((void) (0));
597
598 /*
599 * Determine the "defaultserver" the "hostname" / "address" for
600 * binding to and/or the HTTP location string.
601 */
602 defserver = Ns_ConfigGetValue(path, "defaultserver");
603
604 address = Ns_ConfigGetValue(path, "address");
605 host = Ns_ConfigGetValue(path, "hostname");
606 noHostNameGiven = (host == NULL((void*)0));
607
608 /*
609 * If the listen address was not specified, attempt to determine it
610 * through a DNS lookup of the specified hostname or the server's
611 * primary hostname.
612 */
613 if (address == NULL((void*)0)) {
614 Tcl_DString ds;
615
616 Tcl_DStringInit(&ds);
617 if (noHostNameGiven) {
618 host = Ns_InfoHostname();
619 }
620
621 if (Ns_GetAllAddrByHost(&ds, host) == NS_TRUE1) {
622 address = ns_strdup(Tcl_DStringValue(&ds)((&ds)->string));
623 Ns_SetUpdateSz(set, "address", 7, address, ds.length);
624 Ns_Log(Notice, "no address given, obtained address '%s' from hostname %s", address, host);
625
626 }
627 Tcl_DStringFree(&ds);
628 } else {
629 address = ns_strdup(address);
630 }
631
632 if (address == NULL((void*)0)) {
633 address = NS_IP_UNSPECIFIED"::";
634 Ns_Log(Notice, "no address given, set address to unspecified address %s", address);
635 }
636
637 bindaddrsObj = Tcl_NewStringObj(address, -1);
638 result = Tcl_ListObjGetElements(NULL((void*)0), bindaddrsObj, &nrBindaddrs, &objv);
639 if (result != TCL_OK0 || nrBindaddrs < 1 || nrBindaddrs >= MAX_LISTEN_ADDR_PER_DRIVER16) {
640 Ns_Fatal("%s: bindaddrs '%s' is not a valid Tcl list containing addresses (max %d)",
641 module, address, MAX_LISTEN_ADDR_PER_DRIVER16);
642 }
643
644 /*
645 * If the hostname was not specified and not determined by the lookup
646 * above, set it to the first specified or derived IP address string.
647 */
648 if (host == NULL((void*)0)) {
649 host = ns_strdup(Tcl_GetString(objv[0]));
650 hostDuplicated = NS_TRUE1;
651 }
652
653 if (noHostNameGiven && host != NULL((void*)0)) {
654 Ns_SetUpdateSz(set, "hostname", 8, host, -1);
655 }
656
657 /*
658 * Get configured number of driver threads.
659 */
660 nrDrivers = Ns_ConfigIntRange(path, "driverthreads", 1, 1, 64);
661 if (nrDrivers > 1) {
662#if !defined(SO_REUSEPORT15)
663 Ns_Log(Warning,
664 "server %s module %s requests %d driverthreads, but is not supported by the operating system",
665 server, module, nrDrivers);
666 Ns_SetUpdateSz(set, "driverthreads", 13, "1", 1);
667 nrDrivers = 1;
668#endif
669 }
670
671 /*
672 * The common parameters are determined, create the driver thread(s)
673 */
674 {
675 size_t maxModuleNameLength = strlen(module) + (size_t)TCL_INTEGER_SPACE24 + 1u;
676 char *moduleName = ns_malloc(maxModuleNameLength);
677 const char *passedDefserver = defserver != NULL((void*)0) ? ns_strdup(defserver) : NULL((void*)0);
678 int i;
679
680 for (i = 0; i < nrDrivers; i++) {
681 snprintf(moduleName, maxModuleNameLength, "%s:%d", module, i)__builtin___snprintf_chk (moduleName, maxModuleNameLength, 2 -
1, __builtin_object_size (moduleName, 2 > 1), "%s:%d", module
, i)
;
682 status = DriverInit(server, module, moduleName, init,
683 servPtr, path,
684 address,
685 passedDefserver);
686 if (status != NS_OK) {
687 break;
688 }
689 }
690 ns_free(moduleName);
691 }
692
693 if (hostDuplicated) {
694 ns_free((char*)host);
695 }
696 }
697
698 return status;
699}
700
701
702/*
703 *----------------------------------------------------------------------
704 *
705 * ServerMapEntryAdd --
706 *
707 * Add an entry to the virtual server map. The entry consists of the
708 * value as provided by the host header field and location string,
709 * containing as well the protocol.
710 *
711 * Results:
712 * None
713 *
714 * Side effects:
715 * Potentially adding an entry to the virtual server map.
716 *
717 *----------------------------------------------------------------------
718 */
719
720static void
721ServerMapEntryAdd(Tcl_DString *dsPtr, const char *host,
722 NsServer *servPtr, Driver *drvPtr,
723 NS_TLS_SSL_CTXSSL_CTX *ctx,
724 bool_Bool addDefaultMapEntry) {
725 Tcl_HashEntry *hPtr;
726 int isNew;
727
728 NS_NONNULL_ASSERT(dsPtr != NULL)((void) (0));
729 NS_NONNULL_ASSERT(host != NULL)((void) (0));
730 NS_NONNULL_ASSERT(servPtr != NULL)((void) (0));
731 NS_NONNULL_ASSERT(drvPtr != NULL)((void) (0));
732
733 hPtr = Tcl_CreateHashEntry(&drvPtr->hosts, host, &isNew)(*((&drvPtr->hosts)->createProc))(&drvPtr->hosts
, (const char *)(host), &isNew)
;
734 if (isNew != 0) {
735 ServerMap *mapPtr;
736
737 (void) Ns_DStringVarAppend(dsPtr, drvPtr->protocol, "://", host, (char *)0L);
738 mapPtr = ns_malloc(sizeof(ServerMap) + (size_t)dsPtr->length);
739 if (likely(mapPtr != NULL)(__builtin_expect((mapPtr != ((void*)0)), 1))) {
740 mapPtr->servPtr = servPtr;
741 mapPtr->ctx = ctx;
742 memcpy(mapPtr->location, dsPtr->string, (size_t)dsPtr->length + 1u);
743
744 Tcl_SetHashValue(hPtr, mapPtr)((hPtr)->clientData = (ClientData) (mapPtr));
745 Ns_Log(Notice, "%s: adding virtual host entry for host <%s> location: %s mapped to server: %s ctx %p",
746 drvPtr->threadName, host, mapPtr->location, servPtr->server, (void*)ctx);
747
748 if (addDefaultMapEntry) {
749 drvPtr->defMapPtr = mapPtr;
750 }
751 }
752 /*
753 * Always reset the Tcl_DString
754 */
755 Ns_DStringSetLengthTcl_DStringSetLength(dsPtr, 0);
756 } else {
757 Ns_Log(Notice, "%s: ignore duplicate virtual host entry: %s",
758 drvPtr->threadName, host);
759 }
760}
761
762
763/*
764 *----------------------------------------------------------------------
765 *
766 * NsDriverMapVirtualServers --
767 *
768 * Map "Host:" headers for drivers not bound to physical servers. This
769 * function has to be called a time, when all servers are already defined
770 * such that NsGetServer(server) can succeed.
771 *
772 * Results:
773 * None.
774 *
775 * Side effects:
776 * Add an entry to the virtual server map via ServerMapEntryAdd()
777 *
778 *----------------------------------------------------------------------
779 */
780void NsDriverMapVirtualServers(void)
781{
782 Driver *drvPtr;
783 Tcl_HashTable serverTable; /* names of the driver modules without duplicates */
784
785 Tcl_InitHashTable(&serverTable, TCL_STRING_KEYS(0));
786
787 for (drvPtr = firstDrvPtr; drvPtr != NULL((void*)0); drvPtr = drvPtr->nextPtr) {
788 const Ns_Set *lset;
789 size_t j;
790 Tcl_DString ds, *dsPtr = &ds;
791 const char *path, *defserver, *moduleName;
792
793 moduleName = drvPtr->moduleName;
794 defserver = drvPtr->defserver;
795
796 /*
797 * Check for a "/servers" section for this driver module.
798 */
799 path = Ns_ConfigSectionPath(NULL((void*)0), NULL((void*)0), moduleName, "servers", (char *)0L);
800 lset = Ns_ConfigGetSection(path);
801
802 if (lset == NULL((void*)0) || Ns_SetSize(lset)((lset)->size) == 0u) {
803 /*
804 * The driver module has no (or empty) ".../servers" section.
805 * There is no mapping from hostname to virtual server defined.
806 */
807 if (drvPtr->server == NULL((void*)0)) {
808
809 /*
810 * We have a global driver module. If there is at least a
811 * default server configured, we can use this for the mapping
812 * to the default server.
813 */
814 if (defserver != NULL((void*)0)) {
815 NsServer *servPtr = NsGetServer(defserver);
816
817 Tcl_DStringInit(dsPtr);
818 ServerMapEntryAdd(dsPtr, Ns_InfoHostname(), servPtr, drvPtr, NULL((void*)0), NS_TRUE1);
819 Tcl_DStringFree(dsPtr);
820 Ns_Log(Warning, "global driver has no mapping from host to server"
821 "(section %s missing)", path);
822 } else {
823 /*
824 * Global driver, which has no default server, and no servers section.
825 */
826 Ns_Fatal("%s: virtual servers configured,"
827 " but '%s' has no defaultserver defined", moduleName, path);
828 }
829 }
830
831 continue;
832 }
833
834 /*
835 * We have a ".../servers" section, the driver might be global or
836 * local. It is not clear, why we need the server map for the local
837 * driver, but for compatibility, we keep this.
838 *
839 */
840 if (defserver == NULL((void*)0)) {
841 if (drvPtr->server != NULL((void*)0)) {
842 /*
843 * We have a local (server specific) driver. Since the code
844 * below assumes that we have a "defserver" set, we take the
845 * actual server as defserver.
846 */
847 defserver = drvPtr->server;
848
849 } else {
850 /*
851 * We have a global driver, but no defserver.
852 */
853 Ns_Fatal("%s: virtual servers configured,"
854 " but '%s' has no defaultserver defined", moduleName, path);
855 }
856 }
857
858 assert(defserver != NULL)((void) (0));
859
860 drvPtr->defMapPtr = NULL((void*)0);
861
862 Ns_DStringInitTcl_DStringInit(dsPtr);
863 for (j = 0u; j < Ns_SetSize(lset)((lset)->size); ++j) {
864 const char *server = Ns_SetKey(lset, j)((lset)->fields[(j)].name);
865 const char *host = Ns_SetValue(lset, j)((lset)->fields[(j)].value);
866 NsServer *servPtr;
867 NS_TLS_SSL_CTXSSL_CTX *ctx = NULL((void*)0);
868
869 /*
870 * Perform an explicit lookup of the server.
871 */
872 servPtr = NsGetServer(server);
873 if (servPtr == NULL((void*)0)) {
874 Ns_Log(Error, "%s: no such server: %s", moduleName, server);
875 } else {
876 char *writableHost, *hostName, *portStart, *end;
877 bool_Bool hostParsedOk;
878
879 writableHost = ns_strdup(host);
880 hostParsedOk = Ns_HttpParseHost2(writableHost, NS_TRUE1, &hostName, &portStart, &end);
881 if (!hostParsedOk) {
882 Ns_Log(Warning, "server map: invalid hostname: '%s'", writableHost);
883 continue;
884 }
885
886 if ((drvPtr->opts & NS_DRIVER_SSL0x02u) != 0u) {
887 Tcl_DString ds1;
888 const char *cert;
889
890 Tcl_DStringInit(&ds1);
891 Ns_DStringPrintf(&ds1, "ns/server/%s/module/%s",
892 server, drvPtr->moduleName);
893 cert = Ns_ConfigGetValue(ds1.string, "certificate");
894
895 if (cert != NULL((void*)0)) {
896 int isNew;
897 Tcl_HashEntry *hPtr;
898
899 hPtr = Tcl_CreateHashEntry(&serverTable, ds1.string, &isNew)(*((&serverTable)->createProc))(&serverTable, (const
char *)(ds1.string), &isNew)
;
900
901 /*
902 * A local (server-specific) certificate is configured.
903 */
904 Ns_Log(DriverDebug, "certificate configured: server '%s' on path <%s> driver %s cert %s",
905 server, ds1.string, drvPtr->moduleName,
906 cert);
907
908 if (isNew == 1) {
909 /*
910 * We have already an sslCtx for this server. This
911 * entry is most likely an alternate server name.
912 */
913 if (Ns_TLS_CtxServerInit(ds1.string, NULL((void*)0), 0u, NULL((void*)0), &ctx) == TCL_OK0) {
914 assert(ctx != NULL)((void) (0));
915 drvPtr->opts |= NS_DRIVER_SNI0x20u;
916 Tcl_SetHashValue(hPtr, ctx)((hPtr)->clientData = (ClientData) (ctx));
917 } else {
918 Ns_Log(Error, "driver nsssl: "
919 "could not initialize OpenSSL context (section %s): %s",
920 ds1.string, strerror(errno(*__errno_location ())));
921 ctx = NULL((void*)0);
922 }
923 } else {
924 /*
925 * No sslCtx found for this server. This entry is
926 * most likely an alternate server name.
927 */
928 ctx = Tcl_GetHashValue(hPtr)((hPtr)->clientData);
929 Ns_Log(Debug, "=== reuse sslctx %p from '%s'", (void*)ctx, ds1.string);
930 }
931 }
932 Tcl_DStringFree(&ds1);
933 }
934
935 if (portStart == NULL((void*)0)) {
936 Tcl_DString hostDString;
937 size_t pNum;
938 int prefixLength;
939 /*
940 * The provided host entry does NOT contain a port.
941 *
942 * Add the provided entry to the virtual server map only,
943 * when the configured port is the default port for the
944 * protocol.
945 */
946 if (drvPtr->port == drvPtr->defport) {
947 ServerMapEntryAdd(dsPtr, host, servPtr, drvPtr, ctx,
948 (bool_Bool)STREQ(defserver, server)(((*(defserver)) == (*(server))) && (strcmp((defserver
),(server)) == 0))
);
949 }
950
951 /*
952 * Auto-add all configured ports: Add always entries with
953 * all the explicitly configured ports of the driver.
954 */
955 Tcl_DStringInit(&hostDString);
956 Tcl_DStringAppend(&hostDString, host, -1);
957 prefixLength = hostDString.length;
958
959 for (pNum = 0u; pNum < drvPtr->ports.size; pNum ++) {
960 unsigned short port = DriverGetPort(drvPtr, pNum)(unsigned short)((int)(intptr_t)((drvPtr)->ports.data[(pNum
)]))
;
961
962 (void) Ns_DStringPrintf(&hostDString, ":%hu", port);
963
964 ServerMapEntryAdd(dsPtr, hostDString.string, servPtr, drvPtr, ctx,
965 (bool_Bool)STREQ(defserver, server)(((*(defserver)) == (*(server))) && (strcmp((defserver
),(server)) == 0))
);
966 Tcl_DStringSetLength(&hostDString, prefixLength);
967 }
968
969 Tcl_DStringFree(&hostDString);
970 } else {
971 /*
972 * The provided host entry does contain a port.
973 */
974 size_t pNum;
975 unsigned short providedPort = (unsigned short)strtol(portStart+1, NULL((void*)0), 10);
976 bool_Bool entryAdded = NS_FALSE0;
977
978 /*
979 * In case, the provided port is equal to the default
980 * port of the driver, make sure that we have an entry
981 * without the port.
982 */
983 if (providedPort == drvPtr->defport) {
984 ServerMapEntryAdd(dsPtr, hostName, servPtr, drvPtr, ctx,
985 (bool_Bool)STREQ(defserver, server)(((*(defserver)) == (*(server))) && (strcmp((defserver
),(server)) == 0))
);
986 }
987
988 /*
989 * In case, the provided port is equal to one of the
990 * configured ports of the driver, add an entry.
991 */
992 for (pNum = 0u; pNum < drvPtr->ports.size && !entryAdded; pNum ++) {
993 unsigned short port = DriverGetPort(drvPtr, pNum)(unsigned short)((int)(intptr_t)((drvPtr)->ports.data[(pNum
)]))
;
994
995 if (providedPort == port) {
996 ServerMapEntryAdd(dsPtr, host, servPtr, drvPtr, ctx,
997 (bool_Bool)STREQ(defserver, server)(((*(defserver)) == (*(server))) && (strcmp((defserver
),(server)) == 0))
);
998 entryAdded = NS_TRUE1;
999 }
1000 }
1001 if (!entryAdded) {
1002 Ns_Log(Warning, "%s: driver is not listening on port %hu; "
1003 "virtual host entry %s ignored",
1004 moduleName, providedPort, host);
1005 }
1006 }
1007 ns_free(writableHost);
1008 }
1009 }
1010 Ns_DStringFreeTcl_DStringFree(dsPtr);
1011
1012 if (drvPtr->defMapPtr == NULL((void*)0)) {
1013 fprintf(stderr, "--- Server Map: ---\n")__fprintf_chk (stderr, 2 - 1, "--- Server Map: ---\n");
1014 Ns_SetPrint(lset);
1015 Ns_Fatal("%s: default server '%s' not defined in '%s'", moduleName, defserver, path);
1016 }
1017 }
1018 Tcl_DeleteHashTable(&serverTable);
1019
1020}
1021
1022/*
1023 *----------------------------------------------------------------------
1024 *
1025 * PortsParse --
1026 *
1027 * Parse the configured ports string and check, if it is a valid list and
1028 * contains values feasible to be used as ports, In case the values are
1029 * valid, add these to the provided Ns_DList structure.
1030 *
1031 * Results:
1032 * Number of added ports.
1033 *
1034 * Side effects:
1035 * None.
1036 *
1037 *----------------------------------------------------------------------
1038 */
1039static size_t
1040PortsParse(Ns_DList *dlPtr, const char *listString, const char *path)
1041{
1042 NS_NONNULL_ASSERT(dlPtr != NULL)((void) (0));
1043 NS_NONNULL_ASSERT(path != NULL)((void) (0));
1044
1045 if (listString != NULL((void*)0)) {
1046 int nrPorts, result, i;
1047 Tcl_Obj **objv, *portsObj = Tcl_NewStringObj(listString, -1);
1048
1049 Tcl_IncrRefCount(portsObj)++(portsObj)->refCount;
1050 result = Tcl_ListObjGetElements(NULL((void*)0), portsObj, &nrPorts, &objv);
1051 if (result != TCL_OK0) {
1052 Ns_Fatal("specified ports for %s invalid: %s", path, listString);
1053 }
1054 for (i= 0; i < nrPorts; i++) {
1055 int portValue = 0;
1056
1057 result = Tcl_GetIntFromObj(NULL((void*)0), objv[i], &portValue);
1058 if (result == TCL_OK0) {
1059 if (portValue > 65535 || portValue < 0) {
1060 Ns_Fatal("specified ports for %s invalid: value %d out of range (0..65535)",
1061 path, portValue);
1062 }
1063 Ns_DListAppend(dlPtr, INT2PTR(portValue)((void *)(intptr_t)(portValue)));
1064 }
1065 }
1066 Tcl_DecrRefCount(portsObj)do { Tcl_Obj *_objPtr = (portsObj); if (_objPtr->refCount--
<= 1) { TclFreeObj(_objPtr); } } while(0)
;
1067 }
1068 return dlPtr->size;
1069}
1070
1071/*
1072 *----------------------------------------------------------------------
1073 *
1074 * PortsPrint --
1075 *
1076 * Print the configured ports to the Tcl_DString provided in the first
1077 * argument.
1078 *
1079 * Results:
1080 * String content of the Tcl_DString.
1081 *
1082 * Side effects:
1083 * None.
1084 *
1085 *----------------------------------------------------------------------
1086 */
1087static char *
1088PortsPrint(Tcl_DString *dsPtr, const Ns_DList *dlPtr)
1089{
1090 NS_NONNULL_ASSERT(dsPtr != NULL)((void) (0));
1091 NS_NONNULL_ASSERT(dlPtr != NULL)((void) (0));
1092
1093 if (dlPtr->size > 0) {
1094 size_t i;
1095
1096 for ( i= 0; i < dlPtr->size; i++) {
1097 Ns_DStringPrintf(dsPtr, "%hu ", (unsigned short)PTR2INT(dlPtr->data[i])((int)(intptr_t)(dlPtr->data[i])));
1098 }
1099 Tcl_DStringSetLength(dsPtr, dsPtr->length - 1);
1100 }
1101 return dsPtr->string;
1102}
1103
1104
1105/*
1106 *----------------------------------------------------------------------
1107 *
1108 * DriverInit --
1109 *
1110 * Helper function of Ns_DriverInit. This function actually allocates and
1111 * initialized the driver structure.
1112 *
1113 * Results:
1114 * NS_OK if initialized, NS_ERROR if config or other error.
1115 *
1116 * Side effects:
1117 * Listen socket will be opened later in NsStartDrivers.
1118 *
1119 *----------------------------------------------------------------------
1120 */
1121
1122static Ns_ReturnCode
1123DriverInit(const char *server, const char *moduleName, const char *threadName,
1124 const Ns_DriverInitData *init,
1125 NsServer *servPtr, const char *path,
1126 const char *bindaddrs, const char *defserver)
1127{
1128 const char *defproto;
1129 Driver *drvPtr;
1130 DrvWriter *wrPtr;
1131 DrvSpooler *spPtr;
1132 int i;
1133 unsigned short defport;
1134
1135 NS_NONNULL_ASSERT(threadName != NULL)((void) (0));
1136 NS_NONNULL_ASSERT(init != NULL)((void) (0));
1137 NS_NONNULL_ASSERT(path != NULL)((void) (0));
1138 NS_NONNULL_ASSERT(bindaddrs != NULL)((void) (0));
1139
1140 /*
1141 * Set the protocol and port defaults.
1142 */
1143 if (init->protocol != NULL((void*)0)) {
1144 defproto = init->protocol;
1145 defport = init->defaultPort;
1146 } else {
1147 defproto = "unknown";
1148 defport = 0u;
1149 }
1150 Ns_Log(DriverDebug, "DriverInit server <%s> threadName %s proto %s port %hu",
1151 server, threadName, defproto, defport);
1152
1153 /*
1154 * Allocate a new driver instance and set configurable parameters.
1155 */
1156
1157 drvPtr = ns_calloc(1u, sizeof(Driver));
1158
1159 Ns_MutexInit(&drvPtr->lock);
1160 Ns_MutexSetName2(&drvPtr->lock, "ns:drv", threadName);
1161
1162 Ns_MutexInit(&drvPtr->spooler.lock);
1163 Ns_MutexSetName2(&drvPtr->spooler.lock, "ns:drv:spool", threadName);
1164
1165 Ns_MutexInit(&drvPtr->writer.lock);
1166 Ns_MutexSetName2(&drvPtr->writer.lock, "ns:drv:writer", threadName);
1167
1168 if (ns_sockpair(drvPtr->trigger) != 0) {
1169 Ns_Fatal("ns_sockpair() failed: %s", ns_sockstrerrorstrerror(ns_sockerrno(*__errno_location ())));
1170 }
1171
1172 Ns_Log(DriverDebug, "DriverInit %s set server '%s' defserver %s %p",
1173 moduleName, server, defserver, (void*)defserver);
1174
1175 drvPtr->server = server;
1176 drvPtr->type = init->name;
1177 drvPtr->moduleName = ns_strdup(moduleName);
1178 drvPtr->threadName = ns_strdup(threadName);
1179 drvPtr->defserver = defserver;
1180 drvPtr->listenProc = init->listenProc;
1181 drvPtr->acceptProc = init->acceptProc;
1182 drvPtr->recvProc = init->recvProc;
1183 drvPtr->sendProc = init->sendProc;
1184 drvPtr->sendFileProc = init->sendFileProc;
1185 drvPtr->keepProc = init->keepProc;
1186 drvPtr->requestProc = init->requestProc;
1187 drvPtr->closeProc = init->closeProc;
1188 drvPtr->clientInitProc = init->clientInitProc;
1189 drvPtr->arg = init->arg;
1190 drvPtr->opts = init->opts;
1191 drvPtr->servPtr = servPtr;
1192 drvPtr->defport = defport;
1193
1194 drvPtr->bufsize = (size_t)Ns_ConfigMemUnitRange(path, "bufsize", "16KB", 16384, 1024, INT_MAX2147483647);
1195 drvPtr->maxinput = Ns_ConfigMemUnitRange(path, "maxinput", "1MB", 1024*1024, 1024, LLONG_MAX9223372036854775807LL);
1196 drvPtr->maxupload = Ns_ConfigMemUnitRange(path, "maxupload", NULL((void*)0), 0, 0, (Tcl_WideInt)drvPtr->maxinput);
1197 drvPtr->readahead = Ns_ConfigMemUnitRange(path, "readahead", NULL((void*)0), (Tcl_WideInt)drvPtr->bufsize,
1198 (Tcl_WideInt)drvPtr->bufsize, drvPtr->maxinput);
1199
1200 drvPtr->maxline = (int)Ns_ConfigMemUnitRange(path, "maxline", "8KB", 8192, 512, INT_MAX2147483647);
1201 drvPtr->maxheaders = Ns_ConfigIntRange(path, "maxheaders", 128, 8, INT_MAX2147483647);
1202 drvPtr->maxqueuesize = Ns_ConfigIntRange(path, "maxqueuesize", 1024, 1, INT_MAX2147483647);
1203
1204 Ns_ConfigTimeUnitRange(path, "sendwait",
1205 "30s", 1, 0, INT_MAX2147483647, 0, &drvPtr->sendwait);
1206 Ns_ConfigTimeUnitRange(path, "recvwait",
1207 "30s", 1, 0, INT_MAX2147483647, 0, &drvPtr->recvwait);
1208 Ns_ConfigTimeUnitRange(path, "closewait",
1209 "2s", 0, 0, INT_MAX2147483647, 0, &drvPtr->closewait);
1210 Ns_ConfigTimeUnitRange(path, "keepwait",
1211 "5s", 0, 0, INT_MAX2147483647, 0, &drvPtr->keepwait);
1212
1213 drvPtr->backlog = Ns_ConfigIntRange(path, "backlog", 256, 1, INT_MAX2147483647);
1214 drvPtr->driverthreads = Ns_ConfigIntRange(path, "driverthreads", 1, 1, 32);
1215 drvPtr->reuseport = Ns_ConfigBool(path, "reuseport", NS_FALSE0);
1216 drvPtr->acceptsize = Ns_ConfigIntRange(path, "acceptsize", drvPtr->backlog, 1, INT_MAX2147483647);
1217
1218 drvPtr->keepmaxuploadsize = (size_t)Ns_ConfigMemUnitRange(path, "keepalivemaxuploadsize", NULL((void*)0),
1219 0, 0, INT_MAX2147483647);
1220 drvPtr->keepmaxdownloadsize = (size_t)Ns_ConfigMemUnitRange(path, "keepalivemaxdownloadsize", NULL((void*)0),
1221 0, 0, INT_MAX2147483647);
1222 drvPtr->recvTimeout = drvPtr->recvwait;
1223
1224 drvPtr->nextPtr = firstDrvPtr;
1225 firstDrvPtr = drvPtr;
1226
1227 Tcl_InitHashTable(&drvPtr->hosts, TCL_STRING_KEYS(0));
1228 Ns_DListInit(&drvPtr->ports);
1229
1230 if (drvPtr->driverthreads > 1) {
1231#if !defined(SO_REUSEPORT15)
1232 drvPtr->driverthreads = 1;
1233 drvPtr->reuseport = NS_FALSE0;
1234#else
1235 /*
1236 * When driver threads > 1, "reuseport" has to be active.
1237 */
1238 drvPtr->reuseport = NS_TRUE1;
1239#endif
1240 }
1241 if (drvPtr->reuseport) {
1242 /*
1243 * Reuseport was specified
1244 */
1245#if !defined(SO_REUSEPORT15)
1246 Ns_Log(Warning,
1247 "parameter %s reuseport was specified, but is not supported by the operating system",
1248 path);
1249 drvPtr->reuseport = NS_FALSE0;
1250#endif
1251 }
1252
1253 drvPtr->uploadpath = ns_strdup(Ns_ConfigString(path, "uploadpath", nsconf.tmpDir));
1254
1255 /*
1256 * If activated, "maxupload" has to be at least "readahead" bytes. Tell
1257 * the user in case the config values are overruled.
1258 */
1259 if ((drvPtr->maxupload > 0) &&
1260 (drvPtr->maxupload < drvPtr->readahead)) {
1261 Ns_Log(Warning,
1262 "parameter %s maxupload % " TCL_LL_MODIFIER"l"
1263 "d invalid; can be either 0 or must be >= %" TCL_LL_MODIFIER"l"
1264 "d (size of readahead)",
1265 path, drvPtr->maxupload, drvPtr->readahead);
1266 drvPtr->maxupload = drvPtr->readahead;
1267 }
1268
1269 /*
1270 * Determine the port and then set the HTTP location string either
1271 * as specified in the configuration file or constructed from the
1272 * protocol, hostname and port.
1273 */
1274
1275 drvPtr->protocol = ns_strdup(defproto);
1276 drvPtr->address = ns_strdup(bindaddrs);
1277
1278 /*
1279 * Get list of ports and keep the first port extra in drvPtr->port for the
1280 * time being.
1281 */
1282 i = (int)PortsParse(&drvPtr->ports, Ns_ConfigGetValue(path, "port"), path);
1283 if (i == 0) {
1284 Ns_DListAppend(&drvPtr->ports, INT2PTR(defport)((void *)(intptr_t)(defport)));
1285 }
1286 drvPtr->port = DriverGetPort(drvPtr, 0)(unsigned short)((int)(intptr_t)((drvPtr)->ports.data[(0)]
))
;
1287
1288 /*
1289 * Get the configured "location" value.
1290 */
1291 drvPtr->location = Ns_ConfigGetValue(path, "location");
1292 if (drvPtr->location != NULL((void*)0) && (strstr(drvPtr->location, "://") != NULL((void*)0))) {
1293 drvPtr->location = ns_strdup(drvPtr->location);
1294 }
1295
1296 /*
1297 * Add driver specific extra headers.
1298 */
1299 drvPtr->extraHeaders = Ns_ConfigSet(path, "extraheaders", NULL((void*)0));
1300
1301 /*
1302 * Check if upload spooler threads are enabled.
1303 */
1304 spPtr = &drvPtr->spooler;
1305 spPtr->threads = Ns_ConfigIntRange(path, "spoolerthreads", 0, 0, 32);
1306
1307 if (spPtr->threads > 0) {
1308 Ns_Log(Notice, "%s: enable %d spooler thread(s) "
1309 "for uploads >= %" TCL_LL_MODIFIER"l" "d bytes", threadName,
1310 spPtr->threads, drvPtr->readahead);
1311
1312 for (i = 0; i < spPtr->threads; i++) {
1313 SpoolerQueue *queuePtr = ns_calloc(1u, sizeof(SpoolerQueue));
1314 char buffer[100];
1315
1316 snprintf(buffer, sizeof(buffer), "ns:driver:spooler:%s:%d", threadName, i)__builtin___snprintf_chk (buffer, sizeof(buffer), 2 - 1, __builtin_object_size
(buffer, 2 > 1), "ns:driver:spooler:%s:%d", threadName, i
)
;
1317 Ns_MutexSetName2(&queuePtr->lock, buffer, "queue");
1318 queuePtr->id = i;
1319 Push(queuePtr, spPtr->firstPtr)((queuePtr)->nextPtr = (spPtr->firstPtr), (spPtr->firstPtr
) = (queuePtr))
;
1320 }
1321 } else {
1322 Ns_Log(Notice, "%s: enable %d spooler thread(s) ",
1323 threadName, spPtr->threads);
1324 }
1325
1326 /*
1327 * Enable writer threads
1328 */
1329
1330 wrPtr = &drvPtr->writer;
1331 wrPtr->threads = Ns_ConfigIntRange(path, "writerthreads", 0, 0, 32);
1332
1333 if (wrPtr->threads > 0) {
1334 wrPtr->writersize = (size_t)Ns_ConfigMemUnitRange(path, "writersize", "1MB",
1335 1024*1024, 1024, INT_MAX2147483647);
1336 wrPtr->bufsize = (size_t)Ns_ConfigMemUnitRange(path, "writerbufsize", "8KB",
1337 8192, 512, INT_MAX2147483647);
1338 wrPtr->rateLimit = Ns_ConfigIntRange(path, "writerratelimit", 0, 0, INT_MAX2147483647);
1339 wrPtr->doStream = Ns_ConfigBool(path, "writerstreaming", NS_FALSE0)
1340 ? NS_WRITER_STREAM_ACTIVE : NS_WRITER_STREAM_NONE;
1341 Ns_Log(Notice, "%s: enable %d writer thread(s) "
1342 "for downloads >= %" PRIdz"zd" " bytes, bufsize=%" PRIdz"zd" " bytes, HTML streaming %d",
1343 threadName, wrPtr->threads, wrPtr->writersize, wrPtr->bufsize, wrPtr->doStream);
1344
1345 for (i = 0; i < wrPtr->threads; i++) {
1346 SpoolerQueue *queuePtr = ns_calloc(1u, sizeof(SpoolerQueue));
1347 char buffer[100];
1348
1349 snprintf(buffer, sizeof(buffer), "ns:driver:writer:%s:%d", threadName, i)__builtin___snprintf_chk (buffer, sizeof(buffer), 2 - 1, __builtin_object_size
(buffer, 2 > 1), "ns:driver:writer:%s:%d", threadName, i)
;
1350 Ns_MutexSetName2(&queuePtr->lock, buffer, "queue");
1351 queuePtr->id = i;
1352 Push(queuePtr, wrPtr->firstPtr)((queuePtr)->nextPtr = (wrPtr->firstPtr), (wrPtr->firstPtr
) = (queuePtr))
;
1353 }
1354 } else {
1355 Ns_Log(Notice, "%s: enable %d writer thread(s) ",
1356 threadName, wrPtr->threads);
1357 }
1358
1359 return NS_OK;
1360}
1361
1362
1363
1364/*
1365 *----------------------------------------------------------------------
1366 *
1367 * NsStartDrivers --
1368 *
1369 * Listen on all driver address/ports and start the DriverThread.
1370 *
1371 * Results:
1372 * None.
1373 *
1374 * Side effects:
1375 * See DriverThread.
1376 *
1377 *----------------------------------------------------------------------
1378 */
1379void
1380NsStartDrivers(void)
1381{
1382 Driver *drvPtr;
1383
1384 /*
1385 * Signal and wait for each driver to start.
1386 */
1387 for (drvPtr = firstDrvPtr; drvPtr != NULL((void*)0); drvPtr = drvPtr->nextPtr) {
1388
1389 if (drvPtr->port == 0u) {
1390 /*
1391 * Don't start a driver having the first port zero.
1392 */
1393 continue;
1394 }
1395
1396 Ns_ThreadCreate(DriverThread, drvPtr, 0, &drvPtr->thread);
1397 Ns_MutexLock(&drvPtr->lock);
1398 while ((drvPtr->flags & DRIVER_STARTED1u) == 0u) {
1399 Ns_CondWait(&drvPtr->cond, &drvPtr->lock);
1400 }
1401 /*if ((drvPtr->flags & DRIVER_FAILED)) {
1402 status = NS_ERROR;
1403 }*/
1404 Ns_MutexUnlock(&drvPtr->lock);
1405 }
1406}
1407
1408
1409/*
1410 *----------------------------------------------------------------------
1411 *
1412 * NsStopDrivers --
1413 *
1414 * Trigger the DriverThread to begin shutdown.
1415 *
1416 * Results:
1417 * None.
1418 *
1419 * Side effects:
1420 * DriverThread will close listen sockets and then exit after all
1421 * outstanding connections are complete and closed.
1422 *
1423 *----------------------------------------------------------------------
1424 */
1425
1426void
1427NsStopDrivers(void)
1428{
1429 Driver *drvPtr;
1430
1431 NsAsyncWriterQueueDisable(NS_TRUE1);
1432
1433 for (drvPtr = firstDrvPtr; drvPtr != NULL((void*)0); drvPtr = drvPtr->nextPtr) {
1434 if ((drvPtr->flags & DRIVER_STARTED1u)) {
1435 Ns_MutexLock(&drvPtr->lock);
1436 Ns_Log(Notice, "[driver:%s]: stopping", drvPtr->threadName);
1437 drvPtr->flags |= DRIVER_SHUTDOWN4u;
1438 Ns_CondBroadcast(&drvPtr->cond);
1439 Ns_MutexUnlock(&drvPtr->lock);
1440 SockTrigger(drvPtr->trigger[1]);
1441 }
1442 }
1443}
1444
1445
1446void
1447NsStopSpoolers(void)
1448{
1449 const Driver *drvPtr;
1450
1451 Ns_Log(Notice, "driver: stopping writer and spooler threads");
1452
1453 for (drvPtr = firstDrvPtr; drvPtr != NULL((void*)0); drvPtr = drvPtr->nextPtr) {
1454 if ((drvPtr->flags & DRIVER_STARTED1u)) {
1455 Ns_Time timeout, *shutdown = &nsconf.shutdowntimeout;
1456
1457 Ns_GetTime(&timeout);
1458 Ns_IncrTime(&timeout, shutdown->sec, shutdown->usec);
1459 SpoolerQueueStop(drvPtr->writer.firstPtr, &timeout, "writer");
1460 SpoolerQueueStop(drvPtr->spooler.firstPtr, &timeout, "spooler");
1461 }
1462 }
1463}
1464
1465
1466/*
1467 *----------------------------------------------------------------------
1468 *
1469 * DriverInfoObjCmd --
1470 *
1471 * Implements "ns_driver info". Returns public info of all drivers.
1472 * Subcommand of NsTclDriverObjCmd.
1473 *
1474 * Results:
1475 * Standard Tcl Result.
1476 *
1477 * Side effects:
1478 * None.
1479 *
1480 *----------------------------------------------------------------------
1481 */
1482static int
1483DriverInfoObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1484{
1485 int result = TCL_OK0;
1486
1487 if (Ns_ParseObjv(NULL((void*)0), NULL((void*)0), interp, 2, objc, objv) != NS_OK) {
1488 result = TCL_ERROR1;
1489
1490 } else {
1491 const Driver *drvPtr;
1492 Tcl_Obj *resultObj = Tcl_NewListObj(0, NULL((void*)0));
1493 Tcl_HashTable driverNames; /* names of the driver modules without duplicates */
1494
1495 Tcl_InitHashTable(&driverNames, TCL_STRING_KEYS(0));
1496
1497 /*
1498 * Iterate over all modules, not necessarily all driver threads
1499 */
1500 for (drvPtr = firstDrvPtr; drvPtr != NULL((void*)0); drvPtr = drvPtr->nextPtr) {
1501 int isNew = 0;
1502
1503 (void)Tcl_CreateHashEntry(&driverNames, drvPtr->moduleName, &isNew)(*((&driverNames)->createProc))(&driverNames, (const
char *)(drvPtr->moduleName), &isNew)
;
1504 if (isNew == 1) {
1505 Tcl_Obj *listObj = Tcl_NewListObj(0, NULL((void*)0));
1506
1507 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("module", 6));
1508 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(drvPtr->moduleName, -1));
1509
1510 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("type", 4));
1511 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(drvPtr->type, -1));
1512
1513 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("server", 6));
1514 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(drvPtr->server != NULL((void*)0) ?
1515 drvPtr->server : NS_EMPTY_STRING, -1));
1516
1517 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("location", 8));
1518 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(drvPtr->location != NULL((void*)0) ?
1519 drvPtr->location : NS_EMPTY_STRING, -1));
1520
1521 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("address", 7));
1522 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(drvPtr->address, -1));
1523
1524 {Tcl_DString ds;
1525 Tcl_DStringInit(&ds);
1526 PortsPrint(&ds, &drvPtr->ports);
1527 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("port", 4));
1528 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(ds.string, ds.length));
1529 Tcl_DStringFree(&ds);
1530 }
1531
1532 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("defaultport",11));
1533 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewIntObj(drvPtr->defport));
1534
1535 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("protocol", 8));
1536 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(drvPtr->protocol, -1));
1537
1538 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("sendwait", 8));
1539 Tcl_ListObjAppendElement(interp, listObj, Ns_TclNewTimeObj(&drvPtr->sendwait));
1540
1541 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("recvwait", 8));
1542 Tcl_ListObjAppendElement(interp, listObj, Ns_TclNewTimeObj(&drvPtr->sendwait));
1543
1544 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("extraheaders", 12));
1545 if (drvPtr->extraHeaders != NULL((void*)0)) {
1546 Tcl_DString ds;
1547
1548 Tcl_DStringInit(&ds);
1549 Ns_DStringAppendSet(&ds, drvPtr->extraHeaders);
1550 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(ds.string, ds.length));
1551 Tcl_DStringFree(&ds);
1552 } else {
1553 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(NS_EMPTY_STRING, 0));
1554 }
1555
1556 Tcl_ListObjAppendElement(interp, resultObj, listObj);
1557 }
1558 }
1559 Tcl_SetObjResult(interp, resultObj);
1560 Tcl_DeleteHashTable(&driverNames);
1561 }
1562
1563 return result;
1564}
1565
1566
1567
1568/*
1569 *----------------------------------------------------------------------
1570 *
1571 * DriverStatsObjCmd --
1572 *
1573 * Implements "ns_driver stats". Returns statistics of all drivers.
1574 * Subcommand of NsTclDriverObjCmd.
1575 *
1576 * Results:
1577 * Standard Tcl Result.
1578 *
1579 * Side effects:
1580 * None.
1581 *
1582 *----------------------------------------------------------------------
1583 */
1584static int
1585DriverStatsObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1586{
1587 int result = TCL_OK0;
1588
1589 if (Ns_ParseObjv(NULL((void*)0), NULL((void*)0), interp, 2, objc, objv) != NS_OK) {
1590 result = TCL_ERROR1;
1591
1592 } else {
1593
1594 const Driver *drvPtr;
1595 Tcl_Obj *resultObj = Tcl_NewListObj(0, NULL((void*)0));
1596
1597 /*
1598 * Iterate over all drivers and collect results.
1599 */
1600 for (drvPtr = firstDrvPtr; drvPtr != NULL((void*)0); drvPtr = drvPtr->nextPtr) {
1601 Tcl_Obj *listObj = Tcl_NewListObj(0, NULL((void*)0));
1602
1603
1604 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("thread", 6));
1605 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(drvPtr->threadName, -1));
1606
1607 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("module", 6));
1608 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(drvPtr->moduleName, -1));
1609
1610 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("received", 8));
1611 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewWideIntObj(drvPtr->stats.received));
1612
1613 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("spooled", 7));
1614 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewWideIntObj(drvPtr->stats.spooled));
1615
1616 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("partial", 7));
1617 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewWideIntObj(drvPtr->stats.partial));
1618
1619 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("errors", 6));
1620 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewWideIntObj(drvPtr->stats.errors));
1621
1622 Tcl_ListObjAppendElement(interp, resultObj, listObj);
1623 }
1624 Tcl_SetObjResult(interp, resultObj);
1625 }
1626
1627 return result;
1628}
1629
1630
1631/*
1632 *----------------------------------------------------------------------
1633 *
1634 * DriverThreadsObjCmd --
1635 *
1636 * Implements "ns_driver threads". Returns the names of driver threads
1637 *
1638 * Results:
1639 * Standard Tcl Result.
1640 *
1641 * Side effects:
1642 * None.
1643 *
1644 *----------------------------------------------------------------------
1645 */
1646static int
1647DriverThreadsObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1648{
1649 int result = TCL_OK0;
1650
1651 if (Ns_ParseObjv(NULL((void*)0), NULL((void*)0), interp, 2, objc, objv) != NS_OK) {
1652 result = TCL_ERROR1;
1653
1654 } else {
1655 const Driver *drvPtr;
1656 Tcl_Obj *resultObj = Tcl_NewListObj(0, NULL((void*)0));
1657
1658 /*
1659 * Iterate over all drivers and collect results.
1660 */
1661 for (drvPtr = firstDrvPtr; drvPtr != NULL((void*)0); drvPtr = drvPtr->nextPtr) {
1662 Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj(drvPtr->threadName, -1));
1663 }
1664 Tcl_SetObjResult(interp, resultObj);
1665 }
1666 return result;
1667}
1668
1669
1670/*
1671 *----------------------------------------------------------------------
1672 *
1673 * DriverNamesObjCmd --
1674 *
1675 * Implements "ns_driver names". Returns the names of drivers.
1676 *
1677 * Results:
1678 * Standard Tcl Result.
1679 *
1680 * Side effects:
1681 * None.
1682 *
1683 *----------------------------------------------------------------------
1684 */
1685static int
1686DriverNamesObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1687{
1688 int result = TCL_OK0;
1689
1690 if (Ns_ParseObjv(NULL((void*)0), NULL((void*)0), interp, 2, objc, objv) != NS_OK) {
1691 result = TCL_ERROR1;
1692
1693 } else {
1694 const Driver *drvPtr;
1695 Tcl_Obj *resultObj = Tcl_NewListObj(0, NULL((void*)0));
1696 Tcl_HashTable driverNames; /* names of the drivers without duplicates */
1697
1698 Tcl_InitHashTable(&driverNames, TCL_STRING_KEYS(0));
1699
1700 /*
1701 * Iterate over all drivers and collect results.
1702 */
1703 for (drvPtr = firstDrvPtr; drvPtr != NULL((void*)0); drvPtr = drvPtr->nextPtr) {
1704 int isNew;
1705
1706 (void)Tcl_CreateHashEntry(&driverNames, drvPtr->moduleName, &isNew)(*((&driverNames)->createProc))(&driverNames, (const
char *)(drvPtr->moduleName), &isNew)
;
1707 if (isNew == 1) {
1708 Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj(drvPtr->moduleName, -1));
1709 }
1710 }
1711 Tcl_SetObjResult(interp, resultObj);
1712 Tcl_DeleteHashTable(&driverNames);
1713 }
1714
1715 return result;
1716}
1717
1718/*
1719 *----------------------------------------------------------------------
1720 *
1721 * NsTclDriverObjCmd -
1722 *
1723 * Implements "ns_driver". Give information about drivers.
1724 *
1725 * Results:
1726 * Standard Tcl result.
1727 *
1728 * Side effects:
1729 * None.
1730 *
1731 *----------------------------------------------------------------------
1732 */
1733int
1734NsTclDriverObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1735{
1736 const Ns_SubCmdSpec subcmds[] = {
1737 {"info", DriverInfoObjCmd},
1738 {"names", DriverNamesObjCmd},
1739 {"threads", DriverThreadsObjCmd},
1740 {"stats", DriverStatsObjCmd},
1741 {NULL((void*)0), NULL((void*)0)}
1742 };
1743
1744 return Ns_SubcmdObjv(subcmds, clientData, interp, objc, objv);
1745}
1746
1747
1748/*
1749 *----------------------------------------------------------------------
1750 *
1751 * NsWakeupDriver --
1752 *
1753 * Wake up the associated DriverThread.
1754 *
1755 * Results:
1756 * None.
1757 *
1758 * Side effects:
1759 * The poll waiting for this trigger will be interrupted.
1760 *
1761 *----------------------------------------------------------------------
1762 */
1763void
1764NsWakeupDriver(const Driver *drvPtr) {
1765 NS_NONNULL_ASSERT(drvPtr != NULL)((void) (0));
1766 SockTrigger(drvPtr->trigger[1]);
1767}
1768
1769
1770/*
1771 *----------------------------------------------------------------------
1772 *
1773 * NsWaitDriversShutdown --
1774 *
1775 * Wait for exit of DriverThread. This callback is invoked later
1776 * by the timed shutdown thread.
1777 *
1778 * Results:
1779 * None.
1780 *
1781 * Side effects:
1782 * Driver thread is joined and trigger pipe closed.
1783 *
1784 *----------------------------------------------------------------------
1785 */
1786
1787void
1788NsWaitDriversShutdown(const Ns_Time *toPtr)
1789{
1790 Driver *drvPtr;
1791 Ns_ReturnCode status = NS_OK;
1792
1793 for (drvPtr = firstDrvPtr; drvPtr != NULL((void*)0); drvPtr = drvPtr->nextPtr) {
1794 if ((drvPtr->flags & DRIVER_STARTED1u) == 0u) {
1795 continue;
1796 }
1797 Ns_MutexLock(&drvPtr->lock);
1798 while ((drvPtr->flags & DRIVER_STOPPED2u) == 0u && status == NS_OK) {
1799 status = Ns_CondTimedWait(&drvPtr->cond, &drvPtr->lock, toPtr);
1800 }
1801 Ns_MutexUnlock(&drvPtr->lock);
1802 if (status != NS_OK) {
1803 Ns_Log(Warning, "[driver:%s]: shutdown timeout", drvPtr->threadName);
1804 } else {
1805 Ns_Log(Notice, "[driver:%s]: stopped", drvPtr->threadName);
1806 Ns_ThreadJoin(&drvPtr->thread, NULL((void*)0));
1807 drvPtr->thread = NULL((void*)0);
1808 }
1809 }
1810}
1811
1812
1813/*
1814 *----------------------------------------------------------------------
1815 *
1816 * NsGetRequest --
1817 *
1818 * Return the request buffer, reading it if necessary (i.e., if not an
1819 * async read-ahead connection). This function is called at the start of
1820 * connection processing.
1821 * Results:
1822 * Pointer to Request structure or NULL on error.
1823 *
1824 * Side effects:
1825 * May wait for content to arrive if necessary.
1826 *
1827 *----------------------------------------------------------------------
1828 */
1829Request *
1830NsGetRequest(Sock *sockPtr, const Ns_Time *nowPtr)
1831{
1832 Request *reqPtr;
1833
1834 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
1835
1836 /*
1837 * The underlying "Request" structure is allocated by RequestNew(), which
1838 * must be called for the "sockPtr" prior to calling this
1839 * function. "reqPtr" should be NULL just in error cases.
1840 */
1841 reqPtr = sockPtr->reqPtr;
1842
1843 if (likely(reqPtr != NULL)(__builtin_expect((reqPtr != ((void*)0)), 1))) {
1844
1845 if (likely(reqPtr->request.line != NULL)(__builtin_expect((reqPtr->request.line != ((void*)0)), 1)
)
) {
1846 Ns_Log(DriverDebug, "NsGetRequest got the pre-parsed request <%s> from the driver",
1847 reqPtr->request.line);
1848
1849 } else if (sockPtr->drvPtr->requestProc == NULL((void*)0)) {
1850 /*
1851 * Non-HTTP driver can send the drvPtr->requestProc to perform
1852 * their own request handling.
1853 */
1854 SockState status;
1855
1856 Ns_Log(DriverDebug, "NsGetRequest has to read+parse the request");
1857 /*
1858 * We have no parsed request so far. So, do it now.
1859 */
1860 do {
1861 Ns_Log(DriverDebug, "NsGetRequest calls SockRead");
1862 status = SockRead(sockPtr, 0, nowPtr);
1863 } while (status == SOCK_MORE);
1864
1865 /*
1866 * If anything went wrong, clean the request provided by
1867 * SockRead() and flag the error by returning NULL.
1868 */
1869 if (status != SOCK_READY) {
1870 if (sockPtr->reqPtr != NULL((void*)0)) {
1871 Ns_Log(DriverDebug, "NsGetRequest calls RequestFree");
1872 RequestFree(sockPtr);
1873 }
1874 reqPtr = NULL((void*)0);
1875 }
1876 } else {
1877 Ns_Log(DriverDebug, "NsGetRequest found driver specific request Proc, "
1878 "probably from a non-HTTP driver");
1879 }
1880 } else {
1881 Ns_Log(DriverDebug, "NsGetRequest has reqPtr NULL");
1882 }
1883
1884 return reqPtr;
1885}
1886
1887
1888
1889/*
1890 *----------------------------------------------------------------------
1891 *
1892 * NsSockClose --
1893 *
1894 * Return a connection to the DriverThread for closing or keepalive.
1895 * "keep" might be NS_TRUE/NS_FALSE or -1 if undecided.
1896 *
1897 * Results:
1898 * None.
1899 *
1900 * Side effects:
1901 * Socket may be reused by a keepalive connection.
1902 *
1903 *----------------------------------------------------------------------
1904 */
1905
1906void
1907NsSockClose(Sock *sockPtr, int keep)
1908{
1909 Driver *drvPtr;
1910 bool_Bool trigger = NS_FALSE0;
1911
1912 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
1913 drvPtr = sockPtr->drvPtr;
1914
1915 Ns_Log(DriverDebug, "NsSockClose sockPtr %p (%d) keep %d",
1916 (void *)sockPtr, ((Ns_Sock*)sockPtr)->sock, keep);
1917
1918 SockClose(sockPtr, keep);
1919 /*
1920 * Free the request, unless it is from a non-HTTP driver (who might not
1921 * fill out the request structure).
1922 */
1923 if (sockPtr->reqPtr != NULL((void*)0)) {
1924 Ns_Log(DriverDebug, "NsSockClose calls RequestFree");
1925 RequestFree(sockPtr);
1926 }
1927
1928 Ns_MutexLock(&drvPtr->lock);
1929 if (drvPtr->closePtr == NULL((void*)0)) {
1930 trigger = NS_TRUE1;
1931 }
1932 sockPtr->nextPtr = drvPtr->closePtr;
1933 drvPtr->closePtr = sockPtr;
1934 Ns_MutexUnlock(&drvPtr->lock);
1935
1936 if (trigger) {
1937 SockTrigger(drvPtr->trigger[1]);
1938 }
1939}
1940
1941
1942/*
1943 *----------------------------------------------------------------------
1944 *
1945 * DriverListen --
1946 *
1947 * Open a listening socket for accepting connections.
1948 *
1949 * Results:
1950 * File description of socket, or NS_INVALID_SOCKET on error.
1951 *
1952 * Side effects:
1953 * Depends on driver.
1954 *
1955 *----------------------------------------------------------------------
1956 */
1957
1958static NS_SOCKETint
1959DriverListen(Driver *drvPtr, const char *bindaddr, unsigned short port)
1960{
1961 NS_SOCKETint sock;
1962
1963 NS_NONNULL_ASSERT(drvPtr != NULL)((void) (0));
1964 NS_NONNULL_ASSERT(bindaddr != NULL)((void) (0));
1965
1966 sock = (*drvPtr->listenProc)((Ns_Driver *) drvPtr,
1967 bindaddr,
1968 port,
1969 drvPtr->backlog,
1970 drvPtr->reuseport);
1971 if (sock == NS_INVALID_SOCKET(-1)) {
1972 Ns_Log(Error, "%s: failed to listen on [%s]:%d: %s",
1973 drvPtr->threadName, bindaddr, port,
1974 ns_sockstrerrorstrerror(ns_sockerrno(*__errno_location ())));
1975 } else {
1976 Ns_Log(Notice,
1977#ifdef HAVE_IPV61
1978 "%s: listening on [%s]:%d",
1979#else
1980 "%s: listening on %s:%d",
1981#endif
1982 drvPtr->threadName, bindaddr, port);
1983 }
1984
1985 return sock;
1986}
1987
1988
1989/*
1990 *----------------------------------------------------------------------
1991 *
1992 * DriverAccept --
1993 *
1994 * Accept a new socket. It will be in nonblocking mode.
1995 *
1996 * Results:
1997 * _ACCEPT: a socket was accepted, poll for data
1998 * _ACCEPT_DATA: a socket was accepted, data present, read immediately
1999 * if in async mode, defer reading to connection thread
2000 * _ACCEPT_QUEUE: a socket was accepted, queue immediately
2001 * _ACCEPT_ERROR: no socket was accepted
2002 *
2003 * Side effects:
2004 * Depends on driver.
2005 *
2006 *----------------------------------------------------------------------
2007 */
2008
2009static NS_DRIVER_ACCEPT_STATUS
2010DriverAccept(Sock *sockPtr, NS_SOCKETint sock)
2011{
2012 socklen_t n = (socklen_t)sizeof(struct NS_SOCKADDR_STORAGEsockaddr_storage);
2013
2014 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
2015
2016 return (*sockPtr->drvPtr->acceptProc)((Ns_Sock *) sockPtr,
2017 sock,
2018 (struct sockaddr *) &(sockPtr->sa), &n);
2019}
2020
2021
2022/*
2023 *----------------------------------------------------------------------
2024 *
2025 * NsDriverRecv --
2026 *
2027 * Read data from the socket into the given vector of buffers.
2028 *
2029 * Results:
2030 * Number of bytes read, or -1 on error.
2031 *
2032 * Side effects:
2033 * Depends on driver.
2034 *
2035 *----------------------------------------------------------------------
2036 */
2037
2038ssize_t
2039NsDriverRecv(Sock *sockPtr, struct iovec *bufs, int nbufs, Ns_Time *timeoutPtr)
2040{
2041 ssize_t result;
2042 const Driver *drvPtr;
2043
2044 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
2045
2046 drvPtr = sockPtr->drvPtr;
2047
2048 if (likely(drvPtr->recvProc != NULL)(__builtin_expect((drvPtr->recvProc != ((void*)0)), 1))) {
2049 result = (*drvPtr->recvProc)((Ns_Sock *) sockPtr, bufs, nbufs, timeoutPtr, 0u);
2050 } else {
2051 Ns_Log(Warning, "driver: no recvProc registered for driver %s", drvPtr->threadName);
2052 result = -1;
2053 }
2054
2055 return result;
2056}
2057
2058
2059/*
2060 *----------------------------------------------------------------------
2061 *
2062 * NsDriverSend --
2063 *
2064 * Write a vector of buffers to the socket via the driver callback.
2065 * May not send all of the data.
2066 *
2067 * Results:
2068 * Number of bytes written or -1 on error.
2069 * May return 0 (zero) when socket is not writable.
2070 *
2071 * Side effects:
2072 * Depends on the driver.
2073 *
2074 *----------------------------------------------------------------------
2075 */
2076
2077ssize_t
2078NsDriverSend(Sock *sockPtr, const struct iovec *bufs, int nbufs, unsigned int flags)
2079{
2080 ssize_t sent = -1;
2081 const Driver *drvPtr;
2082
2083 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
2084
2085 drvPtr = sockPtr->drvPtr;
2086
2087 NS_NONNULL_ASSERT(drvPtr != NULL)((void) (0));
2088
2089 if (likely(drvPtr->sendProc != NULL)(__builtin_expect((drvPtr->sendProc != ((void*)0)), 1))) {
2090 /*
2091 * TODO: The Ns_DriverSendProc signature should be modified
2092 * to omit the timeout argument. Same with recvProc().
2093 */
2094 sent = (*drvPtr->sendProc)((Ns_Sock *) sockPtr, bufs, nbufs, NULL((void*)0), flags);
2095 } else {
2096 Ns_Log(Warning, "no sendProc registered for driver %s", drvPtr->threadName);
2097 }
2098
2099 return sent;
2100}
2101
2102
2103/*
2104 *----------------------------------------------------------------------
2105 *
2106 * NsDriverSendFile --
2107 *
2108 * Write a vector of file buffers to the socket via the driver
2109 * callback.
2110 *
2111 * Results:
2112 * Number of bytes written, -1 on error.
2113 * May not send all the data.
2114 *
2115 * Side effects:
2116 * May block on disk read.
2117 *
2118 *----------------------------------------------------------------------
2119 */
2120
2121ssize_t
2122NsDriverSendFile(Sock *sockPtr, Ns_FileVec *bufs, int nbufs, unsigned int flags)
2123{
2124 ssize_t sent;
2125 const Driver *drvPtr;
2126
2127 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
2128 NS_NONNULL_ASSERT(bufs != NULL)((void) (0));
2129
2130 drvPtr = sockPtr->drvPtr;
2131
2132 NS_NONNULL_ASSERT(drvPtr != NULL)((void) (0));
2133
2134 if (drvPtr->sendFileProc != NULL((void*)0)) {
2135 /*
2136 * TODO: The Ns_DriverSendFileProc signature should be modified
2137 * to omit the timeout argument.
2138 */
2139 sent = (*drvPtr->sendFileProc)((Ns_Sock *)sockPtr, bufs, nbufs, NULL((void*)0), flags);
2140 } else {
2141 sent = Ns_SockSendFileBufs((Ns_Sock *)sockPtr, bufs, nbufs, flags);
2142 }
2143
2144 return sent;
2145}
2146
2147
2148/*
2149 *----------------------------------------------------------------------
2150 *
2151 * DriverKeep --
2152 *
2153 * Can the given socket be kept open in the hopes that another
2154 * request will arrive before the keepwait timeout expires?
2155 *
2156 * Results:
2157 * NS_TRUE if the socket is OK for keepalive, NS_FALSE if this is not possible.
2158 *
2159 * Side effects:
2160 * Depends on driver.
2161 *
2162 *----------------------------------------------------------------------
2163 */
2164
2165static bool_Bool
2166DriverKeep(Sock *sockPtr)
2167{
2168 Ns_DriverKeepProc *keepProc;
2169 bool_Bool result;
2170
2171 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
2172
2173 keepProc = sockPtr->drvPtr->keepProc;
2174 if (keepProc == NULL((void*)0)) {
2175 result = NS_FALSE0;
2176 } else {
2177 result = (keepProc)((Ns_Sock *) sockPtr);
2178 }
2179 return result;
2180}
2181
2182
2183/*
2184 *----------------------------------------------------------------------
2185 *
2186 * DriverClose --
2187 *
2188 * Close the given socket.
2189 *
2190 * Results:
2191 * None.
2192 *
2193 * Side effects:
2194 * Depends on driver.
2195 *
2196 *----------------------------------------------------------------------
2197 */
2198
2199static void
2200DriverClose(Sock *sockPtr)
2201{
2202 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
2203 /*fprintf(stderr, "##### DriverClose (%d)\n", sockPtr->sock);*/
2204 (*sockPtr->drvPtr->closeProc)((Ns_Sock *) sockPtr);
2205}
2206
2207
2208/*
2209 *----------------------------------------------------------------------
2210 *
2211 * DriverThread --
2212 *
2213 * Main listening socket driver thread.
2214 *
2215 * Results:
2216 * None.
2217 *
2218 * Side effects:
2219 * Connections are accepted on the configured listen sockets,
2220 * placed on the run queue to be serviced, and gracefully
2221 * closed when done. Async sockets have the entire request read
2222 * here before queuing as well.
2223 *
2224 *----------------------------------------------------------------------
2225 */
2226
2227static void
2228DriverThread(void *arg)
2229{
2230 Driver *drvPtr = (Driver*)arg;
2231 Ns_Time now, diff;
2232 char charBuffer[1], drain[1024];
2233 int pollTimeout, accepted, nrBindaddrs = 0;
2234 bool_Bool stopping;
2235 unsigned int flags;
2236 Sock *sockPtr, *nextPtr, *closePtr = NULL((void*)0), *waitPtr = NULL((void*)0), *readPtr = NULL((void*)0);
2237 PollData pdata;
2238
2239 Ns_ThreadSetName("-driver:%s-", drvPtr->threadName);
2240 Ns_Log(Notice, "starting");
2241
2242 flags = DRIVER_STARTED1u;
2243
2244 {
2245 Tcl_Obj *bindaddrsObj, **objv;
2246 int j = 0, result;
2247
2248 bindaddrsObj = Tcl_NewStringObj(drvPtr->address, -1);
2249 Tcl_IncrRefCount(bindaddrsObj)++(bindaddrsObj)->refCount;
2250
2251 result = Tcl_ListObjGetElements(NULL((void*)0), bindaddrsObj, &nrBindaddrs, &objv);
2252 /*
2253 * "result" was ok during startup, it has still to be ok.
2254 */
2255 assert(result == TCL_OK)((void) (0));
2256
2257 if (result == TCL_OK0) {
2258 int i;
2259
2260 /*
2261 * Bind all provided addresses.
2262 */
2263 for (i = 0; i < nrBindaddrs && j < MAX_LISTEN_ADDR_PER_DRIVER16; i++) {
2264 size_t pNum;
2265 /*
2266 * Bind all provided ports.
2267 */
2268 for (pNum = 0u;
2269 (pNum < drvPtr->ports.size) && (j < MAX_LISTEN_ADDR_PER_DRIVER16);
2270 pNum ++
2271 ) {
2272 drvPtr->listenfd[j] = DriverListen(drvPtr,
2273 Tcl_GetString(objv[i]),
2274 DriverGetPort(drvPtr, pNum)(unsigned short)((int)(intptr_t)((drvPtr)->ports.data[(pNum
)]))
);
2275 if (drvPtr->listenfd[j] != NS_INVALID_SOCKET(-1)) {
2276 j ++;
2277 }
2278 }
2279 }
2280 if (j > 0 && j < nrBindaddrs) {
2281 Ns_Log(Warning, "could only bind to %d out of %d addresses", j, nrBindaddrs);
2282 }
2283 }
2284
2285 /*
2286 * "j" refers to the number of successful listen() operations.
2287 */
2288 nrBindaddrs = j;
2289 Tcl_DecrRefCount(bindaddrsObj)do { Tcl_Obj *_objPtr = (bindaddrsObj); if (_objPtr->refCount
-- <= 1) { TclFreeObj(_objPtr); } } while(0)
;
2290 }
2291
2292 if (nrBindaddrs > 0) {
2293 SpoolerQueueStart(drvPtr->spooler.firstPtr, SpoolerThread);
2294 SpoolerQueueStart(drvPtr->writer.firstPtr, WriterThread);
2295 } else {
2296 Ns_Log(Warning, "could no bind any of the following addresses, stopping this driver: %s", drvPtr->address);
2297 flags |= (DRIVER_FAILED8u | DRIVER_SHUTDOWN4u);
2298 }
2299
2300 Ns_MutexLock(&drvPtr->lock);
2301 drvPtr->flags |= flags;
2302 Ns_CondBroadcast(&drvPtr->cond);
2303 Ns_MutexUnlock(&drvPtr->lock);
2304
2305 /*
2306 * Loop forever until signaled to shut down and all
2307 * connections are complete and gracefully closed.
2308 */
2309
2310 PollCreate(&pdata);
2311 Ns_GetTime(&now);
2312 stopping = ((flags & DRIVER_SHUTDOWN4u) != 0u);
2313
2314 if (!stopping) {
2315 Ns_Log(Notice, "driver: accepting connections");
2316 }
2317
2318 while (!stopping) {
2319 int n;
2320 bool_Bool reanimation = NS_FALSE0;
2321
2322 /*
2323 * Set the bits for all active drivers if a connection
2324 * isn't already pending.
2325 */
2326
2327 PollReset(&pdata);
2328 (void)PollSet(&pdata, drvPtr->trigger[0], (short)POLLIN0x001, NULL((void*)0));
2329
2330 /* was peviously restricted to (waitPtr == NULL) */
2331 {
2332 for (n = 0; n < nrBindaddrs; n++) {
2333 drvPtr->pidx[n] = PollSet(&pdata, drvPtr->listenfd[n],
2334 (short)POLLIN0x001, NULL((void*)0));
2335 }
2336 }
2337
2338 /*
2339 * If there are any closing or read-ahead sockets, set the bits
2340 * and determine the minimum relative timeout.
2341 *
2342 * TODO: the various poll timeouts should probably be configurable.
2343 */
2344
2345 if (readPtr == NULL((void*)0) && closePtr == NULL((void*)0)) {
2346 pollTimeout = 10 * 1000;
2347 } else {
2348
2349 for (sockPtr = readPtr; sockPtr != NULL((void*)0); sockPtr = sockPtr->nextPtr) {
2350 SockPoll(sockPtr, (short)POLLIN0x001, &pdata);
2351 }
2352 for (sockPtr = closePtr; sockPtr != NULL((void*)0); sockPtr = sockPtr->nextPtr) {
2353 SockPoll(sockPtr, (short)POLLIN0x001, &pdata);
2354 }
2355
2356 if (Ns_DiffTime(&pdata.timeout, &now, &diff) > 0) {
2357 /*
2358 * The resolution of "pollTimeout" is ms, therefore, we round
2359 * up. If we would round down (e.g. 500 microseconds to 0 ms),
2360 * the time comparison later would determine that it is too
2361 * early.
2362 */
2363 pollTimeout = (int)Ns_TimeToMilliseconds(&diff) + 1;
2364
2365 } else {
2366 pollTimeout = 0;
2367 }
2368 }
2369
2370 n = PollWait(&pdata, pollTimeout);
2371 reanimation = PollIn(&pdata, 0)(((&pdata)->pfds[(0)].revents & 0x001) == 0x001 );
2372
2373 Ns_Log(DriverDebug, "=== PollWait returned %d, trigger[0] %d", n, reanimation);
2374
2375 if (reanimation && unlikely(ns_recv(drvPtr->trigger[0], charBuffer, 1u, 0) != 1)(__builtin_expect((recv(drvPtr->trigger[0], charBuffer, 1u
, 0) != 1), 0))
) {
2376 const char *errstr = ns_sockstrerrorstrerror(ns_sockerrno(*__errno_location ()));
2377
2378 Ns_Fatal("driver: trigger ns_recv() failed: %s", errstr);
2379 }
2380 /*
2381 * Check whether we should re-animate some connection threads,
2382 * when e.g. the number of current threads dropped below the
2383 * minimal value. Perform this test on timeouts (n == 0;
2384 * just for safety reasons) or on explicit wakeup calls.
2385 */
2386 if ((n == 0) || reanimation) {
2387 NsServer *servPtr = drvPtr->servPtr;
2388
2389 if (servPtr != NULL((void*)0)) {
2390 /*
2391 * Check if we have to reanimate the current server.
2392 */
2393 NsEnsureRunningConnectionThreads(servPtr, NULL((void*)0));
2394
2395 } else {
2396 Ns_Set *servers = Ns_ConfigGetSection("ns/servers");
2397 size_t j;
2398
2399 /*
2400 * Reanimation check on all servers.
2401 */
2402 for (j = 0u; j < Ns_SetSize(servers)((servers)->size); ++j) {
2403 const char *server = Ns_SetKey(servers, j)((servers)->fields[(j)].name);
2404
2405 servPtr = NsGetServer(server);
2406 if (servPtr != NULL((void*)0)) {
2407 NsEnsureRunningConnectionThreads(servPtr, NULL((void*)0));
2408 }
2409 }
2410 }
2411 }
2412
2413 /*
2414 * Update the current time and drain and/or release any
2415 * closing sockets.
2416 */
2417 Ns_GetTime(&now);
2418
2419 if (closePtr != NULL((void*)0)) {
2420 sockPtr = closePtr;
2421 closePtr = NULL((void*)0);
2422 while (sockPtr != NULL((void*)0)) {
2423 nextPtr = sockPtr->nextPtr;
2424 if (unlikely(PollHup(&pdata, sockPtr->pidx))(__builtin_expect(((((&pdata)->pfds[(sockPtr->pidx)
].revents & 0x010) == 0x010)), 0))
) {
2425 /*
2426 * Peer has closed the connection
2427 */
2428 SockRelease(sockPtr, SOCK_CLOSE, 0);
2429 } else if (likely(PollIn(&pdata, sockPtr->pidx))(__builtin_expect(((((&pdata)->pfds[(sockPtr->pidx)
].revents & 0x001) == 0x001 )), 1))
) {
2430 /*
2431 * Got some data
2432 */
2433 ssize_t received = ns_recvrecv(sockPtr->sock, drain, sizeof(drain), 0);
2434 if (received <= 0) {
2435 Ns_Log(DriverDebug, "poll closewait pollin; sockrelease SOCK_READERROR (sock %d)",
2436 sockPtr->sock);
2437 SockRelease(sockPtr, SOCK_READERROR, 0);
2438 } else {
2439 Push(sockPtr, closePtr)((sockPtr)->nextPtr = (closePtr), (closePtr) = (sockPtr));
2440 }
2441 } else if (Ns_DiffTime(&sockPtr->timeout, &now, &diff) <= 0) {
2442 /* no PollHup, no PollIn, maybe timeout */
2443 Ns_Log(DriverDebug, "poll closewait timeout; sockrelease SOCK_CLOSETIMEOUT (sock %d)",
2444 sockPtr->sock);
2445 SockRelease(sockPtr, SOCK_CLOSETIMEOUT, 0);
2446 } else {
2447 /* too early, keep waiting */
2448 Push(sockPtr, closePtr)((sockPtr)->nextPtr = (closePtr), (closePtr) = (sockPtr));
2449 }
2450 sockPtr = nextPtr;
2451 }
2452 }
2453
2454 /*
2455 * Attempt read-ahead of any new connections.
2456 */
2457
2458 sockPtr = readPtr;
2459 readPtr = NULL((void*)0);
2460
2461 while (likely(sockPtr != NULL)(__builtin_expect((sockPtr != ((void*)0)), 1))) {
2462 nextPtr = sockPtr->nextPtr;
2463
2464 if (unlikely(PollHup(&pdata, sockPtr->pidx))(__builtin_expect(((((&pdata)->pfds[(sockPtr->pidx)
].revents & 0x010) == 0x010)), 0))
) {
2465 /*
2466 * Peer has closed the connection
2467 */
2468 Ns_Log(DriverDebug, "Peer has closed %p", (void*)sockPtr);
2469 SockRelease(sockPtr, SOCK_CLOSE, 0);
2470
2471 } else if (unlikely(!PollIn(&pdata, sockPtr->pidx))(__builtin_expect((!(((&pdata)->pfds[(sockPtr->pidx
)].revents & 0x001) == 0x001 )), 0))
2472 && ((sockPtr->reqPtr == NULL((void*)0)) || (sockPtr->reqPtr->leftover == 0u))) {
2473 /*
2474 * Got no data for this sockPtr.
2475 */
2476 Ns_Log(DriverDebug, "Got no data for this sockPtr %p", (void*)sockPtr);
2477 if (Ns_DiffTime(&sockPtr->timeout, &now, &diff) <= 0) {
2478 SockRelease(sockPtr, SOCK_READTIMEOUT, 0);
2479 } else {
2480 Push(sockPtr, readPtr)((sockPtr)->nextPtr = (readPtr), (readPtr) = (sockPtr));
2481 }
2482
2483 } else {
2484 /*
2485 * Got some data for this sockPtr.
2486 * If enabled, perform read-ahead now.
2487 */
2488 assert(drvPtr == sockPtr->drvPtr)((void) (0));
2489 Ns_Log(DriverDebug, "Got some data for this sockPtr %p", (void*)sockPtr);
2490
2491 if (likely((drvPtr->opts & NS_DRIVER_ASYNC) != 0u)(__builtin_expect(((drvPtr->opts & 0x01u) != 0u), 1))) {
2492 SockState s = SockRead(sockPtr, 0, &now);
2493 Ns_Log(DriverDebug, "SockRead on %p returned %s", (void*)sockPtr, GetSockStateName(s));
2494
2495 /*
2496 * Queue for connection processing if ready.
2497 */
2498
2499 switch (s) {
2500 case SOCK_SPOOL:
Duplicate code detected
2501 drvPtr->stats.spooled++;
2502 if (SockSpoolerQueue(drvPtr, sockPtr) == 0) {
2503 Push(sockPtr, readPtr)((sockPtr)->nextPtr = (readPtr), (readPtr) = (sockPtr));
2504 }
2505 break;
2506
2507 case SOCK_MORE:
2508 drvPtr->stats.partial++;
2509 SockTimeout(sockPtr, &now, &drvPtr->recvwait);
2510 Push(sockPtr, readPtr)((sockPtr)->nextPtr = (readPtr), (readPtr) = (sockPtr));
2511 break;
2512
2513 case SOCK_READY:
2514 if (SockQueue(sockPtr, &now) == NS_TIMEOUT) {
2515 Push(sockPtr, waitPtr)((sockPtr)->nextPtr = (waitPtr), (waitPtr) = (sockPtr));
2516 }
2517 break;
2518
2519 /*
2520 * Already handled or normal cases
2521 */
2522 case SOCK_ENTITYTOOLARGE: NS_FALL_THROUGH((void)0); /* fall through */
2523 case SOCK_BADREQUEST: NS_FALL_THROUGH((void)0); /* fall through */
2524 case SOCK_BADHEADER: NS_FALL_THROUGH((void)0); /* fall through */
2525 case SOCK_TOOMANYHEADERS: NS_FALL_THROUGH((void)0); /* fall through */
2526 case SOCK_QUEUEFULL: NS_FALL_THROUGH((void)0); /* fall through */
2527 case SOCK_CLOSE:
2528 SockRelease(sockPtr, s, errno(*__errno_location ()));
2529 break;
2530
2531 /*
2532 * Exceptions
2533 */
2534 case SOCK_READERROR: NS_FALL_THROUGH((void)0); /* fall through */
2535 case SOCK_CLOSETIMEOUT: NS_FALL_THROUGH((void)0); /* fall through */
2536 case SOCK_ERROR: NS_FALL_THROUGH((void)0); /* fall through */
2537 case SOCK_READTIMEOUT: NS_FALL_THROUGH((void)0); /* fall through */
2538 case SOCK_SHUTERROR: NS_FALL_THROUGH((void)0); /* fall through */
2539 case SOCK_WRITEERROR: NS_FALL_THROUGH((void)0); /* fall through */
2540 case SOCK_WRITETIMEOUT:
2541 /*
2542 * Write warning just for real errors. E.g. some
2543 * modern browsers are unhappy about self-signed
2544 * certificates... these would pop up here finally (on
2545 * every request).
2546 */
2547 if (errno(*__errno_location ()) != 0) {
2548 drvPtr->stats.errors++;
2549 Ns_Log(Warning,
2550 "sockread returned unexpected result %s (err %s); close socket (%d)",
2551 GetSockStateName(s),
2552 ((errno(*__errno_location ()) != 0) ? strerror(errno(*__errno_location ())) : NS_EMPTY_STRING),
2553 sockPtr->sock);
2554 }
2555 SockRelease(sockPtr, s, errno(*__errno_location ()));
2556 break;
2557 }
2558 } else {
2559 /*
2560 * Potentially blocking driver, NS_DRIVER_ASYNC is not defined
2561 */
2562 if (Ns_DiffTime(&sockPtr->timeout, &now, &diff) <= 0) {
2563 drvPtr->stats.errors++;
2564 Ns_Log(Notice, "read-ahead has some data, no async sock read ===== diff time %ld",
2565 Ns_DiffTime(&sockPtr->timeout, &now, &diff));
2566 sockPtr->keep = NS_FALSE0;
2567 SockRelease(sockPtr, SOCK_READTIMEOUT, 0);
2568 } else {
2569 if (SockQueue(sockPtr, &now) == NS_TIMEOUT) {
2570 Push(sockPtr, waitPtr)((sockPtr)->nextPtr = (waitPtr), (waitPtr) = (sockPtr));
2571 }
2572 }
2573 }
2574 }
2575
2576 sockPtr = nextPtr;
2577 }
2578
2579 /*
2580 * Attempt to queue any pending connection after reversing the
2581 * list to ensure oldest connections are tried first.
2582 */
2583 if (reanimation && waitPtr != NULL((void*)0)) {
2584 sockPtr = NULL((void*)0);
2585 while ((nextPtr = waitPtr) != NULL((void*)0)) {
2586 waitPtr = nextPtr->nextPtr;
2587 Push(nextPtr, sockPtr)((nextPtr)->nextPtr = (sockPtr), (sockPtr) = (nextPtr));
2588 }
2589
2590 while (sockPtr != NULL((void*)0)) {
2591 nextPtr = sockPtr->nextPtr;
2592 if (SockQueue(sockPtr, &now) == NS_TIMEOUT) {
2593 Push(sockPtr, waitPtr)((sockPtr)->nextPtr = (waitPtr), (waitPtr) = (sockPtr));
2594 }
2595 sockPtr = nextPtr;
2596 }
2597 }
2598
2599 /*
2600 * If no connections are waiting, attempt to accept more.
2601 */
2602 /* was peviously restricted to (waitPtr == NULL) */
2603 {
2604 /*
2605 * If configured, try to accept more than one request, under heavy load
2606 * this helps to process more requests
2607 */
2608 bool_Bool acceptMore = NS_TRUE1;
2609
2610 accepted = 0;
2611 while (acceptMore &&
2612 accepted < drvPtr->acceptsize
2613 && drvPtr->queuesize < drvPtr->maxqueuesize ) {
2614 bool_Bool gotRequests = NS_FALSE0;
2615
2616 /*
2617 * Check for input data on all bind addresses. Stop checking,
2618 * when one round of checking on all addresses fails.
2619 */
2620
2621 for (n = 0; n < nrBindaddrs; n++) {
2622 if (PollIn(&pdata, drvPtr->pidx[n])(((&pdata)->pfds[(drvPtr->pidx[n])].revents & 0x001
) == 0x001 )
) {
2623 SockState s = SockAccept(drvPtr, pdata.pfds[drvPtr->pidx[n]].fd, &sockPtr, &now);
2624
2625 switch (s) {
2626 case SOCK_SPOOL:
Similar code here
2627 drvPtr->stats.spooled++;
2628 if (SockSpoolerQueue(drvPtr, sockPtr) == 0) {
2629 Push(sockPtr, readPtr)((sockPtr)->nextPtr = (readPtr), (readPtr) = (sockPtr));
2630 }
2631 break;
2632
2633 case SOCK_MORE:
2634 drvPtr->stats.partial++;
2635 SockTimeout(sockPtr, &now, &drvPtr->recvwait);
2636 Push(sockPtr, readPtr)((sockPtr)->nextPtr = (readPtr), (readPtr) = (sockPtr));
2637 break;
2638
2639 case SOCK_READY:
2640 if (SockQueue(sockPtr, &now) == NS_TIMEOUT) {
2641 Push(sockPtr, waitPtr)((sockPtr)->nextPtr = (waitPtr), (waitPtr) = (sockPtr));
2642 }
2643 break;
2644
2645 case SOCK_ERROR: {
2646 int sockerrno = ns_sockerrno(*__errno_location ());
2647
2648 if (sockerrno != 0 && sockerrno != NS_EAGAIN11) {
2649 Ns_Log(Warning, "sockAccept on fd %d returned error: %s",
2650 drvPtr->listenfd[n], ns_sockstrerrorstrerror(sockerrno));
2651 }
2652 break;
2653 }
2654
2655 case SOCK_BADHEADER: NS_FALL_THROUGH((void)0); /* fall through */
2656 case SOCK_BADREQUEST: NS_FALL_THROUGH((void)0); /* fall through */
2657 case SOCK_CLOSE: NS_FALL_THROUGH((void)0); /* fall through */
2658 case SOCK_CLOSETIMEOUT: NS_FALL_THROUGH((void)0); /* fall through */
2659 case SOCK_ENTITYTOOLARGE: NS_FALL_THROUGH((void)0); /* fall through */
2660 case SOCK_READERROR: NS_FALL_THROUGH((void)0); /* fall through */
2661 case SOCK_READTIMEOUT: NS_FALL_THROUGH((void)0); /* fall through */
2662 case SOCK_SHUTERROR: NS_FALL_THROUGH((void)0); /* fall through */
2663 case SOCK_TOOMANYHEADERS: NS_FALL_THROUGH((void)0); /* fall through */
2664 case SOCK_WRITEERROR: NS_FALL_THROUGH((void)0); /* fall through */
2665 case SOCK_QUEUEFULL: NS_FALL_THROUGH((void)0); /* fall through */
2666 case SOCK_WRITETIMEOUT:
2667 /*
2668 * These cases should never be returned by SockAccept()
2669 */
2670 Ns_Fatal("driver: SockAccept returned: %s", GetSockStateName(s));
2671 }
2672
2673 if (s != SOCK_ERROR) {
2674 gotRequests = NS_TRUE1;
2675 accepted++;
2676 }
2677#ifdef __APPLE__
2678 /*
2679 * On Darwin, the first accept() succeeds typically, but it is
2680 * useless to try, since this leads always to an NS_EAGAIN
2681 */
2682 acceptMore = NS_FALSE0;
2683 break;
2684#endif
2685 }
2686 if (!gotRequests) {
2687 acceptMore = NS_FALSE0;
2688 }
2689 }
2690 }
2691 if (accepted > 1) {
2692 Ns_Log(Notice, "... sockAccept accepted %d connections", accepted);
2693 }
2694 }
2695
2696 /*
2697 * Check for shut down and get the list of any closing or
2698 * keep-alive sockets.
2699 */
2700
2701 Ns_MutexLock(&drvPtr->lock);
2702 sockPtr = drvPtr->closePtr;
2703 drvPtr->closePtr = NULL((void*)0);
2704 flags = drvPtr->flags;
2705 Ns_MutexUnlock(&drvPtr->lock);
2706
2707 stopping = ((flags & DRIVER_SHUTDOWN4u) != 0u);
2708
2709 /*
2710 * Update the timeout for each closing socket and add to the
2711 * close list if some data has been read from the socket
2712 * (i.e., it is not a closing keep-alive connection).
2713 */
2714 while (sockPtr != NULL((void*)0)) {
2715 nextPtr = sockPtr->nextPtr;
2716 if (sockPtr->keep) {
2717
2718 assert(drvPtr == sockPtr->drvPtr)((void) (0));
2719
2720 Ns_Log(DriverDebug, "setting keepwait " NS_TIME_FMT"%" "l" "d" ".%06ld" " for socket %d",
2721 (int64_t)drvPtr->keepwait.sec, drvPtr->keepwait.usec,
2722 sockPtr->sock);
2723
2724 SockTimeout(sockPtr, &now, &drvPtr->keepwait);
2725 Push(sockPtr, readPtr)((sockPtr)->nextPtr = (readPtr), (readPtr) = (sockPtr));
2726 } else {
2727
2728 /*
2729 * Purely packet oriented drivers set on close the fd to
2730 * NS_INVALID_SOCKET. Since we cannot "shutdown" an UDP-socket
2731 * for writing, we bypass this call.
2732 */
2733 assert(drvPtr == sockPtr->drvPtr)((void) (0));
2734
2735 if (sockPtr->sock == NS_INVALID_SOCKET(-1)) {
2736 SockRelease(sockPtr, SOCK_CLOSE, errno(*__errno_location ()));
2737
2738 Ns_Log(DriverDebug, "DRIVER SockRelease: errno %d "
2739 "drvPtr->closewait " NS_TIME_FMT"%" "l" "d" ".%06ld",
2740 errno(*__errno_location ()), (int64_t)drvPtr->closewait.sec, drvPtr->closewait.usec);
2741
2742 } else if (shutdown(sockPtr->sock, SHUT_WRSHUT_WR) != 0) {
2743 SockRelease(sockPtr, SOCK_SHUTERROR, errno(*__errno_location ()));
2744
2745 } else {
2746 Ns_Log(DriverDebug, "setting closewait " NS_TIME_FMT"%" "l" "d" ".%06ld" " for socket %d",
2747 (int64_t)drvPtr->closewait.sec, drvPtr->closewait.usec, sockPtr->sock);
2748 SockTimeout(sockPtr, &now, &drvPtr->closewait);
2749 Push(sockPtr, closePtr)((sockPtr)->nextPtr = (closePtr), (closePtr) = (sockPtr));
2750 }
2751 }
2752 sockPtr = nextPtr;
2753 }
2754
2755 /*
2756 * Close the active drivers if shutdown is pending.
2757 */
2758
2759 if (stopping) {
2760 for (n = 0; n < nrBindaddrs; n++) {
2761 ns_sockcloseclose(drvPtr->listenfd[n]);
2762 drvPtr->listenfd[n] = NS_INVALID_SOCKET(-1);
2763 }
2764 }
2765 }
2766
2767 PollFree(&pdata);
2768
2769 {
2770 Tcl_HashSearch search;
2771 Tcl_HashEntry *hPtr;
2772
2773 hPtr = Tcl_FirstHashEntry(&drvPtr->hosts, &search);
2774 while (hPtr != NULL((void*)0)) {
2775 void *host;
2776
2777 host = (void*)Tcl_GetHashValue(hPtr)((hPtr)->clientData);
2778 ns_free(host);
2779 Tcl_DeleteHashEntry(hPtr);
2780 hPtr = Tcl_NextHashEntry(&search);
2781 }
2782 Tcl_DeleteHashTable(&drvPtr->hosts);
2783 }
2784
2785 /*fprintf(stderr, "==== driver exit %p closePtr %p waitPtr %p readPtr %p\n",
2786 (void*)drvPtr, (void*)closePtr, (void*)waitPtr, (void*)readPtr);*/
2787 for (sockPtr = readPtr; sockPtr != NULL((void*)0); sockPtr = nextPtr) {
2788 nextPtr = sockPtr->nextPtr;
2789 /*fprintf(stderr, "==== driver exit read %p \n", (void*)sockPtr);*/
2790 ns_free(sockPtr);
2791 }
2792
2793 Ns_Log(Notice, "exiting");
2794
2795 Ns_MutexLock(&drvPtr->lock);
2796 drvPtr->flags |= DRIVER_STOPPED2u;
2797 Ns_CondBroadcast(&drvPtr->cond);
2798 Ns_MutexUnlock(&drvPtr->lock);
2799}
2800
2801static void
2802PollCreate(PollData *pdata)
2803{
2804 NS_NONNULL_ASSERT(pdata != NULL)((void) (0));
2805 memset(pdata, 0, sizeof(PollData));
2806}
2807
2808static void
2809PollFree(PollData *pdata)
2810{
2811 NS_NONNULL_ASSERT(pdata != NULL)((void) (0));
2812 ns_free(pdata->pfds);
2813 memset(pdata, 0, sizeof(PollData));
2814}
2815
2816static void
2817PollReset(PollData *pdata)
2818{
2819 NS_NONNULL_ASSERT(pdata != NULL)((void) (0));
2820 pdata->nfds = 0u;
2821 pdata->timeout.sec = TIME_T_MAX9223372036854775807L;
2822 pdata->timeout.usec = 0;
2823}
2824
2825static NS_POLL_NFDS_TYPEnfds_t
2826PollSet(PollData *pdata, NS_SOCKETint sock, short type, const Ns_Time *timeoutPtr)
2827{
2828 NS_NONNULL_ASSERT(pdata != NULL)((void) (0));
2829 /*
2830 * Grow the pfds array if necessary.
2831 */
2832
2833 if (unlikely(pdata->nfds >= pdata->maxfds)(__builtin_expect((pdata->nfds >= pdata->maxfds), 0)
)
) {
2834 pdata->maxfds += 100u;
2835 pdata->pfds = ns_realloc(pdata->pfds, pdata->maxfds * sizeof(struct pollfd));
2836 }
2837
2838 /*
2839 * Set the next pollfd struct with this socket.
2840 */
2841
2842 pdata->pfds[pdata->nfds].fd = sock;
2843 pdata->pfds[pdata->nfds].events = type;
2844 pdata->pfds[pdata->nfds].revents = 0;
2845
2846 /*
2847 * Check for new minimum timeout.
2848 */
2849
2850 if (timeoutPtr != NULL((void*)0) && Ns_DiffTime(timeoutPtr, &pdata->timeout, NULL((void*)0)) < 0) {
2851 pdata->timeout = *timeoutPtr;
2852 }
2853
2854 return pdata->nfds++;
2855}
2856
2857static int
2858PollWait(const PollData *pdata, int timeout)
2859{
2860 int n;
2861
2862 NS_NONNULL_ASSERT(pdata != NULL)((void) (0));
2863
2864 do {
2865 n = ns_poll(pdata->pfds, pdata->nfds, timeout);
2866 } while (n < 0 && errno(*__errno_location ()) == NS_EINTR4);
2867
2868 if (n < 0) {
2869 Ns_Fatal("PollWait: ns_poll() failed: %s", ns_sockstrerrorstrerror(ns_sockerrno(*__errno_location ())));
2870 }
2871 return n;
2872}
2873
2874/*
2875 *----------------------------------------------------------------------
2876 *
2877 * RequestNew
2878 *
2879 * Allocates or reuses a "Request" struct. The struct might be reused
2880 * from the pool or freshly allocated. Counterpart of RequestFree().
2881 *
2882 * Results:
2883 * None
2884 *
2885 * Side effects:
2886 * None
2887 *
2888 *----------------------------------------------------------------------
2889 */
2890
2891static Request *
2892RequestNew(void)
2893{
2894 Request *reqPtr;
2895 bool_Bool reuseRequest = NS_TRUE1;
2896
2897 /*
2898 * Try to get a request from the pool of allocated Requests.
2899 */
2900 Ns_MutexLock(&reqLock);
2901 reqPtr = firstReqPtr;
2902 if (likely(reqPtr != NULL)(__builtin_expect((reqPtr != ((void*)0)), 1))) {
2903 firstReqPtr = reqPtr->nextPtr;
2904 } else {
2905 reuseRequest = NS_FALSE0;
2906 }
2907 Ns_MutexUnlock(&reqLock);
2908
2909 if (reuseRequest) {
2910 Ns_Log(DriverDebug, "RequestNew reuses a Request");
2911 }
2912
2913 /*
2914 * In case we failed, allocate a new Request.
2915 */
2916 if (reqPtr == NULL((void*)0)) {
2917 Ns_Log(DriverDebug, "RequestNew gets a fresh Request");
2918 reqPtr = ns_calloc(1u, sizeof(Request));
2919 Tcl_DStringInit(&reqPtr->buffer);
2920 reqPtr->headers = NsHeaderSetGet(10);
2921 }
2922
2923 return reqPtr;
2924}
2925
2926
2927/*
2928 *----------------------------------------------------------------------
2929 *
2930 * RequestFree --
2931 *
2932 * Free/clean a socket request structure. This routine is called
2933 * at the end of connection processing or on a socket which
2934 * times out during async read-ahead. Counterpart of RequestNew().
2935 *
2936 * Results:
2937 * None.
2938 *
2939 * Side effects:
2940 * None.
2941 *
2942 *----------------------------------------------------------------------
2943 */
2944
2945static void
2946RequestFree(Sock *sockPtr)
2947{
2948 Request *reqPtr;
2949 bool_Bool keep;
2950
2951 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
2952
2953 /*
2954 * Clear poolPtr assignment, since this is closely related to the request
2955 * info. Otherwise, it might survive for persistent connections, and can
2956 * lead to incorrect pool assignments.
2957 */
2958 sockPtr->poolPtr = NULL((void*)0);
2959
2960 /*
2961 * Cleanup the request info. When (true) pipelining is active, we have to
2962 * perform leftover management for some requests which might be (partly)
2963 * already read in.
2964 */
2965 reqPtr = sockPtr->reqPtr;
2966 assert(reqPtr != NULL)((void) (0));
2967
2968 Ns_Log(DriverDebug, "=== RequestFree cleans %p (avail %" PRIuz"zu"
2969 " keep %d length %" PRIuz"zu" " contentLength %" PRIuz"zu" ")",
2970 (void *)reqPtr, reqPtr->avail, sockPtr->keep, reqPtr->length, reqPtr->contentLength);
2971
2972 keep = (sockPtr->keep) && (reqPtr->avail > reqPtr->contentLength);
2973 if (keep) {
2974 size_t leftover = reqPtr->avail - reqPtr->contentLength;
2975 const char *offset = reqPtr->buffer.string + ((size_t)reqPtr->buffer.length - leftover);
2976
2977 Ns_Log(DriverDebug, "setting leftover to %" PRIuz"zu" " bytes", leftover);
2978 /*
2979 * Here it is safe to move the data in the buffer, although the
2980 * reqPtr->content might point to it, since we re-init the content. In
2981 * case the terminating null character was written to the end of the
2982 * previous buffer, we have to restore the first character.
2983 */
2984 memmove(reqPtr->buffer.string, offset, leftover);
2985 if (reqPtr->savedChar != '\0') {
2986 reqPtr->buffer.string[0] = reqPtr->savedChar;
2987 }
2988 Tcl_DStringSetLength(&reqPtr->buffer, (int)leftover);
2989 LogBuffer(DriverDebug, "KEEP BUFFER", reqPtr->buffer.string, leftover);
2990 reqPtr->leftover = leftover;
2991 } else {
2992 /*
2993 * Clean large buffers in order to avoid memory growth on huge
2994 * uploads (when maxupload is huge)
2995 */
2996 /*fprintf(stderr, "=== reuse buffer size %d avail %d dynamic %d\n",
2997 reqPtr->buffer.length, reqPtr->buffer.spaceAvl,
2998 reqPtr->buffer.string == reqPtr->buffer.staticSpace);*/
2999 if (Tcl_DStringLength(&reqPtr->buffer)((&reqPtr->buffer)->length) > 65536) {
3000 Tcl_DStringFree(&reqPtr->buffer);
3001 } else {
3002 /*
3003 * Reuse buffer, but set length to 0.
3004 */
3005 Tcl_DStringSetLength(&reqPtr->buffer, 0);
3006 }
3007 reqPtr->leftover = 0u;
3008 }
3009
3010 reqPtr->next = NULL((void*)0);
3011 reqPtr->content = NULL((void*)0);
3012 reqPtr->length = 0u;
3013 reqPtr->contentLength = 0u;
3014
3015 reqPtr->expectedLength = 0u;
3016 reqPtr->chunkStartOff = 0u;
3017 reqPtr->chunkWriteOff = 0u;
3018
3019 reqPtr->roff = 0u;
3020 reqPtr->woff = 0u;
3021 reqPtr->coff = 0u;
3022 reqPtr->avail = 0u;
3023 reqPtr->savedChar = '\0';
3024
3025 /*
3026 * The headers should be already cleared, except maybe in error cases.
3027 * Maybe, this should be moved to the error handling, and the assert
3028 * should be established here.
3029 */
3030 /*assert(reqPtr->headers->size == 0);*/
3031 if (reqPtr->headers->size > 0) {
3032#ifdef NS_SET_DSTRING
3033 Ns_Log(Warning, "RequestFree must trunc reqPtr->headers %p->%p: size %lu/%lu buffer %d/%d",
3034 (void*)reqPtr, (void*)reqPtr->headers,
3035 reqPtr->headers->size, reqPtr->headers->maxSize,
3036 reqPtr->headers->data.length, reqPtr->headers->data.spaceAvl);
3037#endif
3038 Ns_SetTrunc(reqPtr->headers, 0u);
3039 };
3040
3041 if (reqPtr->auth != NULL((void*)0)) {
3042 Ns_SetFree(reqPtr->auth);
3043 reqPtr->auth = NULL((void*)0);
3044 }
3045
3046 if (reqPtr->request.line != NULL((void*)0)) {
3047 Ns_Log(DriverDebug, "RequestFree calls Ns_ResetRequest on %p", (void*)&reqPtr->request);
3048 Ns_ResetRequest(&reqPtr->request);
3049 } else {
3050 Ns_Log(DriverDebug, "RequestFree does not call Ns_ResetRequest on %p", (void*)&reqPtr->request);
3051 }
3052
3053 if (!keep) {
3054 /*
3055 * Push the reqPtr to the pool for reuse in other connections.
3056 */
3057 sockPtr->reqPtr = NULL((void*)0);
3058
3059 Ns_MutexLock(&reqLock);
3060 reqPtr->nextPtr = firstReqPtr;
3061 firstReqPtr = reqPtr;
3062 Ns_MutexUnlock(&reqLock);
3063 Ns_Log(DriverDebug, "=== Push request structure %p in (to pool)",
3064 (void*)reqPtr);
3065
3066 } else {
3067 /*
3068 * Keep the partly cleaned up reqPtr associated with the connection.
3069 */
3070 Ns_Log(DriverDebug, "=== KEEP request structure %p in sockPtr (don't push into the pool)",
3071 (void*)reqPtr);
3072 }
3073}
3074
3075/*
3076 *----------------------------------------------------------------------
3077 *
3078 * SockQueue --
3079 *
3080 * Puts socket into connection queue and handle the NS_ERROR case.
3081 *
3082 * Results:
3083 * Ns_ReturnCode, potential values NS_TRUE, NS_FALSE, NS_TIMEOUT
3084 *
3085 * Side effects:
3086 * None.
3087 *
3088 *----------------------------------------------------------------------
3089 */
3090
3091static Ns_ReturnCode
3092SockQueue(Sock *sockPtr, const Ns_Time *timePtr)
3093{
3094 Ns_ReturnCode result;
3095
3096 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
3097 /*
3098 * Verify the conditions. Request struct must exist already.
3099 */
3100 assert(sockPtr->reqPtr != NULL)((void) (0));
3101
3102 SockSetServer(sockPtr);
3103 assert(sockPtr->servPtr != NULL)((void) (0));
3104
3105 /*
3106 * Actual queueing. When we receive NS_ERROR or NS_TIMEOUT, the queuing
3107 * did not succeed.
3108 */
3109 result = NsQueueConn(sockPtr, timePtr);
3110 if (result == NS_ERROR) {
3111 SockRelease(sockPtr, SOCK_QUEUEFULL, 0);
3112 }
3113
3114 return result;
3115}
3116
3117
3118/*
3119 *----------------------------------------------------------------------
3120 *
3121 * SockPoll --
3122 *
3123 * Arrange for given Sock to be monitored.
3124 *
3125 * Results:
3126 * None.
3127 *
3128 * Side effects:
3129 * Sock fd will be monitored for readability on next spin of
3130 * DriverThread.
3131 *
3132 *----------------------------------------------------------------------
3133 */
3134
3135static void
3136SockPoll(Sock *sockPtr, short type, PollData *pdata)
3137{
3138 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
3139 NS_NONNULL_ASSERT(pdata != NULL)((void) (0));
3140
3141 sockPtr->pidx = PollSet(pdata, sockPtr->sock, type, &sockPtr->timeout);
3142}
3143
3144/*
3145 *----------------------------------------------------------------------
3146 *
3147 * SockTimeout --
3148 *
3149 * Update socket with timeout
3150 *
3151 * Results:
3152 * None.
3153 *
3154 * Side effects:
3155 * Socket timeout will have nowPtr + timeout value
3156 *
3157 *----------------------------------------------------------------------
3158 */
3159
3160static void
3161SockTimeout(Sock *sockPtr, const Ns_Time *nowPtr, const Ns_Time *timeout)
3162{
3163 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
3164 sockPtr->timeout = *nowPtr;
3165 Ns_IncrTime(&sockPtr->timeout, timeout->sec, timeout->usec);
3166}
3167
3168
3169
3170/*
3171 *----------------------------------------------------------------------
3172 *
3173 * SockAccept --
3174 *
3175 * Accept and initialize a new Sock in sockPtrPtr.
3176 *
3177 * Results:
3178 * SOCK_READY, SOCK_MORE, SOCK_SPOOL,
3179 * SOCK_ERROR + NULL sockPtr.
3180 *
3181 * Side effects:
3182 * Read-ahead may be attempted on new socket.
3183 *
3184 *----------------------------------------------------------------------
3185 */
3186
3187static SockState
3188SockAccept(Driver *drvPtr, NS_SOCKETint sock, Sock **sockPtrPtr, const Ns_Time *nowPtr)
3189{
3190 Sock *sockPtr;
3191 SockState sockStatus;
3192 NS_DRIVER_ACCEPT_STATUS status;
3193
3194 NS_NONNULL_ASSERT(drvPtr != NULL)((void) (0));
3195
3196 sockPtr = SockNew(drvPtr);
3197
3198 /*
3199 * Accept the new connection.
3200 */
3201
3202 status = DriverAccept(sockPtr, sock);
3203
3204 if (unlikely(status == NS_DRIVER_ACCEPT_ERROR)(__builtin_expect((status == NS_DRIVER_ACCEPT_ERROR), 0))) {
3205 sockStatus = SOCK_ERROR;
3206
3207 /*
3208 * We reach the place frequently, especially on Linux, when we try to
3209 * accept multiple connection in one sweep. Usually, the errno is
3210 * NS_EAGAIN.
3211 */
3212
3213 Ns_MutexLock(&drvPtr->lock);
3214 sockPtr->nextPtr = drvPtr->sockPtr;
3215 drvPtr->sockPtr = sockPtr;
3216 Ns_MutexUnlock(&drvPtr->lock);
3217 /*fprintf(stderr, "=== NS_DRIVER_ACCEPT_ERROR drv %p got %p\n", (void*)drvPtr, (void*)sockPtr);*/
3218
3219 sockPtr = NULL((void*)0);
3220
3221 } else {
3222 sockPtr->acceptTime = *nowPtr;
3223 drvPtr->queuesize++;
3224
3225 if (status == NS_DRIVER_ACCEPT_DATA) {
3226
3227 /*
3228 * If there is already data present then read it without
3229 * polling if we're in async mode.
3230 */
3231
3232 if ((drvPtr->opts & NS_DRIVER_ASYNC0x01u) != 0u) {
3233 sockStatus = SockRead(sockPtr, 0, nowPtr);
3234 if ((int)sockStatus < 0) {
3235 Ns_Log(DriverDebug, "SockRead returned error %s",
3236 GetSockStateName(sockStatus));
3237
3238 SockRelease(sockPtr, sockStatus, errno(*__errno_location ()));
3239 sockStatus = SOCK_ERROR;
3240 sockPtr = NULL((void*)0);
3241 }
3242 } else {
3243
3244 /*
3245 * Queue this socket without reading, NsGetRequest() in the
3246 * connection thread will perform actual reading of the
3247 * request.
3248 */
3249 sockStatus = SOCK_READY;
3250 }
3251 } else if (status == NS_DRIVER_ACCEPT_QUEUE) {
3252
3253 /*
3254 * We need to call RequestNew() to make sure socket has request
3255 * structure allocated, otherwise NsGetRequest() will call
3256 * SockRead() which is not what this driver wants.
3257 */
3258 if (sockPtr->reqPtr == NULL((void*)0)) {
3259 sockPtr->reqPtr = RequestNew();
3260 }
3261 sockStatus = SOCK_READY;
3262 } else {
3263 sockStatus = SOCK_MORE;
3264 }
3265 }
3266
3267 *sockPtrPtr = sockPtr;
3268
3269 return sockStatus;
3270}
3271
3272
3273/*
3274 *----------------------------------------------------------------------
3275 *
3276 * SockNew --
3277 *
3278 * Allocate and/or initialize a Sock structure. Counterpart of
3279 * SockRelease().
3280 *
3281 * Results:
3282 * SockPtr
3283 *
3284 * Side effects:
3285 * Potentially new memory is allocated.
3286 *
3287 *----------------------------------------------------------------------
3288 */
3289
3290static Sock *
3291SockNew(Driver *drvPtr)
3292{
3293 Sock *sockPtr;
3294
3295 NS_NONNULL_ASSERT(drvPtr != NULL)((void) (0));
3296
3297 Ns_MutexLock(&drvPtr->lock);
3298 sockPtr = drvPtr->sockPtr;
3299 if (likely(sockPtr != NULL)(__builtin_expect((sockPtr != ((void*)0)), 1))) {
3300 drvPtr->sockPtr = sockPtr->nextPtr;
3301 sockPtr->keep = NS_FALSE0;
3302 /*fprintf(stderr, "=== SockNew drv %p got %p set %p\n", (void*)drvPtr, (void*)sockPtr, (void*)drvPtr->sockPtr);*/
3303
3304 }
3305 Ns_MutexUnlock(&drvPtr->lock);
3306
3307 if (sockPtr == NULL((void*)0)) {
3308 size_t sockSize = sizeof(Sock) + (nsconf.nextSlsId * sizeof(Ns_Callback *));
3309 sockPtr = ns_calloc(1u, sockSize);
3310 /*fprintf(stderr, "=== SockNew %p\n", (void*)sockPtr);*/
3311 sockPtr->drvPtr = drvPtr;
3312 } else {
3313 sockPtr->tfd = 0;
3314 sockPtr->taddr = NULL((void*)0);
3315 sockPtr->flags = 0u;
3316 sockPtr->arg = NULL((void*)0);
3317 sockPtr->poolPtr = NULL((void*)0);
3318 sockPtr->recvSockState = NS_SOCK_NONE;
3319 sockPtr->recvErrno = 0u;
3320 }
3321 return sockPtr;
3322}
3323
3324
3325/*
3326 *----------------------------------------------------------------------
3327 *
3328 * SockRelease --
3329 *
3330 * Close a socket and release the connection structure for
3331 * re-use.
3332 *
3333 * Results:
3334 * None.
3335 *
3336 * Side effects:
3337 * None.
3338 *
3339 *----------------------------------------------------------------------
3340 */
3341
3342static void
3343SockRelease(Sock *sockPtr, SockState reason, int err)
3344{
3345 Driver *drvPtr;
3346
3347 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
3348
3349 Ns_Log(DriverDebug, "SockRelease reason %s err %d (sock %d)",
3350 GetSockStateName(reason), err, sockPtr->sock);
3351
3352 /*fprintf(stderr, "=== SockRelease %p\n", (void*)sockPtr);*/
3353
3354 drvPtr = sockPtr->drvPtr;
3355 assert(drvPtr != NULL)((void) (0));
3356
3357 SockError(sockPtr, reason, err);
3358
3359 if (sockPtr->sock != NS_INVALID_SOCKET(-1)) {
3360 SockClose(sockPtr, (int)NS_FALSE0);
3361 } else {
3362 Ns_Log(DriverDebug, "SockRelease bypasses SockClose, since we have an invalid socket");
3363 }
3364 NsSlsCleanup(sockPtr);
3365
3366 drvPtr->queuesize--;
3367
3368 if (sockPtr->reqPtr != NULL((void*)0)) {
3369 Ns_Log(DriverDebug, "SockRelease calls RequestFree");
3370 RequestFree(sockPtr);
3371 }
3372
3373 Ns_MutexLock(&drvPtr->lock);
3374 sockPtr->nextPtr = drvPtr->sockPtr;
3375 drvPtr->sockPtr = sockPtr;
3376 Ns_MutexUnlock(&drvPtr->lock);
3377 /*fprintf(stderr, "=== SockRelease drv %p got %p\n", (void*)drvPtr, (void*)sockPtr);*/
3378
3379}
3380
3381
3382/*
3383 *----------------------------------------------------------------------
3384 *
3385 * SockError --
3386 *
3387 * Log error message for given socket
3388 * re-use.
3389 *
3390 * Results:
3391 * None.
3392 *
3393 * Side effects:
3394 * None.
3395 *
3396 *----------------------------------------------------------------------
3397 */
3398
3399static void
3400SockError(Sock *sockPtr, SockState reason, int err)
3401{
3402 const char *errMsg = NULL((void*)0);
3403
3404 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
3405
3406 switch (reason) {
3407 case SOCK_READY: NS_FALL_THROUGH((void)0); /* fall through */
3408 case SOCK_SPOOL: NS_FALL_THROUGH((void)0); /* fall through */
3409 case SOCK_MORE: NS_FALL_THROUGH((void)0); /* fall through */
3410 case SOCK_CLOSE: NS_FALL_THROUGH((void)0); /* fall through */
3411 case SOCK_CLOSETIMEOUT:
3412 /* This is normal, never log. */
3413 break;
3414
3415 case SOCK_READTIMEOUT:
3416 /*
3417 * For this case, whether this is acceptable or not
3418 * depends upon whether this sock was a keep-alive
3419 * that we were allowing to 'linger'.
3420 */
3421 if (!sockPtr->keep) {
3422 errMsg = "Timeout during read";
3423 }
3424 break;
3425
3426 case SOCK_WRITETIMEOUT:
3427 errMsg = "Timeout during write";
3428 break;
3429
3430 case SOCK_READERROR:
3431 errMsg = "Unable to read request";
3432 break;
3433
3434 case SOCK_WRITEERROR:
3435 errMsg = "Unable to write request";
3436 break;
3437
3438 case SOCK_SHUTERROR:
3439 errMsg = "Unable to shutdown socket";
3440 break;
3441
3442 case SOCK_BADREQUEST:
3443 errMsg = "Bad Request";
3444 SockSendResponse(sockPtr, 400, errMsg, NULL((void*)0));
3445 break;
3446
3447 case SOCK_TOOMANYHEADERS:
3448 errMsg = "Too Many Request Headers";
3449 SockSendResponse(sockPtr, 414, errMsg, NULL((void*)0));
3450 break;
3451
3452 case SOCK_BADHEADER:
3453 errMsg = "Invalid Request Header";
3454 SockSendResponse(sockPtr, 400, errMsg, NULL((void*)0));
3455 break;
3456
3457 case SOCK_ENTITYTOOLARGE:
3458 errMsg = "Request Entity Too Large";
3459 SockSendResponse(sockPtr, 413, errMsg, NULL((void*)0));
3460 break;
3461
3462 case SOCK_ERROR:
3463 errMsg = "Unknown Error";
3464 SockSendResponse(sockPtr, 400, errMsg, NULL((void*)0));
3465 break;
3466
3467 case SOCK_QUEUEFULL:
3468 errMsg = "Service Unavailable";
3469 if (sockPtr->poolPtr != NULL((void*)0) && sockPtr->poolPtr->wqueue.retryafter.sec > 0) {
3470 char headers[14 + TCL_INTEGER_SPACE24];
3471
3472 snprintf(headers, sizeof(headers), "Retry-After: %" PRId64,__builtin___snprintf_chk (headers, sizeof(headers), 2 - 1, __builtin_object_size
(headers, 2 > 1), "Retry-After: %" "l" "d", (int64_t)sockPtr
->poolPtr->wqueue.retryafter.sec)
3473 (int64_t)sockPtr->poolPtr->wqueue.retryafter.sec)__builtin___snprintf_chk (headers, sizeof(headers), 2 - 1, __builtin_object_size
(headers, 2 > 1), "Retry-After: %" "l" "d", (int64_t)sockPtr
->poolPtr->wqueue.retryafter.sec)
;
3474 SockSendResponse(sockPtr, 503, errMsg, headers);
3475 } else {
3476 SockSendResponse(sockPtr, 503, errMsg, NULL((void*)0));
3477 }
3478 break;
3479 }
3480
3481 if (errMsg != NULL((void*)0)) {
3482 char ipString[NS_IPADDR_SIZE46];
3483
3484 Ns_Log(DriverDebug, "SockError: %s (%d: %s), sock: %d, peer: [%s]:%d, request: %.99s",
3485 errMsg,
3486 err, (err != 0) ? strerror(err) : NS_EMPTY_STRING,
3487 sockPtr->sock,
3488 ns_inet_ntop((struct sockaddr *)&(sockPtr->sa), ipString, sizeof(ipString)),
3489 Ns_SockaddrGetPort((struct sockaddr *)&(sockPtr->sa)),
3490 (sockPtr->reqPtr != NULL((void*)0)) ? sockPtr->reqPtr->buffer.string : NS_EMPTY_STRING);
3491 }
3492}
3493
3494/*
3495 *----------------------------------------------------------------------
3496 *
3497 * SockSendResponse --
3498 *
3499 * Send an HTTP response directly to the client using the
3500 * driver callback.
3501 *
3502 * Results:
3503 * None.
3504 *
3505 * Side effects:
3506 * May not sent the complete response to the client
3507 * if encountering non-writable connection socket.
3508 *
3509 *----------------------------------------------------------------------
3510 */
3511
3512static void
3513SockSendResponse(Sock *sockPtr, int statusCode, const char *errMsg, const char *headers)
3514{
3515 struct iovec iov[5];
3516 char firstline[32];
3517 ssize_t sent, tosend;
3518 int nbufs;
3519
3520 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
3521 NS_NONNULL_ASSERT(errMsg != NULL)((void) (0));
3522
3523 snprintf(firstline, sizeof(firstline), "HTTP/1.0 %d ", statusCode)__builtin___snprintf_chk (firstline, sizeof(firstline), 2 - 1
, __builtin_object_size (firstline, 2 > 1), "HTTP/1.0 %d "
, statusCode)
;
3524 iov[0].iov_base = firstline;
3525 iov[0].iov_len = strlen(firstline);
3526 iov[1].iov_base = (void *)errMsg;
3527 iov[1].iov_len = strlen(errMsg);
3528 if (headers == NULL((void*)0)) {
3529 iov[2].iov_base = (void *)"\r\n\r\n";
3530 iov[2].iov_len = 4u;
3531 nbufs = 3;
3532 } else {
3533 iov[2].iov_base = (void *)"\r\n";
3534 iov[2].iov_len = 2u;
3535 iov[3].iov_base = (char *)headers;
3536 iov[3].iov_len = strlen(headers);
3537 iov[4].iov_base = (void *)"\r\n\r\n";
3538 iov[4].iov_len = 4u;
3539 nbufs = 5;
3540 }
3541 tosend = (ssize_t)(iov[0].iov_len + iov[1].iov_len + iov[2].iov_len);
3542 sent = NsDriverSend(sockPtr, iov, nbufs, 0u);
3543 if (sent < tosend) {
3544 Ns_Log(Warning, "Driver: partial write while sending response;"
3545 " %" PRIdz"zd" " < %" PRIdz"zd", sent, tosend);
3546 }
3547
3548 /*
3549 * In case we have a request structure, complain in the system log about
3550 * the bad request.
3551 */
3552 if (sockPtr->reqPtr != NULL((void*)0)) {
3553 Request *reqPtr = sockPtr->reqPtr;
3554 const char *requestLine = (reqPtr->request.line != NULL((void*)0))
3555 ? reqPtr->request.line
3556 : NS_EMPTY_STRING;
3557
3558 /*
3559 * Check, if bad request looks like a TLS handshake. If yes, there is
3560 * no need to print out the received buffer.
3561 */
3562 if (unlikely(statusCode == 400)(__builtin_expect((statusCode == 400), 0))) {
3563 char peer[NS_IPADDR_SIZE46];
3564 const char *bufferString = reqPtr->buffer.string;
3565
3566 (void)ns_inet_ntop((struct sockaddr *)&(sockPtr->sa), peer, NS_IPADDR_SIZE46);
3567
3568 if (bufferString[0] == (char)0x16 && bufferString[1] >= 3 && bufferString[2] == 1) {
3569 Ns_Log(Warning, "invalid request %d (%s) from peer %s: received TLS handshake "
3570 "on a non-TLS connection",
3571 statusCode, errMsg, peer);
3572
3573 } else {
3574 Tcl_DString dsReqLine;
3575
3576 /*
3577 * These are errors, which might need some investigation based
3578 * on based on details of the received buffer.
3579 */
3580 Tcl_DStringInit(&dsReqLine);
3581 Ns_Log(Warning, "invalid request: %d (%s) from peer %s request '%s'"
3582 " offsets: read %" PRIuz"zu"
3583 " write %" PRIuz"zu" " content %" PRIuz"zu" " avail %" PRIuz"zu",
3584 statusCode, errMsg,
3585 peer,
3586 Ns_DStringAppendPrintable(&dsReqLine, NS_FALSE0, requestLine, strlen(requestLine)),
3587 reqPtr->roff,
3588 reqPtr->woff,
3589 reqPtr->coff,
3590 reqPtr->avail);
3591 Tcl_DStringFree(&dsReqLine);
3592
3593 LogBuffer(Warning, "REQ BUFFER", reqPtr->buffer.string, (size_t)reqPtr->buffer.length);
3594 }
3595 } else if (statusCode >= 500) {
3596 Ns_Log(Warning, "request returns %d (%s): %s", statusCode, errMsg, requestLine);
3597 }
3598 } else {
3599 Ns_Log(Warning, "invalid request: %d (%s) - no request information available",
3600 statusCode, errMsg);
3601 }
3602}
3603
3604
3605/*
3606 *----------------------------------------------------------------------
3607 *
3608 * SockTrigger --
3609 *
3610 * Wakeup DriversThread from blocking ns_poll().
3611 *
3612 * Results:
3613 * None.
3614 *
3615 * Side effects:
3616 * DriversThread will wake up.
3617 *
3618 *----------------------------------------------------------------------
3619 */
3620
3621static void
3622SockTrigger(NS_SOCKETint sock)
3623{
3624 if (send(sock, NS_EMPTY_STRING, 1, 0) != 1) {
3625 const char *errstr = ns_sockstrerrorstrerror(ns_sockerrno(*__errno_location ()));
3626
3627 Ns_Log(Error, "driver: trigger send() failed: %s", errstr);
3628 }
3629}
3630
3631/*
3632 *----------------------------------------------------------------------
3633 *
3634 * SockClose --
3635 *
3636 * Closes connection socket, does all cleanups. The input parameter
3637 * "keep" might be NS_TRUE/NS_FALSE or -1 if undecided.
3638 *
3639 * Results:
3640 * None.
3641 *
3642 * Side effects:
3643 * None
3644 *
3645 *----------------------------------------------------------------------
3646 */
3647
3648static void
3649SockClose(Sock *sockPtr, int keep)
3650{
3651 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
3652
3653 if (keep != 0) {
3654 bool_Bool driverKeep = DriverKeep(sockPtr);
3655 keep = (int)driverKeep;
3656 }
3657 if (keep == (int)NS_FALSE0) {
3658 DriverClose(sockPtr);
3659 }
3660 Ns_MutexLock(&sockPtr->drvPtr->lock);
3661 sockPtr->keep = (bool_Bool)keep;
3662 Ns_MutexUnlock(&sockPtr->drvPtr->lock);
3663
3664 /*
3665 * Unconditionally remove temporary file, connection thread
3666 * should take care about very large uploads.
3667 */
3668
3669 if (sockPtr->tfile != NULL((void*)0)) {
3670 unlink(sockPtr->tfile);
3671 ns_free(sockPtr->tfile);
3672 sockPtr->tfile = NULL((void*)0);
3673
3674 if (sockPtr->tfd > 0) {
3675 /*
3676 * Close and reset fd. The fd should be > 0 unless we are in error
3677 * conditions.
3678 */
3679 (void) ns_closeclose(sockPtr->tfd);
3680 }
3681 sockPtr->tfd = 0;
3682
3683 } else if (sockPtr->tfd > 0) {
3684 /*
3685 * This must be a fd allocated via Ns_GetTemp();
3686 */
3687 Ns_ReleaseTemp(sockPtr->tfd);
3688 sockPtr->tfd = 0;
3689 }
3690
3691#ifndef _WIN32
3692 /*
3693 * Un-map temp file used for spooled content.
3694 */
3695 if (sockPtr->taddr != NULL((void*)0)) {
3696 munmap(sockPtr->taddr, (size_t)sockPtr->tsize);
3697 sockPtr->taddr = NULL((void*)0);
3698 }
3699#endif
3700}
3701
3702
3703/*
3704 *----------------------------------------------------------------------
3705 *
3706 * ChunkedDecode --
3707 *
3708 * Reads the content form the incoming request buffer and tries
3709 * to decode chunked encoding parts. The function can be called
3710 * repeatedly and with incomplete input and overwrites the buffer
3711 * with the decoded data optionally. The decoded data is always
3712 * shorter than the encoded one.
3713 *
3714 * Results:
3715 * SOCK_READY when chunk was complete, SOCK_MORE when more data is
3716 * required, or some error condition.
3717 *
3718 * Side effects:
3719 * Updates the buffer if update is true (and adjusts
3720 * reqPtr->chunkWriteOff). Updates always reqPtr->chunkStartOff to allow
3721 * incremental operations.
3722 *
3723 *----------------------------------------------------------------------
3724 */
3725static SockState
3726ChunkedDecode(Request *reqPtr, bool_Bool update)
3727{
3728 const Tcl_DString *bufPtr;
3729 const char *end, *chunkStart;
3730 SockState result = SOCK_MORE;
3731
3732 NS_NONNULL_ASSERT(reqPtr != NULL)((void) (0));
3733
3734 bufPtr = &reqPtr->buffer;
3735 end = bufPtr->string + bufPtr->length;
3736 chunkStart = bufPtr->string + reqPtr->chunkStartOff;
3737
3738 while (reqPtr->chunkStartOff < (size_t)bufPtr->length) {
3739 char *p = strstr(chunkStart, "\r\n"), *numberEnd;
3740 long chunkLength;
3741
3742 if (p == NULL((void*)0)) {
3743 Ns_Log(DriverDebug, "ChunkedDecode: chunk did not find end-of-line");
3744 result = SOCK_MORE;
3745 break;
3746 }
3747
3748 *p = '\0';
3749 chunkLength = strtol(chunkStart, &numberEnd, 16);
3750 Ns_Log(DriverDebug, "ChunkedDecode: chunkLength %ld, <%s>", chunkLength, chunkStart);
3751 *p = '\r';
3752 if (chunkLength < 0) {
3753 Ns_Log(Warning, "ChunkedDecode: negative chunk length");
3754 result = SOCK_BADREQUEST;
3755 break;
3756 }
3757 if (chunkStart == numberEnd) {
3758 Ns_Log(Warning, "ChunkedDecode: invalid chunk length");
3759 result = SOCK_BADREQUEST;
3760 break;
3761 }
3762 if (p + 2 + chunkLength > end) {
3763 Ns_Log(DriverDebug, "ChunkedDecode: chunk length past end of buffer");
3764 result = SOCK_MORE;
3765 break;
3766 }
3767 if (update) {
3768 char *writeBuffer = bufPtr->string + reqPtr->chunkWriteOff;
3769
3770 memmove(writeBuffer, p + 2, (size_t)chunkLength);
3771 reqPtr->chunkWriteOff += (size_t)chunkLength;
3772 *(writeBuffer + chunkLength) = '\0';
3773 }
3774 reqPtr->chunkStartOff += (size_t)(p - chunkStart) + 4u + (size_t)chunkLength;
3775 chunkStart = bufPtr->string + reqPtr->chunkStartOff;
3776 result = SOCK_READY;
3777 }
3778
3779 return result;
3780}
3781
3782
3783/*
3784 *----------------------------------------------------------------------
3785 *
3786 * SockRead --
3787 *
3788 * Read content from the given Sock, processing the input as
3789 * necessary. This is the core callback routine designed to
3790 * either be called repeatedly within the DriverThread during
3791 * an async read-ahead or in a blocking loop in NsGetRequest()
3792 * at the start of connection processing.
3793 *
3794 * Results:
3795 * SOCK_READY: Request is ready for processing.
3796 * SOCK_MORE: More input is required.
3797 * SOCK_ERROR: Client drop or timeout.
3798 * SOCK_SPOOL: Pass input handling to spooler
3799 * SOCK_CLOSE: peer closed connection
3800 * SOCK_BADREQUEST
3801 * SOCK_BADHEADER
3802 * SOCK_TOOMANYHEADERS
3803 *
3804 * Side effects:
3805 * The Request structure will be built up for use by the
3806 * connection thread. Also, before returning SOCK_READY,
3807 * the next byte to read mark and bytes available are set
3808 * to the beginning of the content, just beyond the headers.
3809 *
3810 * Contents may be spooled into temp file and mmap-ed
3811 *
3812 *----------------------------------------------------------------------
3813 */
3814
3815static SockState
3816SockRead(Sock *sockPtr, int spooler, const Ns_Time *timePtr)
3817{
3818 const Driver *drvPtr;
3819 Request *reqPtr;
3820 Tcl_DString *bufPtr;
3821 struct iovec buf;
3822 char tbuf[16384];
3823 size_t buflen, nread;
3824 ssize_t n;
3825 SockState resultState;
3826
3827 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
3828 drvPtr = sockPtr->drvPtr;
3829
3830 tbuf[0] = '\0';
3831
3832 /*
3833 * In case of "keepwait", the accept time is not meaningful and
3834 * reset to 0. In such cases, update "acceptTime" to the actual
3835 * begin of a request. This part is intended for async drivers.
3836 */
3837 if (sockPtr->acceptTime.sec == 0) {
3838 assert(timePtr != NULL)((void) (0));
3839 sockPtr->acceptTime = *timePtr;
3840 }
3841
3842 /*
3843 * Initialize request structure if needed.
3844 */
3845 if (sockPtr->reqPtr == NULL((void*)0)) {
3846 sockPtr->reqPtr = RequestNew();
3847 }
3848
3849 /*
3850 * On the first read, attempt to read-ahead "bufsize" bytes.
3851 * Otherwise, read only the number of bytes left in the
3852 * content.
3853 */
3854
3855 reqPtr = sockPtr->reqPtr;
3856 bufPtr = &reqPtr->buffer;
3857 if (reqPtr->length == 0u) {
3858 nread = drvPtr->bufsize;
3859 } else {
3860 nread = reqPtr->length - reqPtr->avail;
3861 }
3862
3863 /*
3864 * Grow the buffer to include space for the next bytes.
3865 */
3866
3867 buflen = (size_t)bufPtr->length;
3868 n = (ssize_t)(buflen + nread);
3869 if (unlikely(n > drvPtr->maxinput)(__builtin_expect((n > drvPtr->maxinput), 0))) {
3870 n = (ssize_t)drvPtr->maxinput;
3871 nread = (size_t)n - buflen;
3872 if (nread == 0u) {
3873 Ns_Log(DriverDebug, "SockRead: maxinput reached %" TCL_LL_MODIFIER"l" "d",
3874 drvPtr->maxinput);
3875 return SOCK_ERROR;
3876 }
3877 }
3878
3879 /*
3880 * Use temp file for content larger than "readahead" bytes.
3881 */
3882#ifndef _WIN32
3883 if (reqPtr->coff > 0u /* We are in the content part (after the header) */
3884 && !reqPtr->chunkStartOff /* Never spool chunked encoded data since we decode in memory */
3885 && reqPtr->length > (size_t)drvPtr->readahead /* We need more data */
3886 && sockPtr->tfd <= 0 /* We have no spool fd */
3887 ) {
3888 const DrvSpooler *spPtr = &drvPtr->spooler;
3889
3890 Ns_Log(DriverDebug, "SockRead: require temporary file for content spooling (length %" PRIuz"zu"" > readahead "
3891 "%" TCL_LL_MODIFIER"l" "d)",
3892 reqPtr->length, drvPtr->readahead);
3893
3894 /*
3895 * In driver mode send this Sock to the spooler thread if
3896 * it is running
3897 */
3898
3899 if (spooler == 0 && spPtr->threads > 0) {
3900 return SOCK_SPOOL;
3901 }
3902
3903 /*
3904 * If "maxupload" is specified and content size exceeds the configured
3905 * values, spool uploads into normal temp file (not deleted). We do
3906 * not want to map such large files into memory.
3907 */
3908 if (drvPtr->maxupload > 0
3909 && reqPtr->length > (size_t)drvPtr->maxupload
3910 ) {
3911 size_t tfileLength = strlen(drvPtr->uploadpath) + 16u;
3912
3913 sockPtr->tfile = ns_malloc(tfileLength);
3914 snprintf(sockPtr->tfile, tfileLength, "%s/%d.XXXXXX", drvPtr->uploadpath, sockPtr->sock)__builtin___snprintf_chk (sockPtr->tfile, tfileLength, 2 -
1, __builtin_object_size (sockPtr->tfile, 2 > 1), "%s/%d.XXXXXX"
, drvPtr->uploadpath, sockPtr->sock)
;
3915 sockPtr->tfd = ns_mkstempmkstemp(sockPtr->tfile);
3916 if (sockPtr->tfd == NS_INVALID_FD(-1)) {
3917 Ns_Log(Error, "SockRead: cannot create spool file with template '%s': %s",
3918 sockPtr->tfile, strerror(errno(*__errno_location ())));
3919 }
3920 } else {
3921 /*
3922 * Get a temporary fd. These FDs are used for mmapping.
3923 */
3924
3925 sockPtr->tfd = Ns_GetTemp();
3926 }
3927
3928 if (unlikely(sockPtr->tfd == NS_INVALID_FD)(__builtin_expect((sockPtr->tfd == (-1)), 0))) {
3929 Ns_Log(DriverDebug, "SockRead: spool fd invalid");
3930 return SOCK_ERROR;
3931 }
3932
3933 n = (ssize_t)((size_t)bufPtr->length - reqPtr->coff);
3934 assert(n >= 0)((void) (0));
3935 if (ns_writewrite(sockPtr->tfd, bufPtr->string + reqPtr->coff, (size_t)n) != n) {
3936 return SOCK_WRITEERROR;
3937 }
3938 Tcl_DStringSetLength(bufPtr, 0);
3939 }
3940#endif
3941 if (sockPtr->tfd > 0) {
3942 buf.iov_base = tbuf;
3943 buf.iov_len = MIN(nread, sizeof(tbuf))(((nread)<(sizeof(tbuf)))?(nread):(sizeof(tbuf)));
3944 } else {
3945 Tcl_DStringSetLength(bufPtr, (int)(buflen + nread));
3946 buf.iov_base = bufPtr->string + reqPtr->woff;
3947 buf.iov_len = nread;
3948 }
3949
3950 if (reqPtr->leftover > 0u) {
3951 /*
3952 * There is some leftover in the buffer, don't read but take the
3953 * leftover instead as input.
3954 */
3955 n = (ssize_t)reqPtr->leftover;
3956 reqPtr->leftover = 0u;
3957 buflen = 0u;
3958 Ns_Log(DriverDebug, "SockRead receive from leftover %" PRIdz"zd" " bytes", n);
3959 } else {
3960 /*
3961 * Receive actually some data from the driver.
3962 */
3963 n = NsDriverRecv(sockPtr, &buf, 1, NULL((void*)0));
3964 Ns_Log(DriverDebug, "SockRead receive from network %" PRIdz"zd" " bytes sockState %.2x",
3965 n, (int)sockPtr->recvSockState);
3966 }
3967
3968 {
3969 Ns_SockState nsSockState = sockPtr->recvSockState;
3970 /*
3971 * The nsSockState has one of the following values, when provided:
3972 *
3973 * NS_SOCK_READ, NS_SOCK_DONE, NS_SOCK_AGAIN, NS_SOCK_EXCEPTION,
3974 * NS_SOCK_TIMEOUT
3975 */
3976 switch (nsSockState) {
3977 case NS_SOCK_TIMEOUT: NS_FALL_THROUGH((void)0); /* fall through */
3978 case NS_SOCK_EXCEPTION:
3979 return SOCK_READERROR;
3980 case NS_SOCK_AGAIN:
3981 Tcl_DStringSetLength(bufPtr, (int)buflen);
3982 return SOCK_MORE;
3983 case NS_SOCK_DONE:
3984 return SOCK_CLOSE;
3985 case NS_SOCK_READ:
3986 break;
3987
3988 case NS_SOCK_CANCEL: NS_FALL_THROUGH((void)0); /* fall through */
3989 case NS_SOCK_EXIT: NS_FALL_THROUGH((void)0); /* fall through */
3990 case NS_SOCK_INIT: NS_FALL_THROUGH((void)0); /* fall through */
3991 case NS_SOCK_WRITE:
3992 Ns_Log(Warning, "SockRead received unexpected state %.2x from driver", nsSockState);
3993 return SOCK_READERROR;
3994
3995 case NS_SOCK_NONE:
3996 /*
3997 * Old style state management based on "n" and "errno", which is
3998 * more fragile. We keep there for old-style drivers.
3999 */
4000 if (n < 0) {
4001 Tcl_DStringSetLength(bufPtr, (int)buflen);
4002 /*
4003 * The driver returns -1 when the peer closed the connection, but
4004 * clears the errno such we can distinguish from error conditions.
4005 */
4006 if (errno(*__errno_location ()) == 0) {
4007 return SOCK_CLOSE;
4008 }
4009 return SOCK_READERROR;
4010 }
4011
4012 if (n == 0) {
4013 Tcl_DStringSetLength(bufPtr, (int)buflen);
4014 return SOCK_MORE;
4015 }
4016 break;
4017
4018 }
4019 }
4020
4021 if (sockPtr->tfd > 0) {
4022 if (ns_writewrite(sockPtr->tfd, tbuf, (size_t)n) != n) {
4023 return SOCK_WRITEERROR;
4024 }
4025 } else {
4026 Tcl_DStringSetLength(bufPtr, (int)(buflen + (size_t)n));
4027 }
4028
4029 reqPtr->woff += (size_t)n;
4030 reqPtr->avail += (size_t)n;
4031
4032 /*
4033 * This driver needs raw buffer, it is binary or non-HTTP request
4034 */
4035
4036 if ((drvPtr->opts & NS_DRIVER_NOPARSE0x04u) != 0u) {
4037 return SOCK_READY;
4038 }
4039
4040 resultState = SockParse(sockPtr);
4041
4042 return resultState;
4043}
4044
4045
4046/*----------------------------------------------------------------------
4047 *
4048 * LogBuffer --
4049 *
4050 * Debug function to output buffer content when the provided severity is
4051 * enabled. The function prints just visible characters and space as is
4052 * and prints the hex code otherwise.
4053 *
4054 * Results:
4055 * None.
4056 *
4057 * Side effects:
4058 * Writes to error.log
4059 *
4060 *----------------------------------------------------------------------
4061 */
4062static void
4063LogBuffer(Ns_LogSeverity severity, const char *msg, const char *buffer, size_t len)
4064{
4065 Tcl_DString ds;
4066
4067 NS_NONNULL_ASSERT(msg != NULL)((void) (0));
4068 NS_NONNULL_ASSERT(buffer != NULL)((void) (0));
4069
4070 if (Ns_LogSeverityEnabled(severity)) {
4071
4072 Tcl_DStringInit(&ds);
4073 Tcl_DStringAppend(&ds, msg, -1);
4074 Tcl_DStringAppend(&ds, ": ", 2);
4075 (void)Ns_DStringAppendPrintable(&ds, NS_FALSE0, buffer, len);
4076
4077 Ns_Log(severity, "%s", ds.string);
4078 Tcl_DStringFree(&ds);
4079 }
4080}
4081
4082
4083/*----------------------------------------------------------------------
4084 *
4085 * EndOfHeader --
4086 *
4087 * Function to be called (once), when end of header is reached. At this
4088 * time, all request header lines were parsed already correctly.
4089 *
4090 * Results:
4091 * None.
4092 *
4093 * Side effects:
4094 * Update various reqPtr fields and signal certain facts and error
4095 * conditions via sockPtr->flags. In error conditions, sockPtr->keep is
4096 * set to NS_FALSE.
4097 *
4098 *----------------------------------------------------------------------
4099 */
4100static size_t
4101EndOfHeader(Sock *sockPtr)
4102{
4103 Request *reqPtr;
4104 const char *s;
4105
4106 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
4107
4108 reqPtr = sockPtr->reqPtr;
4109 assert(reqPtr != NULL)((void) (0));
4110
4111 reqPtr->chunkStartOff = 0u;
4112
4113 /*
4114 * Check for "expect: 100-continue" and clear flag in case we have
4115 * pipelining.
4116 */
4117 sockPtr->flags &= ~(NS_CONN_CONTINUE0x40000u);
4118 s = Ns_SetIGet(reqPtr->headers, "expect");
4119 if (s != NULL((void*)0)) {
4120 if (*s == '1' && *(s+1) == '0' && *(s+2) == '0' && *(s+3) == '-') {
4121 char *dup = ns_strdup(s+4);
4122
4123 Ns_StrToLower(dup);
4124 if (STREQ(dup, "continue")(((*(dup)) == (*("continue"))) && (strcmp((dup),("continue"
)) == 0))
) {
4125 sockPtr->flags |= NS_CONN_CONTINUE0x40000u;
4126 }
4127 ns_free(dup);
4128 }
4129 }
4130
4131 /*
4132 * Handle content-length, which might be provided or not.
4133 * Clear length specific error flags.
4134 */
4135 sockPtr->flags &= ~(NS_CONN_ENTITYTOOLARGE0x0100000u);
4136 s = Ns_SetIGet(reqPtr->headers, "content-length");
4137 if (s == NULL((void*)0)) {
4138 s = Ns_SetIGet(reqPtr->headers, "Transfer-Encoding");
4139
4140 if (s != NULL((void*)0)) {
4141 /* Lower case is in the standard, capitalized by macOS */
4142 if (STREQ(s, "chunked")(((*(s)) == (*("chunked"))) && (strcmp((s),("chunked"
)) == 0))
|| STREQ(s, "Chunked")(((*(s)) == (*("Chunked"))) && (strcmp((s),("Chunked"
)) == 0))
) {
4143 Tcl_WideInt expected;
4144
4145 reqPtr->chunkStartOff = reqPtr->roff;
4146 reqPtr->chunkWriteOff = reqPtr->chunkStartOff;
4147 reqPtr->contentLength = 0u;
4148
4149 /*
4150 * We need reqPtr->expectedLength for safely terminating read loop.
4151 */
4152 s = Ns_SetIGet(reqPtr->headers, "X-Expected-Entity-Length");
4153
4154 if ((s != NULL((void*)0))
4155 && (Ns_StrToWideInt(s, &expected) == NS_OK)
4156 && (expected > 0) ) {
4157 reqPtr->expectedLength = (size_t)expected;
4158 }
4159 s = NULL((void*)0);
4160 }
4161 }
4162 }
4163
4164 /*
4165 * In case a valid and meaningful headers determining the content length
4166 * was provided, the string with the content length ("s") is not NULL.
4167 */
4168 if (s != NULL((void*)0)) {
4169 Tcl_WideInt length;
4170
4171 if ((Ns_StrToWideInt(s, &length) == NS_OK) && (length > 0)) {
4172 reqPtr->length = (size_t)length;
4173 /*
4174 * Handle too large input requests.
4175 */
4176 if (reqPtr->length > (size_t)sockPtr->drvPtr->maxinput) {
4177
4178 Ns_Log(Warning, "SockParse: request too large, length=%"
4179 PRIdz"zd" ", maxinput=%" TCL_LL_MODIFIER"l" "d",
4180 reqPtr->length, sockPtr->drvPtr->maxinput);
4181
4182 sockPtr->keep = NS_FALSE0;
4183 sockPtr->flags |= NS_CONN_ENTITYTOOLARGE0x0100000u;
4184
4185 }
4186 reqPtr->contentLength = (size_t)length;
4187 }
4188 }
4189
4190 /*
4191 * Compression format handling: parse information from request headers
4192 * indicating allowed compression formats for quick access.
4193 *
4194 * Clear compression accepted flag
4195 */
4196 sockPtr->flags &= ~(NS_CONN_ZIPACCEPTED0x10000u|NS_CONN_BROTLIACCEPTED0x20000u);
4197
4198 s = Ns_SetIGet(reqPtr->headers, "Accept-Encoding");
4199 if (s != NULL((void*)0)) {
4200 bool_Bool gzipAccept, brotliAccept;
4201
4202 /*
4203 * Get allowed compression formats from "accept-encoding" headers.
4204 */
4205 NsParseAcceptEncoding(reqPtr->request.version, s, &gzipAccept, &brotliAccept);
4206 if (gzipAccept || brotliAccept) {
4207 /*
4208 * Don't allow compression formats for Range requests.
4209 */
4210 s = Ns_SetIGet(reqPtr->headers, "Range");
4211 if (s == NULL((void*)0)) {
4212 if (gzipAccept) {
4213 sockPtr->flags |= NS_CONN_ZIPACCEPTED0x10000u;
4214 }
4215 if (brotliAccept) {
4216 sockPtr->flags |= NS_CONN_BROTLIACCEPTED0x20000u;
4217 }
4218 }
4219 }
4220 }
4221
4222 /*
4223 * Determine the peer address for clients coming via reverse proxy
4224 * servers.
4225 */
4226 s = Ns_SetIGet(reqPtr->headers, "X-Forwarded-For");
4227 if (s != NULL((void*)0) && !strcasecmp(s, "unknown")) {
4228 s = NULL((void*)0);
4229 }
4230 if (s != NULL((void*)0)) {
4231 int success = ns_inet_pton((struct sockaddr *)&sockPtr->clientsa, s);
4232
4233 if (success <= 0) {
4234 char *comma = strchr(s, INTCHAR(',')((int)((unsigned char)((',')))));
4235 /*
4236 * When multiple IP addresses are provided, the first one is
4237 * the one of the client.
4238 */
4239 if (comma != NULL((void*)0)) {
4240 *comma = '\0';
4241 success = ns_inet_pton((struct sockaddr *)&sockPtr->clientsa, s);
4242 *comma = ',';
4243 }
4244 if (success <= 0) {
4245 Ns_Log(Warning, "invalid content in X-Forwarded-For header: '%s'", s);
4246 }
4247 }
4248 } else {
4249 memset(&sockPtr->clientsa, 0, sizeof(struct NS_SOCKADDR_STORAGEsockaddr_storage));
4250 }
4251
4252 /*
4253 * Set up request length for spooling and further read operations
4254 */
4255 if (reqPtr->contentLength != 0u) {
4256 /*
4257 * Content-Length was provided, use it
4258 */
4259 reqPtr->length = reqPtr->contentLength;
4260 }
4261
4262 return reqPtr->roff;
4263}
4264
4265
4266/*----------------------------------------------------------------------
4267 *
4268 * SockParse --
4269 *
4270 * Construct the given conn by parsing input buffer until end of
4271 * headers. Return SOCK_READY when finished parsing.
4272 *
4273 * Results:
4274 * SOCK_READY: Conn is ready for processing.
4275 * SOCK_MORE: More input is required.
4276 * SOCK_ERROR: Malformed request.
4277 * SOCK_BADREQUEST
4278 * SOCK_BADHEADER
4279 * SOCK_TOOMANYHEADERS
4280 *
4281 * Side effects:
4282 * An Ns_Request and/or Ns_Set may be allocated.
4283 * Ns_Conn buffer management offsets updated.
4284 *
4285 *----------------------------------------------------------------------
4286 */
4287
4288static SockState
4289SockParse(Sock *sockPtr)
4290{
4291 const Tcl_DString *bufPtr;
4292 const Driver *drvPtr;
4293 Request *reqPtr;
4294 char save;
4295 SockState result;
4296
4297 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
4298 drvPtr = sockPtr->drvPtr;
4299
4300 NsUpdateProgress((Ns_Sock *) sockPtr);
4301
4302 reqPtr = sockPtr->reqPtr;
4303 bufPtr = &reqPtr->buffer;
4304
4305 /*
4306 * Scan lines (header) until start of content (body-part)
4307 */
4308
4309 while (reqPtr->coff == 0u) {
4310 char *s, *e;
4311 size_t cnt;
4312
4313 /*
4314 * Find the next header line.
4315 */
4316 s = bufPtr->string + reqPtr->roff;
4317 e = memchr(s, INTCHAR('\n')((int)((unsigned char)(('\n')))), reqPtr->avail);
4318
4319 if (unlikely(e == NULL)(__builtin_expect((e == ((void*)0)), 0))) {
4320 /*
4321 * Input not yet newline terminated - request more data.
4322 */
4323 return SOCK_MORE;
4324 }
4325
4326 /*
4327 * Check for max single line overflows.
4328 *
4329 * Previous versions if the driver returned here directly an
4330 * error code, which was handled via HTTP error message
4331 * provided via SockError(). However, the SockError() handling
4332 * closes the connection immediately. This has the
4333 * consequence, that the HTTP client might never see the error
4334 * message, since the request was not yet fully transmitted,
4335 * but it will see a "broken pipe: 13" message instead. We
4336 * read now the full request and return the message via
4337 * ConnRunRequest().
4338 */
4339
4340 if (unlikely((e - s) > drvPtr->maxline)(__builtin_expect(((e - s) > drvPtr->maxline), 0))) {
4341 sockPtr->keep = NS_FALSE0;
4342 if (reqPtr->request.line == NULL((void*)0)) {
4343 Ns_Log(DriverDebug, "SockParse: maxline reached of %d bytes",
4344 drvPtr->maxline);
4345 sockPtr->flags = NS_CONN_REQUESTURITOOLONG0x0200000u;
4346 Ns_Log(Warning, "request line is too long (%d bytes)", (int)(e - s));
4347 } else {
4348 sockPtr->flags = NS_CONN_LINETOOLONG0x0400000u;
4349 Ns_Log(Warning, "request header line is too long (%d bytes)", (int)(e - s));
4350 }
4351 }
4352
4353 /*
4354 * Update next read pointer to end of this line.
4355 */
4356 cnt = (size_t)(e - s) + 1u;
4357 reqPtr->roff += cnt;
4358 reqPtr->avail -= cnt;
4359
4360 /*
4361 * Adjust end pointer to the last content character before the line
4362 * terminator.
4363 */
4364 if (likely(e > s)(__builtin_expect((e > s), 1)) && likely(*(e-1) == '\r')(__builtin_expect((*(e-1) == '\r'), 1))) {
4365 --e;
4366 }
4367
4368 /*
4369 * Check for end of headers in case we have not done it yet.
4370 */
4371 if (unlikely(e == s)(__builtin_expect((e == s), 0)) && (reqPtr->coff == 0u)) {
4372 /*
4373 * We are at end of headers.
4374 */
4375 reqPtr->coff = EndOfHeader(sockPtr);
4376
4377 /*
4378 * In cases the client sent "expect: 100-continue", report back that
4379 * everything is fine with the headers.
4380 */
4381 if ((sockPtr->flags & NS_CONN_CONTINUE0x40000u) != 0u) {
4382
4383 Ns_Log(Ns_LogRequestDebug, "honoring 100-continue");
4384
4385 /*
4386 * In case, the request entity (body) was too large, we can
4387 * return immediately the error message, when the client has
4388 * flagged this via "Expect:". Otherwise we have to read the
4389 * full request (although it is too large) to drain the
4390 * channel. Otherwise, the server might close the connection
4391 * *before* it has received full request with its body from
4392 * the client. We just keep the flag and let
4393 * Ns_ConnRunRequest() handle the error message.
4394 */
4395 if ((sockPtr->flags & NS_CONN_ENTITYTOOLARGE0x0100000u) != 0u) {
4396 Ns_Log(Ns_LogRequestDebug, "100-continue: entity too large");
4397
4398 return SOCK_ENTITYTOOLARGE;
4399
4400 /*
4401 * We have no other error message flagged (future ones
4402 * have to be handled here).
4403 */
4404 } else {
4405 struct iovec iov[1];
4406 ssize_t sent;
4407
4408 /*
4409 * Reply with "100 continue".
4410 */
4411 Ns_Log(Ns_LogRequestDebug, "100-continue: reply CONTINUE");
4412
4413 iov[0].iov_base = (char *)"HTTP/1.1 100 Continue\r\n\r\n";
4414 iov[0].iov_len = strlen(iov[0].iov_base);
4415
4416 sent = Ns_SockSendBufs((Ns_Sock *)sockPtr, iov, 1,
4417 NULL((void*)0), 0u);
4418 if (sent != (ssize_t)iov[0].iov_len) {
4419 Ns_Log(Warning, "could not deliver response: 100 Continue");
4420 /*
4421 * Should we bail out here?
4422 */
4423 }
4424 }
4425 }
4426 } else {
4427 /*
4428 * We have the request-line or a header line to process.
4429 */
4430 save = *e;
4431 *e = '\0';
4432
4433 if (unlikely(reqPtr->request.line == NULL)(__builtin_expect((reqPtr->request.line == ((void*)0)), 0)
)
) {
4434 /*
4435 * There is no request-line set. The received line must the
4436 * the request-line.
4437 */
4438 Ns_Log(DriverDebug, "SockParse (%d): parse request line <%s>", sockPtr->sock, s);
4439
4440 if (Ns_ParseRequest(&reqPtr->request, s, (size_t)(e-s)) == NS_ERROR) {
4441 /*
4442 * Invalid request.
4443 */
4444 return SOCK_BADREQUEST;
4445 }
4446
4447 /*
4448 * HTTP 0.9 did not have an HTTP-version number or request headers
4449 * and no empty line terminating the request header.
4450 */
4451 if (unlikely(reqPtr->request.version < 1.0)(__builtin_expect((reqPtr->request.version < 1.0), 0))) {
4452 /*
4453 * Pre-HTTP/1.0 request.
4454 */
4455 reqPtr->coff = reqPtr->roff;
4456 Ns_Log(Notice, "pre-HTTP/1.0 request <%s>", reqPtr->request.line);
4457 }
4458
4459 } else if (Ns_ParseHeader(reqPtr->headers, s, NULL((void*)0), Preserve, NULL((void*)0)) != NS_OK) {
4460 /*
4461 * Invalid header.
4462 */
4463 return SOCK_BADHEADER;
4464
4465 } else {
4466 /*
4467 * Check for max number of headers
4468 */
4469 if (unlikely(Ns_SetSize(reqPtr->headers) > (size_t)drvPtr->maxheaders)(__builtin_expect((((reqPtr->headers)->size) > (size_t
)drvPtr->maxheaders), 0))
) {
4470 Ns_Log(DriverDebug, "SockParse (%d): maxheaders reached of %d bytes",
4471 sockPtr->sock, drvPtr->maxheaders);
4472 return SOCK_TOOMANYHEADERS;
4473 }
4474 }
4475
4476 *e = save;
4477 }
4478 }
4479
4480 if (unlikely(reqPtr->request.line == NULL)(__builtin_expect((reqPtr->request.line == ((void*)0)), 0)
)
) {
4481 /*
4482 * We are at end of headers, but we have not parsed a request line
4483 * (maybe just two linefeeds).
4484 */
4485 return SOCK_BADREQUEST;
4486 }
4487
4488
4489 /*
4490 * We are in the request body.
4491 */
4492 assert(reqPtr->coff > 0u)((void) (0));
4493 assert(reqPtr->request.line != NULL)((void) (0));
4494
4495 /*
4496 * Check if all content has arrived.
4497 */
4498 Ns_Log(Debug, "=== length < avail (length %" PRIuz"zu"
4499 ", avail %" PRIuz"zu" ") tfd %d tfile %p chunkStartOff %" PRIuz"zu",
4500 reqPtr->length, reqPtr->avail, sockPtr->tfd,
4501 (void *)sockPtr->tfile, reqPtr->chunkStartOff);
4502
4503 if (reqPtr->chunkStartOff != 0u) {
4504 /*
4505 * Chunked encoding was provided.
4506 */
4507 SockState chunkState;
4508 size_t currentContentLength;
4509
4510 chunkState = ChunkedDecode(reqPtr, NS_TRUE1);
4511 currentContentLength = reqPtr->chunkWriteOff - reqPtr->coff;
4512
4513 /*
4514 * A chunk might be complete, but it might not be the last
4515 * chunk from the client. The best thing would be to be able
4516 * to read until EOF here. In cases, where the (optional)
4517 * "expectedLength" was provided by the client, we terminate
4518 * depending on that information
4519 */
4520 if ((chunkState == SOCK_MORE)
4521 || (reqPtr->expectedLength != 0u && currentContentLength < reqPtr->expectedLength)) {
4522 /*
4523 * ChunkedDecode wants more data.
4524 */
4525 return SOCK_MORE;
4526
4527 } else if (chunkState != SOCK_READY) {
4528 return chunkState;
4529 }
4530 /*
4531 * ChunkedDecode has enough data.
4532 */
4533 reqPtr->length = (size_t)currentContentLength;
4534 }
4535
4536 if (reqPtr->avail < reqPtr->length) {
4537 Ns_Log(DriverDebug, "SockRead wait for more input");
4538 /*
4539 * Wait for more input.
4540 */
4541 return SOCK_MORE;
4542 }
4543
4544 Ns_Log(Dev, "=== all required data is available (avail %" PRIuz"zu"", length %" PRIuz"zu" ", "
4545 "readahead %" TCL_LL_MODIFIER"l" "d maxupload %" TCL_LL_MODIFIER"l" "d) tfd %d",
4546 reqPtr->avail, reqPtr->length, drvPtr->readahead, drvPtr->maxupload,
4547 sockPtr->tfd);
4548
4549 /*
4550 * We have all required data in the receive buffer or in a temporary file.
4551 *
4552 * - Uploads > "readahead": these are put into temporary files.
4553 *
4554 * - Uploads > "maxupload": these are put into temporary files
4555 * without mmapping, no content parsing will be performed in memory.
4556 */
4557 result = SOCK_READY;
4558
4559 if (sockPtr->tfile != NULL((void*)0)) {
4560 reqPtr->content = NULL((void*)0);
4561 reqPtr->next = NULL((void*)0);
4562 reqPtr->avail = 0u;
4563 Ns_Log(DriverDebug, "content spooled to file: size %" PRIdz"zd" ", file %s",
4564 reqPtr->length, sockPtr->tfile);
4565 /*
4566 * Nothing more to do, return via SOCK_READY;
4567 */
4568 } else {
4569
4570 /*
4571 * Uploads < "maxupload" are spooled to files and mmapped in order to
4572 * provide the usual interface via [ns_conn content].
4573 */
4574 if (sockPtr->tfd > 0) {
4575#ifdef _WIN32
4576 /*
4577 * For _WIN32, tfd should never be set, since tfd-spooling is not
4578 * implemented for windows.
4579 */
4580 assert(0)((void) (0));
4581#else
4582 int prot = PROT_READ0x1 | PROT_WRITE0x2;
4583 /*
4584 * Add a byte to make sure, the string termination with \0 below falls
4585 * always into the mmapped area. On some older OSes this might lead to
4586 * crashes when we hitting page boundaries.
4587 */
4588 ssize_t rc = ns_writewrite(sockPtr->tfd, "\0", 1);
4589 if (rc == -1) {
4590 Ns_Log(Error, "socket: could not append terminating 0-byte");
4591 }
4592 sockPtr->tsize = reqPtr->length + 1;
4593 sockPtr->taddr = mmap(0, sockPtr->tsize, prot, MAP_PRIVATE0x02,
4594 sockPtr->tfd, 0);
4595 if (sockPtr->taddr == MAP_FAILED((void *) -1)) {
4596 sockPtr->taddr = NULL((void*)0);
4597 result = SOCK_ERROR;
4598
4599 } else {
4600 reqPtr->content = sockPtr->taddr;
4601 Ns_Log(Debug, "content spooled to mmapped file: readahead=%"
4602 TCL_LL_MODIFIER"l" "d, filesize=%" PRIdz"zd",
4603 drvPtr->readahead, sockPtr->tsize);
4604 }
4605#endif
4606 } else {
4607 /*
4608 * Set the content the begin of the remaining buffer (content offset).
4609 * This happens as well when reqPtr->contentLength is 0, but it is
4610 * needed for chunked input processing.
4611 */
4612 reqPtr->content = bufPtr->string + reqPtr->coff;
4613 }
4614 reqPtr->next = reqPtr->content;
4615
4616 /*
4617 * Add a terminating null character. The content might be from the receive
4618 * buffer (Tcl_DString) or from the mmapped file. Non-mmapped files are handled
4619 * above.
4620 */
4621 if (reqPtr->length > 0u) {
4622 Ns_Log(DriverDebug, "SockRead adds null terminating character at content[%" PRIuz"zu" "]", reqPtr->length);
4623
4624 reqPtr->savedChar = reqPtr->content[reqPtr->length];
4625 reqPtr->content[reqPtr->length] = '\0';
4626 if (sockPtr->taddr == NULL((void*)0)) {
4627 LogBuffer(DriverDebug, "UPDATED BUFFER", sockPtr->reqPtr->buffer.string, (size_t)reqPtr->buffer.length);
4628 }
4629 }
4630 }
4631
4632 return result;
4633}
4634
4635/*
4636 *----------------------------------------------------------------------
4637 *
4638 * DriverLookupHost --
4639 *
4640 * Lookup the specified hostname in the virtual hosts mapping table.
4641 *
4642 * Results:
4643 * ServerMap entry or NULL.
4644 *
4645 * Side effects:
4646 *
4647 * None.
4648 *
4649 *----------------------------------------------------------------------
4650 */
4651static const ServerMap *
4652DriverLookupHost(Tcl_DString *hostDs, Driver *drvPtr)
4653{
4654 const ServerMap *result = NULL((void*)0);
4655 const Tcl_HashEntry *hPtr;
4656 char lastChar;
4657
4658 NS_NONNULL_ASSERT(hostDs != NULL)((void) (0));
4659 NS_NONNULL_ASSERT(drvPtr != NULL)((void) (0));
4660
4661 /*
4662 * Remove trailing dot of host header field, since RFC 2976 allows fully
4663 * qualified "absolute" DNS names in host fields (see e.g. §3.2.2).
4664 *
4665 */
4666 lastChar = hostDs->string[hostDs->length - 1];
4667
4668 if (lastChar == '.') {
4669 hostDs->string[hostDs->length - 1] = '\0';
4670 } else if (CHARTYPE(digit, lastChar)(((*__ctype_b_loc ())[(int) (((int)((unsigned char)(lastChar)
)))] & (unsigned short int) _ISdigit))
!= 0) {
4671 /*
4672 * Note: in case, the host header contains a port, we are checking
4673 * with the test above the last character of the port, but the
4674 * trailing dot might be with the hostname before the port separator
4675 * characgter (dot).
4676 */
4677 char *colon = strrchr(hostDs->string, INTCHAR(':')((int)((unsigned char)((':')))));
4678
4679 if (colon != NULL((void*)0)) {
4680 /*
4681 * We found colon, which might be the port separator or inside an
4682 * IPv6 address. However, we look here for a dot before the colon,
4683 * which would not be permitted for IPv6 addresses.
4684 */
4685 lastChar = *(colon - 1);
4686 if (lastChar == '.') {
4687 memmove(colon-1, colon,
4688 (1u + (size_t)hostDs->length) -
4689 (size_t)(colon - hostDs->string));
4690 }
4691 }
4692 }
4693
4694 /*
4695 * Convert provided host header field to lowercase before hash lookup.
4696 */
4697 Ns_StrToLower(hostDs->string);
4698
4699 hPtr = Tcl_FindHashEntry(&drvPtr->hosts, hostDs->string)(*((&drvPtr->hosts)->findProc))(&drvPtr->hosts
, (const char *)(hostDs->string))
;
4700 Ns_Log(DriverDebug, "SockSetServer driver '%s' host '%s' => %p",
4701 drvPtr->moduleName, hostDs->string, (void*)hPtr);
4702
4703 if (hPtr != NULL((void*)0)) {
4704 /*
4705 * Request with provided host header field could be resolved against a
4706 * certain server.
4707 */
4708 result = Tcl_GetHashValue(hPtr)((hPtr)->clientData);
4709
4710 } else {
4711 /*
4712 * Host header field content is not found in the mapping table.
4713 */
4714 Ns_Log(DriverDebug,
4715 "cannot locate host header content '%s' in virtual hosts "
4716 "table of driver '%s', fall back to default "
4717 "(default mapping or driver data)",
4718 hostDs->string, drvPtr->moduleName);
4719
4720 if (Ns_LogSeverityEnabled(DriverDebug)) {
4721 Tcl_HashEntry *hPtr2;
4722 Tcl_HashSearch search;
4723
4724 hPtr2 = Tcl_FirstHashEntry(&drvPtr->hosts, &search);
4725 while (hPtr2 != NULL((void*)0)) {
4726 Ns_Log(Notice, "... host entry: '%s'",
4727 (char *)Tcl_GetHashKey(&drvPtr->hosts, hPtr2)((void *) (((&drvPtr->hosts)->keyType == (1) || (&
drvPtr->hosts)->keyType == (-1)) ? (hPtr2)->key.oneWordValue
: (hPtr2)->key.string))
);
4728 hPtr2 = Tcl_NextHashEntry(&search);
4729 }
4730 }
4731 }
4732 return result;
4733}
4734
4735NS_TLS_SSL_CTXSSL_CTX *
4736NsDriverLookupHostCtx(Tcl_DString *hostDs, const Ns_Driver *drvPtr)
4737{
4738 const ServerMap *mapPtr;
4739
4740 NS_NONNULL_ASSERT(hostDs != NULL)((void) (0));
4741 NS_NONNULL_ASSERT(drvPtr != NULL)((void) (0));
4742
4743 mapPtr = DriverLookupHost(hostDs, (Driver *)drvPtr);
4744 return (mapPtr != NULL((void*)0)) ? mapPtr->ctx : NULL((void*)0);
4745}
4746
4747/*
4748 *----------------------------------------------------------------------
4749 *
4750 * SockSetServer --
4751 *
4752 * Set virtual server from driver context or Host header.
4753 *
4754 * Results:
4755 * void.
4756 *
4757 * Side effects:
4758 *
4759 * Updates sockPtr->servPtr. In case an invalid server set, or the
4760 * required host field in HTTP/1.1 is missing the HTTP-method is set to
4761 * the constant "BAD".
4762 *
4763 *----------------------------------------------------------------------
4764 */
4765
4766static void
4767SockSetServer(Sock *sockPtr)
4768{
4769 const char *host;
4770 Request *reqPtr;
4771 bool_Bool bad_request = NS_FALSE0;
4772 Driver *drvPtr;
4773 const ServerMap *mapPtr = NULL((void*)0);
4774
4775 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
4776
4777 reqPtr = sockPtr->reqPtr;
4778 assert(reqPtr != NULL)((void) (0));
4779
4780 drvPtr = sockPtr->drvPtr;
4781 assert(drvPtr != NULL)((void) (0));
4782
4783 sockPtr->servPtr = drvPtr->servPtr;
4784 sockPtr->location = NULL((void*)0);
4785
4786 host = Ns_SetIGet(reqPtr->headers, "Host");
4787 Ns_Log(DriverDebug, "SockSetServer host '%s' request line '%s' servPtr %p",
4788 host, reqPtr->request.line, (void*)sockPtr->servPtr);
4789
4790 if (unlikely((host == NULL) && (reqPtr->request.version >= 1.1))(__builtin_expect(((host == ((void*)0)) && (reqPtr->
request.version >= 1.1)), 0))
) {
4791 /*
4792 * HTTP/1.1 requires host header
4793 */
4794 Ns_Log(Notice, "request header field \"Host\" is missing in HTTP/1.1 request: \"%s\"\n",
4795 reqPtr->request.line);
4796 bad_request = NS_TRUE1;
4797 }
4798
4799 if (host != NULL((void*)0)) {
4800 Tcl_DString hostDs;
4801
4802 Tcl_DStringInit(&hostDs);
4803 Tcl_DStringAppend(&hostDs, host, -1);
4804 mapPtr = DriverLookupHost(&hostDs, drvPtr);
4805 Tcl_DStringFree(&hostDs);
4806 }
4807
4808 if (mapPtr == NULL((void*)0) && sockPtr->servPtr == NULL((void*)0)) {
4809 /*
4810 * The driver is installed globally, fall back to the default server,
4811 * which has to be defined in this case.
4812 */
4813 mapPtr = drvPtr->defMapPtr;
4814 }
4815
4816 if (mapPtr != NULL((void*)0)) {
4817 /*
4818 * Get server and location from the configured mapping.
4819 */
4820 if (sockPtr->servPtr == NULL((void*)0)) {
4821 sockPtr->servPtr = mapPtr->servPtr;
4822 }
4823 sockPtr->location = mapPtr->location;
4824 Ns_Log(DriverDebug, "SockSetServer request line '%s' get location from mapping '%s'",
4825 reqPtr->request.line, sockPtr->location);
4826 } else {
4827 /*
4828 * There is no configured mapping.
4829 */
4830 if (sockPtr->servPtr == NULL((void*)0)) {
4831 Ns_Log(Warning, "cannot determine server for request: \"%s\" (host \"%s\")\n",
4832 reqPtr->request.line, host);
4833 bad_request = NS_TRUE1;
4834 }
4835 /*
4836 * Could not lookup the virtual host, get the default location from
4837 * the driver or from local side of the socket connection.
4838 */
4839 if (drvPtr->location != NULL((void*)0)) {
4840 sockPtr->location = drvPtr->location;
4841 } else {
4842 static NS_THREAD_LOCAL__thread Tcl_DString locationDs;
4843 static NS_THREAD_LOCAL__thread bool_Bool initialized;
4844
4845 if (!initialized) {
4846 Tcl_DStringInit(&locationDs);
4847 initialized = NS_TRUE1;
4848 }
4849 Tcl_DStringSetLength(&locationDs, 0);
4850
4851 Ns_HttpLocationString(&locationDs,
4852 drvPtr->protocol,
4853 Ns_SockGetAddr((Ns_Sock *)sockPtr),
4854 Ns_SockGetPort((Ns_Sock *)sockPtr),
4855 drvPtr->defport);
4856 sockPtr->location = locationDs.string;
4857 }
4858
4859 Ns_Log(DriverDebug, "SockSetServer request line '%s' get location from driver '%s'",
4860 reqPtr->request.line, sockPtr->location);
4861 }
4862
4863 /*
4864 * Since the URLencoding can be set per server, we need the server
4865 * assignment to check the validity of the request URL.
4866 *
4867 * In some error conditions (e.g. from nssmtpd) the reqPtr->request.*
4868 * members might be NULL.
4869 */
4870 if (likely(sockPtr->servPtr != NULL)(__builtin_expect((sockPtr->servPtr != ((void*)0)), 1))
4871 && NsEncodingIsUtf8(sockPtr->servPtr->encoding.urlEncoding)
4872 && reqPtr->request.url != NULL((void*)0)
4873 ) {
4874 if (!Ns_Valid_UTF8((const unsigned char *)reqPtr->request.url,
4875 strlen(reqPtr->request.url), NULL((void*)0))) {
4876 Ns_Log(Warning, "Invalid UTF-8 encoding in url '%s'",
4877 reqPtr->request.url);
4878 bad_request = NS_TRUE1;
4879 }
4880 }
4881
4882 if (unlikely(bad_request)(__builtin_expect((bad_request), 0))) {
4883 Ns_Log(DriverDebug, "SockSetServer sets method to BAD");
4884 ns_free((char *)reqPtr->request.method);
4885 reqPtr->request.method = ns_strdup("BAD");
4886 }
4887
4888 Ns_Log(DriverDebug, "SockSetServer host '%s' request line '%s' final location '%s'",
4889 host, reqPtr->request.line, sockPtr->location);
4890}
4891
4892/*
4893 *======================================================================
4894 * Spooler Thread: Receive asynchronously from the client socket
4895 *======================================================================
4896 */
4897
4898/*
4899 *----------------------------------------------------------------------
4900 *
4901 * SpoolerThread --
4902 *
4903 * Spooling socket driver thread.
4904 *
4905 * Results:
4906 * None.
4907 *
4908 * Side effects:
4909 * Connections are accepted on the configured listen sockets,
4910 * placed on the run queue to be serviced, and gracefully
4911 * closed when done. Async sockets have the entire request read
4912 * here before queuing as well.
4913 *
4914 *----------------------------------------------------------------------
4915 */
4916
4917static void
4918SpoolerThread(void *arg)
4919{
4920 SpoolerQueue *queuePtr = (SpoolerQueue*)arg;
4921 char charBuffer[1];
4922 int pollTimeout;
4923 bool_Bool stopping = NS_FALSE0;
4924 Sock *sockPtr, *nextPtr, *waitPtr = NULL((void*)0), *readPtr = NULL((void*)0);
4925 Ns_Time now, diff;
4926 const Driver *drvPtr;
4927 PollData pdata;
4928
4929 Ns_ThreadSetName("-spooler%d-", queuePtr->id);
4930 queuePtr->threadName = Ns_ThreadGetName();
4931
4932 /*
4933 * Loop forever until signaled to shut down and all
4934 * connections are complete and gracefully closed.
4935 */
4936
4937 Ns_Log(Notice, "spooler%d: accepting connections", queuePtr->id);
4938
4939 PollCreate(&pdata);
4940 Ns_GetTime(&now);
4941
4942 while (!stopping) {
4943
4944 /*
4945 * If there are any read sockets, set the bits
4946 * and determine the minimum relative timeout.
4947 */
4948
4949 PollReset(&pdata);
4950 (void)PollSet(&pdata, queuePtr->pipe[0], (short)POLLIN0x001, NULL((void*)0));
4951
4952 if (readPtr == NULL((void*)0)) {
4953 pollTimeout = 30 * 1000;
4954 } else {
4955 sockPtr = readPtr;
4956 while (sockPtr != NULL((void*)0)) {
4957 SockPoll(sockPtr, (short)POLLIN0x001, &pdata);
4958 sockPtr = sockPtr->nextPtr;
4959 }
4960 pollTimeout = -1;
4961 }
4962
4963 /*
4964 * Select and drain the trigger pipe if necessary.
4965 */
4966
4967 /*n =*/ (void) PollWait(&pdata, pollTimeout);
4968
4969 if (PollIn(&pdata, 0)(((&pdata)->pfds[(0)].revents & 0x001) == 0x001 ) && unlikely(ns_recv(queuePtr->pipe[0], charBuffer, 1u, 0) != 1)(__builtin_expect((recv(queuePtr->pipe[0], charBuffer, 1u,
0) != 1), 0))
) {
4970 Ns_Fatal("spooler: trigger ns_recv() failed: %s",
4971 ns_sockstrerrorstrerror(ns_sockerrno(*__errno_location ())));
4972 }
4973
4974 /*
4975 * Attempt read-ahead of any new connections.
4976 */
4977
4978 Ns_GetTime(&now);
4979 sockPtr = readPtr;
4980 readPtr = NULL((void*)0);
4981
4982 while (sockPtr != NULL((void*)0)) {
4983 nextPtr = sockPtr->nextPtr;
4984 drvPtr = sockPtr->drvPtr;
4985 if (unlikely(PollHup(&pdata, sockPtr->pidx))(__builtin_expect(((((&pdata)->pfds[(sockPtr->pidx)
].revents & 0x010) == 0x010)), 0))
) {
4986 /*
4987 * Peer has closed the connection
4988 */
4989 SockRelease(sockPtr, SOCK_CLOSE, 0);
4990
4991 } else if (!PollIn(&pdata, sockPtr->pidx)(((&pdata)->pfds[(sockPtr->pidx)].revents & 0x001
) == 0x001 )
) {
4992 /*
4993 * Got no data
4994 */
4995 if (Ns_DiffTime(&sockPtr->timeout, &now, &diff) <= 0) {
4996 SockRelease(sockPtr, SOCK_READTIMEOUT, 0);
4997 queuePtr->queuesize--;
4998 } else {
4999 Push(sockPtr, readPtr)((sockPtr)->nextPtr = (readPtr), (readPtr) = (sockPtr));
5000 }
5001 } else {
5002 /*
5003 * Got some data
5004 */
5005 SockState n = SockRead(sockPtr, 1, &now);
5006 switch (n) {
5007 case SOCK_MORE:
5008 SockTimeout(sockPtr, &now, &drvPtr->recvwait);
5009 Push(sockPtr, readPtr)((sockPtr)->nextPtr = (readPtr), (readPtr) = (sockPtr));
5010 break;
5011
5012 case SOCK_READY:
5013 assert(sockPtr->reqPtr != NULL)((void) (0));
5014 Ns_Log(DriverDebug, "spooler thread done with request");
5015 SockSetServer(sockPtr);
5016 Push(sockPtr, waitPtr)((sockPtr)->nextPtr = (waitPtr), (waitPtr) = (sockPtr));
5017 break;
5018
5019 case SOCK_BADHEADER: NS_FALL_THROUGH((void)0); /* fall through */
5020 case SOCK_BADREQUEST: NS_FALL_THROUGH((void)0); /* fall through */
5021 case SOCK_CLOSE: NS_FALL_THROUGH((void)0); /* fall through */
5022 case SOCK_CLOSETIMEOUT: NS_FALL_THROUGH((void)0); /* fall through */
5023 case SOCK_ENTITYTOOLARGE: NS_FALL_THROUGH((void)0); /* fall through */
5024 case SOCK_ERROR: NS_FALL_THROUGH((void)0); /* fall through */
5025 case SOCK_READERROR: NS_FALL_THROUGH((void)0); /* fall through */
5026 case SOCK_READTIMEOUT: NS_FALL_THROUGH((void)0); /* fall through */
5027 case SOCK_SHUTERROR: NS_FALL_THROUGH((void)0); /* fall through */
5028 case SOCK_SPOOL: NS_FALL_THROUGH((void)0); /* fall through */
5029 case SOCK_TOOMANYHEADERS: NS_FALL_THROUGH((void)0); /* fall through */
5030 case SOCK_WRITEERROR: NS_FALL_THROUGH((void)0); /* fall through */
5031 case SOCK_QUEUEFULL: NS_FALL_THROUGH((void)0); /* fall through */
5032 case SOCK_WRITETIMEOUT:
5033 SockRelease(sockPtr, n, errno(*__errno_location ()));
5034 queuePtr->queuesize--;
5035 break;
5036 }
5037 }
5038 sockPtr = nextPtr;
5039 }
5040
5041 /*
5042 * Attempt to queue any pending connection
5043 * after reversing the list to ensure oldest
5044 * connections are tried first.
5045 */
5046
5047 if (waitPtr != NULL((void*)0)) {
5048 sockPtr = NULL((void*)0);
5049 while ((nextPtr = waitPtr) != NULL((void*)0)) {
5050 waitPtr = nextPtr->nextPtr;
5051 Push(nextPtr, sockPtr)((nextPtr)->nextPtr = (sockPtr), (sockPtr) = (nextPtr));
5052 }
5053 while (sockPtr != NULL((void*)0)) {
5054 nextPtr = sockPtr->nextPtr;
5055 if (NsQueueConn(sockPtr, &now) == NS_TIMEOUT) {
5056 Push(sockPtr, waitPtr)((sockPtr)->nextPtr = (waitPtr), (waitPtr) = (sockPtr));
5057 } else {
5058 queuePtr->queuesize--;
5059 }
5060 sockPtr = nextPtr;
5061 }
5062 }
5063
5064 /*
5065 * Add more connections from the spooler queue
5066 */
5067
5068 Ns_MutexLock(&queuePtr->lock);
5069 if (waitPtr == NULL((void*)0)) {
5070 sockPtr = (Sock*)queuePtr->sockPtr;
5071 queuePtr->sockPtr = NULL((void*)0);
5072 while (sockPtr != NULL((void*)0)) {
5073 nextPtr = sockPtr->nextPtr;
5074 drvPtr = sockPtr->drvPtr;
5075 SockTimeout(sockPtr, &now, &drvPtr->recvwait);
5076 Push(sockPtr, readPtr)((sockPtr)->nextPtr = (readPtr), (readPtr) = (sockPtr));
5077 queuePtr->queuesize++;
5078 sockPtr = nextPtr;
5079 }
5080 }
5081
5082 /*
5083 * Check for shutdown
5084 */
5085
5086 stopping = queuePtr->shutdown;
5087 Ns_MutexUnlock(&queuePtr->lock);
5088 }
5089 PollFree(&pdata);
5090
5091 Ns_Log(Notice, "exiting");
5092
5093 Ns_MutexLock(&queuePtr->lock);
5094 queuePtr->stopped = NS_TRUE1;
5095 Ns_CondBroadcast(&queuePtr->cond);
5096 Ns_MutexUnlock(&queuePtr->lock);
5097}
5098
5099static void
5100SpoolerQueueStart(SpoolerQueue *queuePtr, Ns_ThreadProc *proc)
5101{
5102 NS_NONNULL_ASSERT(proc != NULL)((void) (0));
5103
5104 while (queuePtr != NULL((void*)0)) {
5105 if (ns_sockpair(queuePtr->pipe) != 0) {
5106 Ns_Fatal("ns_sockpair() failed: %s", ns_sockstrerrorstrerror(ns_sockerrno(*__errno_location ())));
5107 }
5108 Ns_ThreadCreate(proc, queuePtr, 0, &queuePtr->thread);
5109 queuePtr = queuePtr->nextPtr;
5110 }
5111}
5112
5113static void
5114SpoolerQueueStop(SpoolerQueue *queuePtr, const Ns_Time *timeoutPtr, const char *name)
5115{
5116
5117 NS_NONNULL_ASSERT(timeoutPtr != NULL)((void) (0));
5118 NS_NONNULL_ASSERT(name != NULL)((void) (0));
5119
5120 while (queuePtr != NULL((void*)0)) {
5121 Ns_ReturnCode status;
5122
5123 Ns_MutexLock(&queuePtr->lock);
5124 if (!queuePtr->stopped && !queuePtr->shutdown) {
5125 Ns_Log(Debug, "%s%d: triggering shutdown", name, queuePtr->id);
5126 queuePtr->shutdown = NS_TRUE1;
5127 SockTrigger(queuePtr->pipe[1]);
5128 }
5129 status = NS_OK;
5130 while (!queuePtr->stopped && status == NS_OK) {
5131 status = Ns_CondTimedWait(&queuePtr->cond, &queuePtr->lock, timeoutPtr);
5132 }
5133 if (status != NS_OK) {
5134 Ns_Log(Warning, "%s%d: timeout waiting for shutdown", name, queuePtr->id);
5135 } else {
5136 /*Ns_Log(Notice, "%s%d: shutdown complete", name, queuePtr->id);*/
5137 if (queuePtr->thread != NULL((void*)0)) {
5138 Ns_ThreadJoin(&queuePtr->thread, NULL((void*)0));
5139 queuePtr->thread = NULL((void*)0);
5140 } else {
5141 Ns_Log(Notice, "%s%d: shutdown: thread already gone", name, queuePtr->id);
5142 }
5143 ns_sockcloseclose(queuePtr->pipe[0]);
5144 ns_sockcloseclose(queuePtr->pipe[1]);
5145 }
5146 Ns_MutexUnlock(&queuePtr->lock);
5147 queuePtr = queuePtr->nextPtr;
5148 }
5149}
5150
5151static int
5152SockSpoolerQueue(Driver *drvPtr, Sock *sockPtr)
5153{
5154 bool_Bool trigger = NS_FALSE0;
5155 SpoolerQueue *queuePtr;
5156
5157 NS_NONNULL_ASSERT(drvPtr != NULL)((void) (0));
5158 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
5159 /*
5160 * Get the next spooler thread from the list, all spooler requests are
5161 * rotated between all spooler threads
5162 */
5163
5164 Ns_MutexLock(&drvPtr->spooler.lock);
5165 if (drvPtr->spooler.curPtr == NULL((void*)0)) {
5166 drvPtr->spooler.curPtr = drvPtr->spooler.firstPtr;
5167 }
5168 queuePtr = drvPtr->spooler.curPtr;
5169 drvPtr->spooler.curPtr = drvPtr->spooler.curPtr->nextPtr;
5170 Ns_MutexUnlock(&drvPtr->spooler.lock);
5171
5172 Ns_Log(Debug, "Spooler: %d: started fd=%d: %" PRIdz"zd" " bytes",
5173 queuePtr->id, sockPtr->sock, sockPtr->reqPtr->length);
5174
5175 Ns_MutexLock(&queuePtr->lock);
5176 if (queuePtr->sockPtr == NULL((void*)0)) {
5177 trigger = NS_TRUE1;
5178 }
5179 Push(sockPtr, queuePtr->sockPtr)((sockPtr)->nextPtr = (queuePtr->sockPtr), (queuePtr->
sockPtr) = (sockPtr))
;
5180 Ns_MutexUnlock(&queuePtr->lock);
5181
5182 /*
5183 * Wake up spooler thread
5184 */
5185
5186 if (trigger) {
5187 SockTrigger(queuePtr->pipe[1]);
5188 }
5189
5190 return 1;
5191}
5192
5193/*
5194 *======================================================================
5195 * Writer Thread: Write asynchronously to the client socket
5196 *======================================================================
5197 */
5198
5199/*
5200 *----------------------------------------------------------------------
5201 *
5202 * NsWriterLock, NsWriterUnlock --
5203 *
5204 * Provide an API for locking and unlocking context information
5205 * for streaming asynchronous writer jobs. The locks are just
5206 * needed for managing linkage between "connPtr" and a writer
5207 * entry. The lock operations are rather infrequent and the
5208 * lock duration is very short, such that at a single global
5209 * appears sufficient.
5210 *
5211 * Results:
5212 * None
5213 *
5214 * Side effects:
5215 * Change Mutex state.
5216 *
5217 *----------------------------------------------------------------------
5218 */
5219void NsWriterLock(void) {
5220 Ns_MutexLock(&writerlock);
5221}
5222
5223void NsWriterUnlock(void) {
5224 Ns_MutexUnlock(&writerlock);
5225}
5226
5227/*
5228 *----------------------------------------------------------------------
5229 *
5230 * WriterSockFileVecCleanup --
5231 *
5232 * Cleanup function for FileVec array in WriterSock structure.
5233 *
5234 * Results:
5235 * None.
5236 *
5237 * Side effects:
5238 * Closing potentially file descriptors, freeing Ns_FileVec memory.
5239 *
5240 *----------------------------------------------------------------------
5241 */
5242static void
5243WriterSockFileVecCleanup(const WriterSock *wrSockPtr) {
5244
5245 NS_NONNULL_ASSERT(wrSockPtr != NULL)((void) (0));
5246
5247 if ( wrSockPtr->c.file.nbufs > 0) {
5248 int i;
5249
5250 Ns_Log(DriverDebug, "WriterSockRelease nbufs %d", wrSockPtr->c.file.nbufs);
5251
5252 for (i = 0; i < wrSockPtr->c.file.nbufs; i++) {
5253 /*
5254 * The fd of c.file.currentbuf is always the same as
5255 * wrSockPtr->fd and therefore already closed at this point.
5256 */
5257 if ( (i != wrSockPtr->c.file.currentbuf)
5258 && (wrSockPtr->c.file.bufs[i].fd != NS_INVALID_FD(-1)) ) {
5259
5260 Ns_Log(DriverDebug, "WriterSockRelease must close fd %d",
5261 wrSockPtr->c.file.bufs[i].fd);
5262 ns_closeclose(wrSockPtr->c.file.bufs[i].fd);
5263 }
5264 }
5265 ns_free(wrSockPtr->c.file.bufs);
5266 }
5267 ns_free(wrSockPtr->c.file.buf);
5268}
5269
5270
5271/*
5272 *----------------------------------------------------------------------
5273 *
5274 * WriterSockRequire, WriterSockRelease --
5275 *
5276 * Management functions for WriterSocks. WriterSockRequire() and
5277 * WriterSockRelease() are responsible for obtaining and
5278 * freeing "WriterSock" structures. When shuch a structure is finally
5279 * released, it is removed from the queue, the socket is
5280 * closed and the memory is freed.
5281 *
5282 * Results:
5283 * WriterSockRequire() returns a WriterSock from a connection,
5284 * the other functions return nothing.
5285 *
5286 * Side effects:
5287 * Updating reference counters, closing socket, freeing memory.
5288 *
5289 *----------------------------------------------------------------------
5290 */
5291
5292static WriterSock *
5293WriterSockRequire(const Conn *connPtr) {
5294 WriterSock *wrSockPtr;
5295
5296 NS_NONNULL_ASSERT(connPtr != NULL)((void) (0));
5297
5298 NsWriterLock();
5299 wrSockPtr = (WriterSock *)connPtr->strWriter;
5300 if (wrSockPtr != NULL((void*)0)) {
5301 wrSockPtr->refCount ++;
5302 }
5303 NsWriterUnlock();
5304 return wrSockPtr;
5305}
5306
5307static void
5308WriterSockRelease(WriterSock *wrSockPtr) {
5309 SpoolerQueue *queuePtr;
5310
5311 NS_NONNULL_ASSERT(wrSockPtr != NULL)((void) (0));
5312
5313 wrSockPtr->refCount --;
5314
5315 Ns_Log(DriverDebug, "WriterSockRelease %p refCount %d keep %d",
5316 (void *)wrSockPtr, wrSockPtr->refCount, wrSockPtr->keep);
5317
5318 if (wrSockPtr->refCount > 0) {
5319 return;
5320 }
5321
5322 Ns_Log(DriverDebug,
5323 "Writer: closed sock %d, file fd %d, error %d/%d, "
5324 "sent=%" TCL_LL_MODIFIER"l" "d, flags=%X",
5325 wrSockPtr->sockPtr->sock, wrSockPtr->fd,
5326 wrSockPtr->status, wrSockPtr->err,
5327 wrSockPtr->nsent, wrSockPtr->flags);
5328
5329 NsPoolAddBytesSent(wrSockPtr->poolPtr, wrSockPtr->nsent);
5330
5331 if (wrSockPtr->doStream != NS_WRITER_STREAM_NONE) {
5332 Conn *connPtr;
5333
5334 NsWriterLock();
5335 connPtr = wrSockPtr->connPtr;
5336 if (connPtr != NULL((void*)0) && connPtr->strWriter != NULL((void*)0)) {
5337 connPtr->strWriter = NULL((void*)0);
5338 }
5339 NsWriterUnlock();
5340
5341 /*
5342 * In case, writer streams are activated for this wrSockPtr, make sure
5343 * to release the temporary file. See thread Naviserver Open Files on the
5344 * sourceforge mailing list (starting July 2019).
5345 */
5346 if (wrSockPtr->doStream == NS_WRITER_STREAM_FINISH) {
5347 Ns_ReleaseTemp(wrSockPtr->fd);
5348 }
5349 }
5350
5351 /*
5352 * Remove the entry from the queue and decrement counter
5353 */
5354 queuePtr = wrSockPtr->queuePtr;
5355 if (queuePtr->curPtr == wrSockPtr) {
5356 queuePtr->curPtr = wrSockPtr->nextPtr;
5357 queuePtr->queuesize--;
5358 } else {
5359 WriterSock *curPtr, *lastPtr = queuePtr->curPtr;
5360
5361 for (curPtr = (lastPtr != NULL((void*)0)) ? lastPtr->nextPtr : NULL((void*)0);
5362 curPtr != NULL((void*)0);
5363 lastPtr = curPtr, curPtr = curPtr->nextPtr
5364 ) {
5365 if (curPtr == wrSockPtr) {
5366 lastPtr->nextPtr = wrSockPtr->nextPtr;
5367 queuePtr->queuesize--;
5368 break;
5369 }
5370 }
5371 }
5372
5373 if ((wrSockPtr->err != 0) || (wrSockPtr->status != SPOOLER_OK)) {
5374 int i;
5375 /*
5376 * Lookup the matching sockState from the spooler state. The array has
5377 * just 5 elements, on average, just 2 comparisons are needed (since
5378 * OK is at the end).
5379 */
5380 for (i = 0; i < Ns_NrElements(spoolerStateMap)((int) (sizeof(spoolerStateMap) / sizeof((spoolerStateMap)[0]
)))
; i++) {
5381 if (spoolerStateMap[i].spoolerState == wrSockPtr->status) {
5382 SockError(wrSockPtr->sockPtr, spoolerStateMap[i].sockState, wrSockPtr->err);
5383 break;
5384 }
5385 }
5386 NsSockClose(wrSockPtr->sockPtr, (int)NS_FALSE0);
5387 } else {
5388 NsSockClose(wrSockPtr->sockPtr, (int)wrSockPtr->keep);
5389 }
5390 if (wrSockPtr->clientData != NULL((void*)0)) {
5391 ns_free(wrSockPtr->clientData);
5392 }
5393 if (wrSockPtr->fd != NS_INVALID_FD(-1)) {
5394 if (wrSockPtr->doStream != NS_WRITER_STREAM_FINISH) {
5395 (void) ns_closeclose(wrSockPtr->fd);
5396 }
5397 WriterSockFileVecCleanup(wrSockPtr);
5398
5399 } else if (wrSockPtr->c.mem.bufs != NULL((void*)0)) {
5400 if (wrSockPtr->c.mem.fmap.addr != NULL((void*)0)) {
5401 NsMemUmap(&wrSockPtr->c.mem.fmap);
5402
5403 } else {
5404 int i;
5405 for (i = 0; i < wrSockPtr->c.mem.nbufs; i++) {
5406 ns_free((char *)wrSockPtr->c.mem.bufs[i].iov_base);
5407 }
5408 }
5409 if (wrSockPtr->c.mem.bufs != wrSockPtr->c.mem.preallocated_bufs) {
5410 ns_free(wrSockPtr->c.mem.bufs);
5411 }
5412 }
5413 if (wrSockPtr->headerString != NULL((void*)0)) {
5414 ns_free(wrSockPtr->headerString);
5415 }
5416
5417 ns_free(wrSockPtr);
5418}
5419
5420
5421/*
5422 *----------------------------------------------------------------------
5423 *
5424 * WriterReadFromSpool --
5425 *
5426 * Utility function of the WriterThread to read blocks from a
5427 * file into the output buffer of the writer. It handles
5428 * left overs from previous send attempts and takes care for
5429 * locking in case simultaneous reading and writing from the
5430 * same file.
5431 *
5432 * Results:
5433 * None.
5434 *
5435 * Side effects:
5436 * Fills up curPtr->c.file.buf and updates counters/sizes.
5437 *
5438 *----------------------------------------------------------------------
5439 */
5440
5441static SpoolerState
5442WriterReadFromSpool(WriterSock *curPtr) {
5443 NsWriterStreamState doStream;
5444 SpoolerState status = SPOOLER_OK;
5445 size_t maxsize, toRead;
5446 unsigned char *bufPtr;
5447
5448 NS_NONNULL_ASSERT(curPtr != NULL)((void) (0));
5449
5450 doStream = curPtr->doStream;
5451 if (doStream != NS_WRITER_STREAM_NONE) {
5452 Ns_MutexLock(&curPtr->c.file.fdlock);
5453 toRead = curPtr->c.file.toRead;
5454 Ns_MutexUnlock(&curPtr->c.file.fdlock);
5455 } else {
5456 toRead = curPtr->c.file.toRead;
5457
5458 Ns_Log(DriverDebug, "### WriterReadFromSpool [%d]: fd %d tosend %lu files %d",
5459 curPtr->c.file.currentbuf, curPtr->fd, toRead, curPtr->c.file.nbufs);
5460 }
5461
5462 maxsize = curPtr->c.file.maxsize;
5463 bufPtr = curPtr->c.file.buf;
5464
5465 /*
5466 * When bufsize > 0 we have a leftover from previous send. In such
5467 * cases, move the leftover to the front, and fill the reminder of
5468 * the buffer with new data from curPtr->c.
5469 */
5470
5471 if (curPtr->c.file.bufsize > 0u) {
5472 Ns_Log(DriverDebug,
5473 "### WriterReadFromSpool %p %.6x leftover %" PRIdz"zd" " offset %ld",
5474 (void *)curPtr,
5475 curPtr->flags,
5476 curPtr->c.file.bufsize,
5477 (long)curPtr->c.file.bufoffset);
5478 if (likely(curPtr->c.file.bufoffset > 0)(__builtin_expect((curPtr->c.file.bufoffset > 0), 1))) {
5479 memmove(curPtr->c.file.buf,
5480 curPtr->c.file.buf + curPtr->c.file.bufoffset,
5481 curPtr->c.file.bufsize);
5482 }
5483 bufPtr = curPtr->c.file.buf + curPtr->c.file.bufsize;
5484 maxsize -= curPtr->c.file.bufsize;
5485 }
5486 if (toRead > maxsize) {
5487 toRead = maxsize;
5488 }
5489
5490 /*
5491 * Read content from the file into the buffer.
5492 */
5493 if (toRead > 0u) {
5494 ssize_t n;
5495
5496 if (doStream != NS_WRITER_STREAM_NONE) {
5497 /*
5498 * In streaming mode, the connection thread writes to the
5499 * spool file and the writer thread reads from the same
5500 * file. Therefore, we have to re-adjust the current
5501 * read/writer position, which might be changed by the
5502 * other thread. These positions have to be locked, since
5503 * seeking might be subject to race conditions. Here we
5504 * set the read pointer to the position after the last
5505 * send operation.
5506 */
5507 Ns_MutexLock(&curPtr->c.file.fdlock);
5508 (void) ns_lseeklseek(curPtr->fd, (off_t)curPtr->nsent, SEEK_SET0);
5509 }
5510
5511 if (curPtr->c.file.nbufs == 0) {
5512 /*
5513 * Working on a single fd.
5514 */
5515 n = ns_readread(curPtr->fd, bufPtr, toRead);
5516
5517 } else {
5518 /*
5519 * Working on an Ns_FileVec.
5520 */
5521 int currentbuf = curPtr->c.file.currentbuf;
5522 size_t wantRead = curPtr->c.file.bufs[currentbuf].length;
5523 size_t segSize = (wantRead > toRead ? toRead : wantRead);
5524
5525 n = ns_readread(curPtr->fd, bufPtr, segSize);
5526
5527 Ns_Log(DriverDebug, "### WriterReadFromSpool [%d] (nbufs %d): read from fd %d want %lu got %ld (remain %lu)",
5528 currentbuf, curPtr->c.file.nbufs, curPtr->fd, segSize, n, wantRead);
5529
5530 if (n > 0) {
5531 /*
5532 * Reduce the remaining length in the Ns_FileVec for the
5533 * next iteration.
5534 */
5535 curPtr->c.file.bufs[currentbuf].length -= (size_t)n;
5536
5537 if ((size_t)n < wantRead) {
5538 /*
5539 * Partial read on a segment.
5540 */
5541 Ns_Log(DriverDebug, "### WriterReadFromSpool [%d] (nbufs %d): partial read on fd %d (got %ld)",
5542 currentbuf, curPtr->c.file.nbufs,
5543 curPtr->fd, n);
5544
5545 } else if (currentbuf < curPtr->c.file.nbufs - 1 /* && (n == wantRead) */) {
5546 /*
5547 * All read from this segment, setup next read.
5548 */
5549 ns_closeclose(curPtr->fd);
5550 curPtr->c.file.bufs[currentbuf].fd = NS_INVALID_FD(-1);
5551
5552 curPtr->c.file.currentbuf ++;
5553 curPtr->fd = curPtr->c.file.bufs[curPtr->c.file.currentbuf].fd;
5554
5555 Ns_Log(DriverDebug, "### WriterReadFromSpool switch to [%d] fd %d",
5556 curPtr->c.file.currentbuf, curPtr->fd);
5557 }
5558 }
5559 }
5560
5561 if (n <= 0) {
5562 status = SPOOLER_READERROR;
5563 } else {
5564 /*
5565 * curPtr->c.file.toRead is still protected by
5566 * curPtr->c.file.fdlock when needed (in streaming mode).
5567 */
5568 curPtr->c.file.toRead -= (size_t)n;
5569 curPtr->c.file.bufsize += (size_t)n;
5570 }
5571
5572 if (doStream != NS_WRITER_STREAM_NONE) {
5573 Ns_MutexUnlock(&curPtr->c.file.fdlock);
5574 }
5575 }
5576
5577 return status;
5578}
5579
5580/*
5581 *----------------------------------------------------------------------
5582 *
5583 * WriterSend --
5584 *
5585 * Utility function of the WriterThread to send content to the client. It
5586 * handles partial write operations from the lower level driver
5587 * infrastructure.
5588 *
5589 * Results:
5590 * either NS_OK or SOCK_ERROR;
5591 *
5592 * Side effects:
5593 * Sends data, might reshuffle iovec.
5594 *
5595 *----------------------------------------------------------------------
5596 */
5597
5598static SpoolerState
5599WriterSend(WriterSock *curPtr, int *err) {
5600 const struct iovec *bufs;
5601 struct iovec vbuf;
5602 int nbufs;
5603 SpoolerState status = SPOOLER_OK;
5604 size_t toWrite;
5605 ssize_t n;
5606
5607 NS_NONNULL_ASSERT(curPtr != NULL)((void) (0));
5608 NS_NONNULL_ASSERT(err != NULL)((void) (0));
5609
5610 /*
5611 * Prepare send operation
5612 */
5613 if (curPtr->fd != NS_INVALID_FD(-1)) {
5614 /*
5615 * We have a valid file descriptor, send data from file.
5616 *
5617 * Prepare sending a single buffer with curPtr->c.file.bufsize bytes
5618 * from the curPtr->c.file.buf to the client.
5619 */
5620 vbuf.iov_len = curPtr->c.file.bufsize;
5621 vbuf.iov_base = (void *)curPtr->c.file.buf;
5622 bufs = &vbuf;
5623 nbufs = 1;
5624 toWrite = curPtr->c.file.bufsize;
5625 } else {
5626 int i;
5627
5628 /*
5629 * Prepare sending multiple memory buffers. Get length of remaining
5630 * buffers.
5631 */
5632 toWrite = 0u;
5633 for (i = 0; i < curPtr->c.mem.nsbufs; i ++) {
5634 toWrite += curPtr->c.mem.sbufs[i].iov_len;
5635 }
5636 Ns_Log(DriverDebug,
5637 "### Writer wants to send remainder nbufs %d len %" PRIdz"zd",
5638 curPtr->c.mem.nsbufs, toWrite);
5639
5640 /*
5641 * Add buffers from the source and fill structure up to max
5642 */
5643 while (curPtr->c.mem.bufIdx < curPtr->c.mem.nbufs &&
5644 curPtr->c.mem.sbufIdx < UIO_SMALLIOV8) {
5645 const struct iovec *vPtr = &curPtr->c.mem.bufs[curPtr->c.mem.bufIdx];
5646
5647 if (vPtr->iov_len > 0u && vPtr->iov_base != NULL((void*)0)) {
5648
5649 Ns_Log(DriverDebug,
5650 "### Writer copies source %d to scratch %d len %" PRIiovlen"zd",
5651 curPtr->c.mem.bufIdx, curPtr->c.mem.sbufIdx, vPtr->iov_len);
5652
5653 toWrite += Ns_SetVec(curPtr->c.mem.sbufs, curPtr->c.mem.sbufIdx++,
5654 vPtr->iov_base, vPtr->iov_len);
5655 curPtr->c.mem.nsbufs++;
5656 }
5657 curPtr->c.mem.bufIdx++;
5658 }
5659
5660 bufs = curPtr->c.mem.sbufs;
5661 nbufs = curPtr->c.mem.nsbufs;
5662 Ns_Log(DriverDebug, "### Writer wants to send %d bufs size %" PRIdz"zd",
5663 nbufs, toWrite);
5664 }
5665
5666 /*
5667 * Perform the actual send operation.
5668 */
5669 n = NsDriverSend(curPtr->sockPtr, bufs, nbufs, 0u);
5670
5671 if (n == -1) {
5672 *err = ns_sockerrno(*__errno_location ());
5673 status = SPOOLER_WRITEERROR;
5674 } else {
5675 /*
5676 * We have sent zero or more bytes.
5677 */
5678 if (curPtr->doStream != NS_WRITER_STREAM_NONE) {
5679 Ns_MutexLock(&curPtr->c.file.fdlock);
5680 curPtr->size -= (size_t)n;
5681 Ns_MutexUnlock(&curPtr->c.file.fdlock);
5682 } else {
5683 curPtr->size -= (size_t)n;
5684 }
5685 curPtr->nsent += n;
5686 curPtr->sockPtr->timeout.sec = 0;
5687
5688 if (curPtr->fd != NS_INVALID_FD(-1)) {
5689 /*
5690 * File-descriptor based send operation. Reduce the (remainig)
5691 * buffer size the amount of data sent and adjust the buffer
5692 * offset. For partial send operations, this will lead to a
5693 * remaining buffer size > 0.
5694 */
5695 curPtr->c.file.bufsize -= (size_t)n;
5696 curPtr->c.file.bufoffset = (off_t)n;
5697
5698 } else {
5699 if (n < (ssize_t)toWrite) {
5700 /*
5701 * We have a partial transmit from the iovec
5702 * structure. We have to compact it to fill content in
5703 * the next round.
5704 */
5705 curPtr->c.mem.sbufIdx = Ns_ResetVec(curPtr->c.mem.sbufs, curPtr->c.mem.nsbufs, (size_t)n);
5706 curPtr->c.mem.nsbufs -= curPtr->c.mem.sbufIdx;
5707
5708 memmove(curPtr->c.mem.sbufs, curPtr->c.mem.sbufs + curPtr->c.mem.sbufIdx,
5709 /* move the iovecs to the start of the scratch buffers */
5710 sizeof(struct iovec) * (size_t)curPtr->c.mem.nsbufs);
5711 }
5712 }
5713 }
5714
5715 return status;
5716}
5717
5718/*
5719 *----------------------------------------------------------------------
5720 *
5721 * WriterGetInfoPtr --
5722 *
5723 * Helper function to obtain ConnPoolInfo structure for a WriterSock.
5724 *
5725 * The connInfoPtr is allocated only once per pool and cached in the
5726 * WriterSock. Only the first time, a writer thread "sees" a pool, it
5727 * allocates the structure for it.
5728 *
5729 * Results:
5730 * None.
5731 *
5732 * Side effects:
5733 * Can allocate memory
5734 *
5735 *----------------------------------------------------------------------
5736 */
5737static ConnPoolInfo *
5738WriterGetInfoPtr(WriterSock *curPtr, Tcl_HashTable *pools)
5739{
5740 NS_NONNULL_ASSERT(curPtr != NULL)((void) (0));
5741 NS_NONNULL_ASSERT(pools != NULL)((void) (0));
5742
5743 if (curPtr->infoPtr == NULL((void*)0)) {
5744 int isNew;
5745 Tcl_HashEntry *hPtr;
5746
5747 hPtr = Tcl_CreateHashEntry(pools, (void*)curPtr->poolPtr, &isNew)(*((pools)->createProc))(pools, (const char *)((void*)curPtr
->poolPtr), &isNew)
;
5748 if (isNew == 1) {
5749 /*
5750 * This is a pool that we have not seen yet.
5751 */
5752 curPtr->infoPtr = ns_malloc(sizeof(ConnPoolInfo));
5753 curPtr->infoPtr->currentPoolRate = 0;
5754 curPtr->infoPtr->threadSlot =
5755 NsPoolAllocateThreadSlot(curPtr->poolPtr, Ns_ThreadId());
5756 Tcl_SetHashValue(hPtr, curPtr->infoPtr)((hPtr)->clientData = (ClientData) (curPtr->infoPtr));
5757 Ns_Log(DriverDebug, "poollimit: pool '%s' allocate infoPtr with slot %lu poolLimit %d",
5758 curPtr->poolPtr->pool,
5759 curPtr->infoPtr->threadSlot,
5760 curPtr->poolPtr->rate.poolLimit);
5761 } else {
5762 curPtr->infoPtr = (ConnPoolInfo *)Tcl_GetHashValue(hPtr)((hPtr)->clientData);
5763 }
5764 }
5765
5766 return curPtr->infoPtr;
5767}
5768
5769/*
5770 *----------------------------------------------------------------------
5771 *
5772 * WriterPerPoolRates --
5773 *
5774 * Compute current bandwidths per pool and writer.
5775 *
5776 * Since we have potentially multiple writer threads running, all these
5777 * might have writer threads of the same pool. In order to minimize
5778 * locking, we compute first writer thread specific subresults and combine
5779 * these later with with the results of the other threads.
5780 *
5781 * Results:
5782 * None.
5783 *
5784 * Side effects:
5785 * Connections are accepted and their SockPtr is set to NULL
5786 * such that closing actual connection does not close the socket.
5787 *
5788 *----------------------------------------------------------------------
5789 */
5790
5791static void
5792WriterPerPoolRates(WriterSock *writePtr, Tcl_HashTable *pools)
5793{
5794 WriterSock *curPtr;
5795 Tcl_HashSearch search;
5796 Tcl_HashEntry *hPtr;
5797
5798 NS_NONNULL_ASSERT(writePtr != NULL)((void) (0));
5799 NS_NONNULL_ASSERT(pools != NULL)((void) (0));
5800
5801 /*
5802 * First reset pool total rate. We keep the bandwidth managed pools in a
5803 * thread-local memory. Before, we accumulate the data, we reset it.
5804 */
5805 hPtr = Tcl_FirstHashEntry(pools, &search);
5806 while (hPtr != NULL((void*)0)) {
5807 ConnPoolInfo *infoPtr = (ConnPoolInfo *)Tcl_GetHashValue(hPtr)((hPtr)->clientData);
5808 infoPtr->currentPoolRate = 0;
5809 hPtr = Tcl_NextHashEntry(&search);
5810 }
5811
5812 /*
5813 * Sum the actual rates per bandwidth limited pool for all active writer
5814 * jobs.
5815 */
5816 for (curPtr = writePtr; curPtr != NULL((void*)0); curPtr = curPtr->nextPtr) {
5817 /*
5818 * Does the writer come form a badwidth limited pool?
5819 */
5820 if (curPtr->poolPtr->rate.poolLimit > 0 && curPtr->currentRate > 0) {
5821 /*
5822 * Add the actual rate to the writer specific pool rate.
5823 */
5824 ConnPoolInfo *infoPtr = WriterGetInfoPtr(curPtr, pools);
5825
5826 infoPtr->currentPoolRate += curPtr->currentRate;
5827 Ns_Log(DriverDebug, "poollimit pool '%s' added rate poolLimit %d poolRate %d",
5828 curPtr->poolPtr->pool,
5829 curPtr->poolPtr->rate.poolLimit,
5830 infoPtr->currentPoolRate);
5831 }
5832 }
5833
5834 /*
5835 * Now iterate over the pools used by this thread and sum the specific
5836 * pool rates from all writer threads.
5837 */
5838 hPtr = Tcl_FirstHashEntry(pools, &search);
5839 while (hPtr != NULL((void*)0)) {
5840 ConnPool *poolPtr = (ConnPool *)Tcl_GetHashKey(pools, hPtr)((void *) (((pools)->keyType == (1) || (pools)->keyType
== (-1)) ? (hPtr)->key.oneWordValue : (hPtr)->key.string
))
;
5841 int totalPoolRate, writerThreadCount, threadDeltaRate;
5842 ConnPoolInfo *infoPtr;
5843
5844 /*
5845 * Compute the following indicators:
5846 * - totalPoolRate: accumulated pool rates from all writer threads.
5847 *
5848 * - threadDeltaRate: how much of the available bandwidth can i used
5849 * the current thread. We assume that the distribution of writers
5850 * between all writer threads is even, so we can split the
5851 * available rate by the number of writer threads working on this
5852 * pool.
5853 *
5854 * - deltaPercentage: adjust in a single iteration just a fraction
5855 * (e.g. 10 percent) of the potential change. This function is
5856 * called often enough to justify delayed adjustments.
5857 */
5858 infoPtr = (ConnPoolInfo *)Tcl_GetHashValue(hPtr)((hPtr)->clientData);
5859 totalPoolRate = NsPoolTotalRate(poolPtr,
5860 infoPtr->threadSlot,
5861 infoPtr->currentPoolRate,
5862 &writerThreadCount);
5863
5864 /*
5865 * If nothing is going on, allow a thread the full rate.
5866 */
5867 if (infoPtr->currentPoolRate == 0) {
5868 threadDeltaRate = (poolPtr->rate.poolLimit - totalPoolRate);
5869 } else {
5870 threadDeltaRate = (poolPtr->rate.poolLimit - totalPoolRate) / writerThreadCount;
5871 }
5872 infoPtr->deltaPercentage = threadDeltaRate / 10;
5873 if (infoPtr->deltaPercentage < -50) {
5874 infoPtr->deltaPercentage = -50;
5875 }
5876
5877 if (totalPoolRate > 0) {
5878 Ns_Log(Notice, "... pool '%s' thread's pool rate %d total pool rate %d limit %d "
5879 "(#%d writer threads) -> computed rate %d (%d%%) ",
5880 NsPoolName(poolPtr->pool),
5881 infoPtr->currentPoolRate,
5882 totalPoolRate,
5883 poolPtr->rate.poolLimit,
5884 writerThreadCount,
5885 threadDeltaRate,
5886 infoPtr->deltaPercentage
5887 );
5888 }
5889
5890 hPtr = Tcl_NextHashEntry(&search);
5891 }
5892}
5893
5894/*
5895 *----------------------------------------------------------------------
5896 *
5897 * WriterThread --
5898 *
5899 * Thread that writes files to clients.
5900 *
5901 * Results:
5902 * None.
5903 *
5904 * Side effects:
5905 * Connections are accepted and their SockPtr is set to NULL
5906 * such that closing actual connection does not close the socket.
5907 *
5908 *----------------------------------------------------------------------
5909 */
5910
5911static void
5912WriterThread(void *arg)
5913{
5914 SpoolerQueue *queuePtr = (SpoolerQueue*)arg;
5915 int err, pollTimeout;
5916 bool_Bool stopping = NS_FALSE0;
5917 Ns_Time now;
5918 Sock *sockPtr;
5919 const Driver *drvPtr;
5920 WriterSock *curPtr, *nextPtr, *writePtr = NULL((void*)0);
5921 PollData pdata;
5922 Tcl_HashTable pools; /* used for accumulating bandwidth per pool */
5923
5924 Ns_ThreadSetName("-writer%d-", queuePtr->id);
5925 queuePtr->threadName = Ns_ThreadGetName();
5926
5927 Tcl_InitHashTable(&pools, TCL_ONE_WORD_KEYS(1));
5928
5929 /*
5930 * Loop forever until signaled to shut down and all
5931 * connections are complete and gracefully closed.
5932 */
5933
5934 Ns_Log(Notice, "writer%d: accepting connections", queuePtr->id);
5935
5936 PollCreate(&pdata);
5937
5938 while (!stopping) {
5939 char charBuffer[1];
5940
5941 /*
5942 * If there are any write sockets, set the bits.
5943 */
5944
5945 PollReset(&pdata);
5946 (void)PollSet(&pdata, queuePtr->pipe[0], (short)POLLIN0x001, NULL((void*)0));
5947
5948 if (writePtr == NULL((void*)0)) {
5949 pollTimeout = 30 * 1000;
5950 } else {
5951
5952 /*
5953 * If per-pool bandwidth management is requested, compute the base
5954 * data for the adjustment. If there is no bandwidth management
5955 * requested, there is no slowdow.
5956 */
5957 if (NsWriterBandwidthManagement) {
5958 WriterPerPoolRates(writePtr, &pools);
5959 }
5960
5961 /*
5962 * There are writers active. Determine on which writers we poll
5963 * and compute the maximal poll wait time.
5964 */
5965 pollTimeout = 1000;
5966 for (curPtr = writePtr; curPtr != NULL((void*)0); curPtr = curPtr->nextPtr) {
5967 int sleepTimeMs = 0;
5968
5969 Ns_Log(DriverDebug, "### Writer poll collect %p size %" PRIdz"zd"
5970 " streaming %d rateLimit %d",
5971 (void *)curPtr, curPtr->size, curPtr->doStream, curPtr->rateLimit);
5972
5973 if (curPtr->rateLimit > 0
5974 && curPtr->nsent > 0
5975 && curPtr->currentRate > 0
5976 ) {
5977 int currentMs, targetTimeMs;
5978
5979 /*
5980 * Perform per-pool rate management, when
5981 * - a poolLimit is provided,
5982 * - we have performance data of thee pool, and
5983 * - changes are possible (as flagged by deltaPercentage).
5984 */
5985 if (NsWriterBandwidthManagement
5986 && curPtr->poolPtr->rate.poolLimit > 0
5987 && curPtr->infoPtr != NULL((void*)0)
5988 && curPtr->infoPtr->deltaPercentage != 0
5989 ) {
5990 /*
5991 * Only adjust data for busy writer jobs, which
5992 * are close to their limits.
5993 */
5994 bool_Bool onLimit = (curPtr->currentRate*100 / curPtr->rateLimit) > 90;
5995
5996 Ns_Log(DriverDebug, "we allowed %d we use %d on limit %d (%d) , we can do %d%%",
5997 curPtr->rateLimit, curPtr->currentRate,
5998 (int)onLimit, curPtr->currentRate*100/curPtr->rateLimit,
5999 curPtr->infoPtr->deltaPercentage);
6000 if (onLimit) {
6001 /*
6002 * Compute new rate limit based on
6003 * positive/negative delta percentage.
6004 */
6005 int newRate = curPtr->currentRate +
6006 (curPtr->currentRate * curPtr->infoPtr->deltaPercentage / 100);
6007 /*
6008 * Sanity checks:
6009 * - never allow more than poolLimit
6010 * - never kill connections completely (e.g. minRate 5KB/s)
6011 */
6012 if (newRate > curPtr->poolPtr->rate.poolLimit) {
6013 newRate = curPtr->poolPtr->rate.poolLimit;
6014 } else if (newRate < 5) {
6015 newRate = 5;
6016 }
6017 Ns_Log(Notice, "... pool '%s' new rate limit changed from %d to %d KB/s (delta %d%%)",
6018 curPtr->poolPtr->pool, curPtr->rateLimit, newRate,
6019 curPtr->infoPtr->deltaPercentage);
6020 curPtr->rateLimit = newRate;
6021 }
6022 }
6023
6024 /*
6025 * Adjust rate to the rate limit.
6026 */
6027 currentMs = (int)(curPtr->nsent/(Tcl_WideInt)curPtr->currentRate);
6028 targetTimeMs = (int)(curPtr->nsent/(Tcl_WideInt)curPtr->rateLimit);
6029 sleepTimeMs = 1 + targetTimeMs - currentMs;
6030 Ns_Log(WriterDebug, "### Writer(%d)"
6031 " byte sent %" TCL_LL_MODIFIER"l" "d msecs %d rate %d KB/s"
6032 " targetRate %d KB/s sleep %d",
6033 curPtr->sockPtr->sock,
6034 curPtr->nsent, currentMs,
6035 curPtr->currentRate,
6036 curPtr->rateLimit,
6037 sleepTimeMs);
6038 }
6039
6040 if (likely(curPtr->size > 0u)(__builtin_expect((curPtr->size > 0u), 1))) {
6041 if (sleepTimeMs <= 0) {
6042 SockPoll(curPtr->sockPtr, (short)POLLOUT0x004, &pdata);
6043 pollTimeout = -1;
6044 } else {
6045 pollTimeout = MIN(sleepTimeMs, pollTimeout)(((sleepTimeMs)<(pollTimeout))?(sleepTimeMs):(pollTimeout)
)
;
6046 }
6047 } else if (unlikely(curPtr->doStream == NS_WRITER_STREAM_FINISH)(__builtin_expect((curPtr->doStream == NS_WRITER_STREAM_FINISH
), 0))
) {
6048 pollTimeout = -1;
6049 }
6050 }
6051 }
6052 Ns_Log(DriverDebug, "### Writer final pollTimeout %d", pollTimeout);
6053
6054 /*
6055 * Select and drain the trigger pipe if necessary.
6056 */
6057 (void) PollWait(&pdata, pollTimeout);
6058
6059 if (PollIn(&pdata, 0)(((&pdata)->pfds[(0)].revents & 0x001) == 0x001 ) && unlikely(ns_recv(queuePtr->pipe[0], charBuffer, 1u, 0) != 1)(__builtin_expect((recv(queuePtr->pipe[0], charBuffer, 1u,
0) != 1), 0))
) {
6060 Ns_Fatal("writer: trigger ns_recv() failed: %s",
6061 ns_sockstrerrorstrerror(ns_sockerrno(*__errno_location ())));
6062 }
6063
6064 /*
6065 * Write to all available sockets
6066 */
6067 Ns_GetTime(&now);
6068 curPtr = writePtr;
6069 writePtr = NULL((void*)0);
6070
6071 while (curPtr != NULL((void*)0)) {
6072 NsWriterStreamState doStream;
6073 SpoolerState spoolerState = SPOOLER_OK;
6074
6075 nextPtr = curPtr->nextPtr;
6076 sockPtr = curPtr->sockPtr;
6077 err = 0;
6078
6079 /*
6080 * The truth value of doStream does not change through
6081 * concurrency.
6082 */
6083 doStream = curPtr->doStream;
6084
6085 if (unlikely(PollHup(&pdata, sockPtr->pidx))(__builtin_expect(((((&pdata)->pfds[(sockPtr->pidx)
].revents & 0x010) == 0x010)), 0))
) {
6086 Ns_Log(DriverDebug, "### Writer %p reached POLLHUP fd %d", (void *)curPtr, sockPtr->sock);
6087 spoolerState = SPOOLER_CLOSE;
6088 err = 0;
6089 curPtr->infoPtr = WriterGetInfoPtr(curPtr, &pools);
6090 curPtr->infoPtr->currentPoolRate += curPtr->currentRate;
6091
6092
6093 } else if (likely(PollOut(&pdata, sockPtr->pidx))(__builtin_expect(((((&pdata)->pfds[(sockPtr->pidx)
].revents & 0x004) == 0x004)), 1))
|| (doStream == NS_WRITER_STREAM_FINISH)) {
6094 /*
6095 * The socket is writable, we can compute the rate, when
6096 * something was sent already and some kind of rate limiting
6097 * is in place ... and we have sent enough data to make a good
6098 * estimate (just after the 2nd send, so more than driver
6099 * buffer size.
6100 */
6101 Ns_Log(DriverDebug, "Socket of pool '%s' is writable, writer limit %d nsent %ld",
6102 curPtr->poolPtr->pool, curPtr->rateLimit, (long)curPtr->nsent);
6103
6104 if (curPtr->rateLimit > 0
6105 && (size_t)curPtr->nsent > curPtr->sockPtr->drvPtr->bufsize
6106 ) {
6107 Ns_Time diff;
6108 time_t currentMs;
6109
6110 Ns_DiffTime(&now, &curPtr->startTime, &diff);
6111 currentMs = Ns_TimeToMilliseconds(&diff);
6112 if (currentMs > 0) {
6113 curPtr->currentRate = (int)((curPtr->nsent)/(Tcl_WideInt)currentMs);
6114 Ns_Log(DriverDebug,
6115 "Socket of pool '%s' is writable, currentMs %" PRId64"l" "d"
6116 " has updated current rate %d",
6117 curPtr->poolPtr->pool, (int64_t)currentMs, curPtr->currentRate);
6118 }
6119 }
6120 Ns_Log(DriverDebug,
6121 "### Writer %p can write to client fd %d (trigger %d) streaming %.6x"
6122 " size %" PRIdz"zd" " nsent %" TCL_LL_MODIFIER"l" "d bufsize %" PRIdz"zd",
6123 (void *)curPtr, sockPtr->sock, PollIn(&pdata, 0)(((&pdata)->pfds[(0)].revents & 0x001) == 0x001 ), doStream,
6124 curPtr->size, curPtr->nsent, curPtr->c.file.bufsize);
6125 if (unlikely(curPtr->size < 1u)(__builtin_expect((curPtr->size < 1u), 0))) {
6126 /*
6127 * Size < 1 means that everything was sent.
6128 */
6129 if (doStream != NS_WRITER_STREAM_ACTIVE) {
6130 if (doStream == NS_WRITER_STREAM_FINISH) {
6131 Ns_ReleaseTemp(curPtr->fd);
6132 }
6133 spoolerState = SPOOLER_CLOSE;
6134 }
6135 } else {
6136 /*
6137 * If size > 0, there is still something to send.
6138 * If we are spooling from a file, read some data
6139 * from the (spool) file and place it into curPtr->c.file.buf.
6140 */
6141 if (curPtr->fd != NS_INVALID_FD(-1)) {
6142 spoolerState = WriterReadFromSpool(curPtr);
6143 }
6144
6145 if (spoolerState == SPOOLER_OK) {
6146 spoolerState = WriterSend(curPtr, &err);
6147 }
6148 }
6149 } else {
6150
6151 /*
6152 * Mark when first timeout occurred or check if it is already
6153 * for too long and we need to stop this socket
6154 */
6155 if (sockPtr->timeout.sec == 0) {
6156 Ns_Log(DriverDebug, "Writer %p fd %d setting sendwait " NS_TIME_FMT"%" "l" "d" ".%06ld",
6157 (void *)curPtr, sockPtr->sock,
6158 (int64_t)curPtr->sockPtr->drvPtr->sendwait.sec,
6159 curPtr->sockPtr->drvPtr->sendwait.usec);
6160 SockTimeout(sockPtr, &now, &curPtr->sockPtr->drvPtr->sendwait);
6161 } else if (Ns_DiffTime(&sockPtr->timeout, &now, NULL((void*)0)) <= 0) {
6162 Ns_Log(DriverDebug, "Writer %p fd %d timeout", (void *)curPtr, sockPtr->sock);
6163 err = ETIMEDOUT110;
6164 spoolerState = SPOOLER_CLOSETIMEOUT;
6165 }
6166 }
6167
6168 /*
6169 * Check result status and close the socket in case of
6170 * timeout or completion
6171 */
6172
6173 Ns_MutexLock(&queuePtr->lock);
6174 if (spoolerState == SPOOLER_OK) {
6175 if (curPtr->size > 0u || doStream == NS_WRITER_STREAM_ACTIVE) {
6176 Ns_Log(DriverDebug,
6177 "Writer %p continue OK (size %" PRIdz"zd" ") => PUSH",
6178 (void *)curPtr, curPtr->size);
6179 Push(curPtr, writePtr)((curPtr)->nextPtr = (writePtr), (writePtr) = (curPtr));
6180 } else {
6181 Ns_Log(DriverDebug,
6182 "Writer %p done OK (size %" PRIdz"zd" ") => RELEASE",
6183 (void *)curPtr, curPtr->size);
6184 WriterSockRelease(curPtr);
6185 }
6186 } else {
6187 /*
6188 * spoolerState might be SPOOLER_CLOSE or SPOOLER_*TIMEOUT, or SPOOLER_*ERROR
6189 */
6190 Ns_Log(DriverDebug,
6191 "Writer %p fd %d release, not OK (status %d) => RELEASE",
6192 (void *)curPtr, curPtr->sockPtr->sock, (int)spoolerState);
6193 curPtr->status = spoolerState;
6194 curPtr->err = err;
6195 WriterSockRelease(curPtr);
6196 }
6197 Ns_MutexUnlock(&queuePtr->lock);
6198 curPtr = nextPtr;
6199 }
6200
6201 /*
6202 * Add more sockets to the writer queue
6203 */
6204
6205 if (queuePtr->sockPtr != NULL((void*)0)) {
6206 Ns_MutexLock(&queuePtr->lock);
6207 if (queuePtr->sockPtr != NULL((void*)0)) {
6208 curPtr = queuePtr->sockPtr;
6209 queuePtr->sockPtr = NULL((void*)0);
6210 while (curPtr != NULL((void*)0)) {
6211 nextPtr = curPtr->nextPtr;
6212 sockPtr = curPtr->sockPtr;
6213 drvPtr = sockPtr->drvPtr;
6214 SockTimeout(sockPtr, &now, &drvPtr->sendwait);
6215 Push(curPtr, writePtr)((curPtr)->nextPtr = (writePtr), (writePtr) = (curPtr));
6216 queuePtr->queuesize++;
6217 curPtr = nextPtr;
6218 }
6219 queuePtr->curPtr = writePtr;
6220 }
6221 Ns_MutexUnlock(&queuePtr->lock);
6222 }
6223
6224 /*
6225 * Check for shutdown (potentially a dirty read)
6226 */
6227 stopping = queuePtr->shutdown;
6228 }
6229 PollFree(&pdata);
6230
6231 {
6232 /*
6233 * Free ConnPoolInfo
6234 */
6235 Tcl_HashSearch search;
6236 Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&pools, &search);
6237 while (hPtr != NULL((void*)0)) {
6238 ConnPoolInfo *infoPtr = (ConnPoolInfo *)Tcl_GetHashValue(hPtr)((hPtr)->clientData);
6239 ns_free(infoPtr);
6240 hPtr = Tcl_NextHashEntry(&search);
6241 }
6242 /*
6243 * Delete the hash table for pools.
6244 */
6245 Tcl_DeleteHashTable(&pools);
6246 }
6247
6248 /*fprintf(stderr, "==== writerthread exits queuePtr %p writePtr %p\n",
6249 (void*)queuePtr->sockPtr,
6250 (void*)writePtr);
6251 for (sockPtr = queuePtr->sockPtr; sockPtr != NULL; sockPtr = sockPtr->nextPtr) {
6252 fprintf(stderr, "==== writerthread exits queuePtr %p sockPtr %p\n", (void*)queuePtr, (void*)sockPtr);
6253 }*/
6254
6255 Ns_Log(Notice, "exiting");
6256
6257 Ns_MutexLock(&queuePtr->lock);
6258 queuePtr->stopped = NS_TRUE1;
6259 Ns_CondBroadcast(&queuePtr->cond);
6260 Ns_MutexUnlock(&queuePtr->lock);
6261}
6262
6263/*
6264 *----------------------------------------------------------------------
6265 *
6266 * NsWriterFinish --
6267 *
6268 * Finish a streaming writer job (typically called at the close
6269 * of a connection). A streaming writer job is fed typically by a
6270 * sequence of ns_write operations. After such an operation, the
6271 * WriterThread has to keep the writer job alive.
6272 * NsWriterFinish() tells the WriterThread that no more
6273 * other writer jobs will come from this connection.
6274 *
6275 * Results:
6276 * None.
6277 *
6278 * Side effects:
6279 * Change the state of the writer job and trigger the queue.
6280 *
6281 *----------------------------------------------------------------------
6282 */
6283void
6284NsWriterFinish(NsWriterSock *wrSockPtr) {
6285 WriterSock *writerSockPtr = (WriterSock *)wrSockPtr;
6286
6287 NS_NONNULL_ASSERT(wrSockPtr != NULL)((void) (0));
6288
6289 Ns_Log(DriverDebug, "NsWriterFinish: %p", (void *)writerSockPtr);
6290 writerSockPtr->doStream = NS_WRITER_STREAM_FINISH;
6291 SockTrigger(writerSockPtr->queuePtr->pipe[1]);
6292}
6293
6294
6295/*
6296 *----------------------------------------------------------------------
6297 *
6298 * WriterSetupStreamingMode --
6299 *
6300 * In streaming mode, setup a temporary fd which is used as input and
6301 * output. Streaming i/o will append to the file, while the write will
6302 * read from it.
6303 *
6304 * Results:
6305 * Ns_ReturnCode (NS_OK, NS_ERROR, NS_FILTER_BREAK). In the last case
6306 * signals that all processing was already performed and the caller can
6307 * stop handling more data. On success, the function returns an fd as
6308 * last argument.
6309 *
6310 * Side effects:
6311 * Potentially allocating temp file and updating connPtr members.
6312 *
6313 *----------------------------------------------------------------------
6314 */
6315Ns_ReturnCode
6316WriterSetupStreamingMode(Conn *connPtr, const struct iovec *bufs, int nbufs, int *fdPtr)
6317{
6318 bool_Bool first;
6319 size_t wrote = 0u;
6320 WriterSock *wrSockPtr1;
6321 Ns_ReturnCode status = NS_OK;
6322
6323 NS_NONNULL_ASSERT(connPtr != NULL)((void) (0));
6324 NS_NONNULL_ASSERT(fdPtr != NULL)((void) (0));
6325
6326 Ns_Log(DriverDebug, "NsWriterQueue: streaming writer job");
6327
6328 if (connPtr->fd == 0) {
6329 /*
6330 * Create a new temporary spool file and provide the fd to the
6331 * connection thread via connPtr.
6332 */
6333 first = NS_TRUE1;
6334 wrSockPtr1 = NULL((void*)0);
6335
6336 *fdPtr = Ns_GetTemp();
6337 connPtr->fd = *fdPtr;
6338
6339 Ns_Log(DriverDebug, "NsWriterQueue: new temporary file has fd %d", *fdPtr);
6340
6341 } else {
6342 /*
6343 * Reuse previously created spool file.
6344 */
6345 first = NS_FALSE0;
6346 wrSockPtr1 = WriterSockRequire(connPtr);
6347
6348 if (wrSockPtr1 == NULL((void*)0)) {
6349 Ns_Log(Notice,
6350 "NsWriterQueue: writer job was already canceled (fd %d); maybe user dropped connection",
6351 connPtr->fd);
6352 return NS_ERROR;
6353
6354 } else {
6355 /*
6356 * lock only, when first == NS_FALSE.
6357 */
6358 Ns_MutexLock(&wrSockPtr1->c.file.fdlock);
6359 (void)ns_lseeklseek(connPtr->fd, 0, SEEK_END2);
6360 }
6361 }
6362
6363 /*
6364 * For the time being, handle just "string data" in streaming
6365 * output (iovec bufs). Write the content to the spool file.
6366 */
6367 {
6368 int i;
6369
6370 for (i = 0; i < nbufs; i++) {
6371 ssize_t j = ns_writewrite(connPtr->fd, bufs[i].iov_base, bufs[i].iov_len);
6372
6373 if (j > 0) {
6374 wrote += (size_t)j;
6375 Ns_Log(Debug, "NsWriterQueue: fd %d [%d] spooled %" PRIdz"zd" " of %" PRIiovlen"zd" " OK %d",
6376 connPtr->fd, i, j, bufs[i].iov_len, (j == (ssize_t)bufs[i].iov_len));
6377 } else {
6378 Ns_Log(Warning, "NsWriterQueue: spool to fd %d write operation failed",
6379 connPtr->fd);
6380 }
6381 }
6382 }
6383
6384 if (first) {
6385 connPtr->nContentSent = wrote;
6386#ifndef _WIN32
6387 /*
6388 * sock_set_blocking can't be used under windows, since sockets
6389 * are under windows no file descriptors.
6390 */
6391 (void)ns_sock_set_blocking(connPtr->fd, NS_FALSE0);
6392#endif
6393 /*
6394 * Fall through to register stream writer with temp file
6395 */
6396 } else {
6397 WriterSock *writerSockPtr;
6398
6399 /*
6400 * This is a later streaming operation, where the writer job
6401 * (strWriter) was previously established.
6402 */
6403 assert(wrSockPtr1 != NULL)((void) (0));
6404 /*
6405 * Update the controlling variables (size and toread) in the connPtr,
6406 * and the length info for the access log, and trigger the writer to
6407 * notify it about the change.
6408 */
6409
6410 writerSockPtr = (WriterSock *)connPtr->strWriter;
6411 writerSockPtr->size += wrote;
6412 writerSockPtr->c.file.toRead += wrote;
6413 Ns_MutexUnlock(&wrSockPtr1->c.file.fdlock);
6414
6415 connPtr->nContentSent += wrote;
6416 if (likely(wrSockPtr1->queuePtr != NULL)(__builtin_expect((wrSockPtr1->queuePtr != ((void*)0)), 1)
)
) {
6417 SockTrigger(wrSockPtr1->queuePtr->pipe[1]);
6418 }
6419 WriterSockRelease(wrSockPtr1);
6420 status = NS_FILTER_BREAK;
6421 }
6422
6423 return status;
6424}
6425
6426
6427/*
6428 *----------------------------------------------------------------------
6429 *
6430 * NsWriterQueue --
6431 *
6432 * Submit a new job to the writer queue.
6433 *
6434 * Results:
6435 *
6436 * NS_ERROR means that the Writer thread refuses to accept this
6437 * job and that the client (the connection thread) has to handle
6438 * this data. NS_OK means that the Writer thread cares for
6439 * transmitting the content to the client.
6440 *
6441 * Side effects:
6442 * Potentially adding a job to the writer queue.
6443 *
6444 *----------------------------------------------------------------------
6445 */
6446
6447Ns_ReturnCode
6448NsWriterQueue(Ns_Conn *conn, size_t nsend,
6449 Tcl_Channel chan, FILE *fp, int fd,
6450 struct iovec *bufs, int nbufs,
6451 const Ns_FileVec *filebufs, int nfilebufs,
6452 bool_Bool everysize)
6453{
6454 Conn *connPtr;
6455 WriterSock *wrSockPtr;
6456 SpoolerQueue *queuePtr;
6457 DrvWriter *wrPtr;
6458 bool_Bool trigger = NS_FALSE0;
6459 size_t headerSize;
6460 Ns_ReturnCode status = NS_OK;
6461 Ns_FileVec *fbufs = NULL((void*)0);
6462 int nfbufs = 0;
6463
6464 NS_NONNULL_ASSERT(conn != NULL)((void) (0));
6465 connPtr = (Conn *)conn;
6466
6467 if (unlikely(connPtr->sockPtr == NULL)(__builtin_expect((connPtr->sockPtr == ((void*)0)), 0))) {
6468 Ns_Log(Warning,
6469 "NsWriterQueue: called without sockPtr size %" PRIdz"zd" " bufs %d flags %.6x stream %.6x chan %p fd %d",
6470 nsend, nbufs, connPtr->flags, connPtr->flags & NS_CONN_STREAM0x040u,
6471 (void *)chan, fd);
6472 status = NS_ERROR;
6473 wrPtr = NULL((void*)0);
6474 } else {
6475
6476 wrPtr = &connPtr->sockPtr->drvPtr->writer;
6477
6478 Ns_Log(DriverDebug,
6479 "NsWriterQueue: size %" PRIdz"zd" " bufs %p (%d) flags %.6x stream %.6x chan %p fd %d thread %d",
6480 nsend, (void *)bufs, nbufs, connPtr->flags, connPtr->flags & NS_CONN_STREAM0x040u,
6481 (void *)chan, fd, wrPtr->threads);
6482
6483 if (unlikely(wrPtr->threads == 0)(__builtin_expect((wrPtr->threads == 0), 0))) {
6484 Ns_Log(DriverDebug, "NsWriterQueue: no writer threads configured");
6485 status = NS_ERROR;
6486
6487 } else if (nsend < (size_t)wrPtr->writersize && !everysize && connPtr->fd == 0) {
6488 Ns_Log(DriverDebug, "NsWriterQueue: file is too small(%" PRIdz"zd" " < %" PRIdz"zd" ")",
6489 nsend, wrPtr->writersize);
6490 status = NS_ERROR;
6491 }
6492 }
6493 if (status != NS_OK) {
6494 return status;
6495 }
6496
6497 assert(wrPtr != NULL)((void) (0));
6498
6499 /*
6500 * In streaming mode, setup a temporary fd which is used as input and
6501 * output. Streaming i/o will append to the file, while the write will
6502 * read from it.
6503 */
6504 if (((connPtr->flags & NS_CONN_STREAM0x040u) != 0u) || connPtr->fd > 0) {
6505
6506 if (wrPtr->doStream == NS_WRITER_STREAM_NONE) {
6507 status = NS_ERROR;
6508 } else if (unlikely(fp != NULL || fd != NS_INVALID_FD)(__builtin_expect((fp != ((void*)0) || fd != (-1)), 0))) {
6509 Ns_Log(DriverDebug, "NsWriterQueue: does not stream from this source via writer");
6510 status = NS_ERROR;
6511 } else {
6512 status = WriterSetupStreamingMode(connPtr, bufs, nbufs, &fd);
6513 }
6514
6515 if (unlikely(status != NS_OK)(__builtin_expect((status != NS_OK), 0))) {
6516 if (status == NS_FILTER_BREAK) {
6517 status = NS_OK;
6518 }
6519 return status;
6520 }
6521
6522 /*
6523 * As a result of successful WriterSetupStreamingMode(), we have fd
6524 * set.
6525 */
6526 assert(fd != NS_INVALID_FD)((void) (0));
6527
6528 } else {
6529 if (fp != NULL((void*)0)) {
6530 /*
6531 * The client provided an open file pointer and closes it
6532 */
6533 fd = ns_dup(fileno(fp))fcntl((fileno(fp)), 1030, 0);
6534 } else if (fd != NS_INVALID_FD(-1)) {
6535 /*
6536 * The client provided an open file descriptor and closes it
6537 */
6538 fd = ns_dup(fd)fcntl((fd), 1030, 0);
6539 } else if (chan != NULL((void*)0)) {
6540 ClientData clientData;
6541 /*
6542 * The client provided an open Tcl channel and closes it
6543 */
6544 if (Tcl_GetChannelHandle(chan, TCL_READABLE(1<<1), &clientData) != TCL_OK0) {
6545 return NS_ERROR;
6546 }
6547 fd = ns_dup(PTR2INT(clientData))fcntl((((int)(intptr_t)(clientData))), 1030, 0);
6548 } else if (filebufs != NULL((void*)0) && nfilebufs > 0) {
6549 /*
6550 * The client provided Ns_FileVec with open files. The client is
6551 * responsible for closing it, like in all other cases.
6552 */
6553 size_t i;
6554
6555 /*
6556 * This is the only case, where fbufs will be != NULL,
6557 * i.e. keeping a duplicate of the passed-in Ns_FileVec structure
6558 * for which the client is responsible.
6559 */
6560 fbufs = (Ns_FileVec *)ns_calloc((size_t)nfilebufs, sizeof(Ns_FileVec));
6561 nfbufs = nfilebufs;
6562
6563 for (i = 0u; i < (size_t)nfilebufs; i++) {
6564 fbufs[i].fd = ns_dup(filebufs[i].fd)fcntl((filebufs[i].fd), 1030, 0);
6565 fbufs[i].length = filebufs[i].length;
6566 fbufs[i].offset = filebufs[i].offset;
6567 }
6568 /*
6569 * Place the fd of the first Ns_FileVec to fd.
6570 */
6571 fd = fbufs[0].fd;
6572
6573 Ns_Log(DriverDebug, "NsWriterQueue: filevec mode, take first fd %d tosend %lu", fd, nsend);
6574 }
6575 }
6576
6577 Ns_Log(DriverDebug, "NsWriterQueue: writer threads %d nsend %" PRIdz"zd" " writersize %" PRIdz"zd",
6578 wrPtr->threads, nsend, wrPtr->writersize);
6579
6580 assert(connPtr->poolPtr != NULL)((void) (0));
6581 connPtr->poolPtr->stats.spool++;
6582
6583 wrSockPtr = (WriterSock *)ns_calloc(1u, sizeof(WriterSock));
6584 wrSockPtr->sockPtr = connPtr->sockPtr;
6585 wrSockPtr->poolPtr = connPtr->poolPtr; /* just for being able to trace back the origin, e.g. list */
6586 wrSockPtr->sockPtr->timeout.sec = 0;
6587 wrSockPtr->flags = connPtr->flags;
6588 wrSockPtr->refCount = 1;
6589 /*
6590 * Take the rate limit from the connection.
6591 */
6592 wrSockPtr->rateLimit = connPtr->rateLimit;
6593 if (wrSockPtr->rateLimit == -1) {
6594 /*
6595 * The value was not specified via connection. Use either the pool
6596 * limit as a base for the computation or fall back to the driver
6597 * default value.
6598 */
6599 if (connPtr->poolPtr->rate.poolLimit > 0) {
6600 /*
6601 * Very optimistic start value, but values will float through via
6602 * bandwidth management.
6603 */
6604 wrSockPtr->rateLimit = connPtr->poolPtr->rate.poolLimit / 2;
6605 } else {
6606 wrSockPtr->rateLimit = wrPtr->rateLimit;
6607 }
6608 }
6609 Ns_Log(WriterDebug, "### Writer(%d): initial rate limit %d KB/s",
6610 wrSockPtr->sockPtr->sock, wrSockPtr->rateLimit);
6611
6612 /*
6613 * Make sure we have proper content length header for
6614 * keep-alive/pipelining.
6615 */
6616 Ns_ConnSetLengthHeader(conn, nsend, (wrSockPtr->flags & NS_CONN_STREAM0x040u) != 0u);
6617
6618 /*
6619 * Flush the headers
6620 */
6621
6622 if ((conn->flags & NS_CONN_SENTHDRS0x010u) == 0u) {
6623 Tcl_DString ds;
6624
6625 Ns_DStringInitTcl_DStringInit(&ds);
6626 Ns_Log(DriverDebug, "### Writer(%d): add header", fd);
6627 conn->flags |= NS_CONN_SENTHDRS0x010u;
6628 (void)Ns_CompleteHeaders(conn, nsend, 0u, &ds);
6629
6630 headerSize = (size_t)Ns_DStringLength(&ds)((&ds)->length);
6631 if (headerSize > 0u) {
6632 wrSockPtr->headerString = ns_strdup(Tcl_DStringValue(&ds)((&ds)->string));
6633 }
6634 Ns_DStringFreeTcl_DStringFree(&ds);
6635 } else {
6636 headerSize = 0u;
6637 }
6638
6639 if (fd != NS_INVALID_FD(-1)) {
6640 /* maybe add mmap support for files (fd != NS_INVALID_FD) */
6641
6642 wrSockPtr->fd = fd;
6643 wrSockPtr->c.file.bufs = fbufs;
6644 wrSockPtr->c.file.nbufs = nfbufs;
6645
6646 Ns_Log(DriverDebug, "### Writer(%d) tosend %" PRIdz"zd" " files %d bufsize %" PRIdz"zd",
6647 fd, nsend, nfbufs, wrPtr->bufsize);
6648
6649 if (unlikely(headerSize >= wrPtr->bufsize)(__builtin_expect((headerSize >= wrPtr->bufsize), 0))) {
6650 /*
6651 * We have a header which is larger than bufsize; place it
6652 * as "leftover" and use the headerString as buffer for file
6653 * reads (rather rare case)
6654 */
6655 wrSockPtr->c.file.buf = (unsigned char *)wrSockPtr->headerString;
6656 wrSockPtr->c.file.maxsize = headerSize;
6657 wrSockPtr->c.file.bufsize = headerSize;
6658 wrSockPtr->headerString = NULL((void*)0);
6659 } else if (headerSize > 0u) {
6660 /*
6661 * We have a header that fits into the bufsize; place it
6662 * as "leftover" at the end of the buffer.
6663 */
6664 wrSockPtr->c.file.buf = ns_malloc(wrPtr->bufsize);
6665 memcpy(wrSockPtr->c.file.buf, wrSockPtr->headerString, headerSize);
6666 wrSockPtr->c.file.bufsize = headerSize;
6667 wrSockPtr->c.file.maxsize = wrPtr->bufsize;
6668 ns_free(wrSockPtr->headerString);
6669 wrSockPtr->headerString = NULL((void*)0);
6670 } else {
6671 assert(wrSockPtr->headerString == NULL)((void) (0));
6672 wrSockPtr->c.file.buf = ns_malloc(wrPtr->bufsize);
6673 wrSockPtr->c.file.maxsize = wrPtr->bufsize;
6674 }
6675 wrSockPtr->c.file.bufoffset = 0;
6676 wrSockPtr->c.file.toRead = nsend;
6677
6678 } else if (bufs != NULL((void*)0)) {
6679 int i, j, headerbufs = (headerSize > 0u ? 1 : 0);
6680
6681 wrSockPtr->fd = NS_INVALID_FD(-1);
6682
6683 if (nbufs+headerbufs < UIO_SMALLIOV8) {
6684 wrSockPtr->c.mem.bufs = wrSockPtr->c.mem.preallocated_bufs;
6685 } else {
6686 Ns_Log(DriverDebug, "NsWriterQueue: alloc %d iovecs", nbufs);
6687 wrSockPtr->c.mem.bufs = ns_calloc((size_t)nbufs + (size_t)headerbufs, sizeof(struct iovec));
6688 }
6689 wrSockPtr->c.mem.nbufs = nbufs+headerbufs;
6690 if (headerbufs != 0) {
6691 wrSockPtr->c.mem.bufs[0].iov_base = wrSockPtr->headerString;
6692 wrSockPtr->c.mem.bufs[0].iov_len = headerSize;
6693 }
6694
6695 if (connPtr->fmap.addr != NULL((void*)0)) {
6696 Ns_Log(DriverDebug, "NsWriterQueue: deliver fmapped %p", (void *)connPtr->fmap.addr);
6697 /*
6698 * Deliver an mmapped file, no need to copy content
6699 */
6700 for (i = 0, j=headerbufs; i < nbufs; i++, j++) {
6701 wrSockPtr->c.mem.bufs[j].iov_base = bufs[i].iov_base;
6702 wrSockPtr->c.mem.bufs[j].iov_len = bufs[i].iov_len;
6703 }
6704 /*
6705 * Make a copy of the fmap structure and make clear that
6706 * we unmap in the writer thread.
6707 */
6708 wrSockPtr->c.mem.fmap = connPtr->fmap;
6709 connPtr->fmap.addr = NULL((void*)0);
6710 /* header string will be freed via wrSockPtr->headerString */
6711
6712 } else {
6713 /*
6714 * Deliver a content from iovec. The lifetime of the
6715 * source is unknown, we have to copy the c.
6716 */
6717 for (i = 0, j=headerbufs; i < nbufs; i++, j++) {
6718 wrSockPtr->c.mem.bufs[j].iov_base = ns_malloc(bufs[i].iov_len);
6719 wrSockPtr->c.mem.bufs[j].iov_len = bufs[i].iov_len;
6720 memcpy(wrSockPtr->c.mem.bufs[j].iov_base, bufs[i].iov_base, bufs[i].iov_len);
6721 }
6722 /* header string will be freed a buf[0] */
6723 wrSockPtr->headerString = NULL((void*)0);
6724 }
6725
6726 } else {
6727 ns_free(wrSockPtr);
6728 return NS_ERROR;
6729 }
6730
6731 /*
6732 * Add header size to total size.
6733 */
6734 nsend += headerSize;
6735
6736
6737 if (connPtr->clientData != NULL((void*)0)) {
6738 wrSockPtr->clientData = ns_strdup(connPtr->clientData);
6739 }
6740 wrSockPtr->startTime = *Ns_ConnStartTime(conn);
6741
6742 /*
6743 * Setup streaming context before sending potentially headers.
6744 */
6745
6746 if ((wrSockPtr->flags & NS_CONN_STREAM0x040u) != 0u) {
6747 wrSockPtr->doStream = NS_WRITER_STREAM_ACTIVE;
6748 assert(connPtr->strWriter == NULL)((void) (0));
6749 /*
6750 * Add a reference to the stream writer to the connection such
6751 * it can efficiently append to a stream when multiple output
6752 * operations happen. The backpointer (from the stream writer
6753 * to the connection is needed to clear the reference to the
6754 * writer in case the writer is deleted. No locks are needed,
6755 * since nobody can share this structure yet.
6756 */
6757 connPtr->strWriter = (NsWriterSock *)wrSockPtr;
6758 wrSockPtr->connPtr = connPtr;
6759 }
6760
6761 /*
6762 * Tell connection, that writer handles the output (including
6763 * closing the connection to the client).
6764 */
6765
6766 connPtr->flags |= NS_CONN_SENT_VIA_WRITER0x400u;
6767 wrSockPtr->keep = (connPtr->keep > 0);
6768 wrSockPtr->size = nsend;
6769 Ns_Log(DriverDebug, "NsWriterQueue NS_CONN_SENT_VIA_WRITER connPtr %p",
6770 (void*)connPtr);
6771
6772 if ((wrSockPtr->flags & NS_CONN_STREAM0x040u) == 0u) {
6773 Ns_Log(DriverDebug, "NsWriterQueue NS_CONN_SENT_VIA_WRITER connPtr %p clear sockPtr %p",
6774 (void*)connPtr, (void*)connPtr->sockPtr);
6775 connPtr->sockPtr = NULL((void*)0);
6776 connPtr->flags |= NS_CONN_CLOSED0x001u;
6777 connPtr->nContentSent = nsend - headerSize;
6778 }
6779
6780 /*
6781 * Get the next writer thread from the list, all writer requests are
6782 * rotated between all writer threads
6783 */
6784
6785 Ns_MutexLock(&wrPtr->lock);
6786 if (wrPtr->curPtr == NULL((void*)0)) {
6787 wrPtr->curPtr = wrPtr->firstPtr;
6788 }
6789 queuePtr = wrPtr->curPtr;
6790 wrPtr->curPtr = wrPtr->curPtr->nextPtr;
6791 Ns_MutexUnlock(&wrPtr->lock);
6792
6793 Ns_Log(WriterDebug, "Writer(%d): started: id=%d fd=%d, "
6794 "size=%" PRIdz"zd" ", flags=%X, rate %d KB/s: %s",
6795 wrSockPtr->sockPtr->sock,
6796 queuePtr->id, wrSockPtr->fd,
6797 nsend, wrSockPtr->flags,
6798 wrSockPtr->rateLimit,
6799 connPtr->request.line);
6800
6801 /*
6802 * Now add new writer socket to the writer thread's queue
6803 */
6804 wrSockPtr->queuePtr = queuePtr;
6805
6806 Ns_MutexLock(&queuePtr->lock);
6807 if (queuePtr->sockPtr == NULL((void*)0)) {
6808 trigger = NS_TRUE1;
6809 }
6810
6811 Push(wrSockPtr, queuePtr->sockPtr)((wrSockPtr)->nextPtr = (queuePtr->sockPtr), (queuePtr->
sockPtr) = (wrSockPtr))
;
6812 Ns_MutexUnlock(&queuePtr->lock);
6813
6814 /*
6815 * Wake up writer thread
6816 */
6817
6818 if (trigger) {
6819 SockTrigger(queuePtr->pipe[1]);
6820 }
6821
6822 return NS_OK;
6823}
6824
6825/*
6826 *----------------------------------------------------------------------
6827 *
6828 * DriverWriterFromObj --
6829 *
6830 * Lookup driver by name and return its DrvWriter. When driverObj is
6831 * NULL, get the driver from the conn.
6832 *
6833 * Results:
6834 * Ns_ReturnCode
6835 *
6836 * Side effects:
6837 * Set error message in interp in case of failure.
6838 *
6839 *----------------------------------------------------------------------
6840 */
6841static Ns_ReturnCode
6842DriverWriterFromObj(Tcl_Interp *interp, Tcl_Obj *driverObj, const Ns_Conn *conn, DrvWriter **wrPtrPtr) {
6843 Driver *drvPtr;
6844 const char *driverName = NULL((void*)0);
6845 int driverNameLen = 0;
6846 DrvWriter *wrPtr = NULL((void*)0);
6847 Ns_ReturnCode result;
6848
6849 /*
6850 * If no driver is provided, take the current driver. The caller has
6851 * to make sure that in cases, where no driver is specified, the
6852 * command is run in a connection thread.
6853 */
6854 if (driverObj == NULL((void*)0)) {
6855 if (conn != NULL((void*)0)) {
6856 driverName = Ns_ConnDriverName(conn);
6857 driverNameLen = (int)strlen(driverName);
6858 }
6859 } else {
6860 driverName = Tcl_GetStringFromObj(driverObj, &driverNameLen);
6861 }
6862
6863 if (driverName != NULL((void*)0)) {
6864
6865 for (drvPtr = firstDrvPtr; drvPtr != NULL((void*)0); drvPtr = drvPtr->nextPtr) {
6866 if (strncmp(driverName, drvPtr->threadName, (size_t)driverNameLen) == 0) {
6867 if (drvPtr->writer.firstPtr != NULL((void*)0)) {
6868 wrPtr = &drvPtr->writer;
6869 }
6870 break;
6871 }
6872 }
6873 }
6874 if (unlikely(wrPtr == NULL)(__builtin_expect((wrPtr == ((void*)0)), 0))) {
6875 Ns_TclPrintfResult(interp, "no writer configured for a driver with name %s",
6876 driverName);
6877 result = NS_ERROR;
6878 } else {
6879 *wrPtrPtr = wrPtr;
6880 result = NS_OK;
6881 }
6882
6883 return result;
6884}
6885
6886
6887/*
6888 *----------------------------------------------------------------------
6889 *
6890 * WriterSubmitObjCmd - subcommand of NsTclWriterObjCmd --
6891 *
6892 * Implements "ns_writer submit" command.
6893 * Send the provided data to the client.
6894 *
6895 * Results:
6896 * Standard Tcl result.
6897 *
6898 * Side effects:
6899 * None.
6900 *
6901 *----------------------------------------------------------------------
6902 */
6903static int
6904WriterSubmitObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
6905{
6906 int result = TCL_OK0;
6907 Ns_Conn *conn;
6908 Tcl_Obj *dataObj;
6909 Ns_ObjvSpec args[] = {
6910 {"data", Ns_ObjvObj, &dataObj, NULL((void*)0)},
6911 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
6912 };
6913
6914 if (Ns_ParseObjv(NULL((void*)0), args, interp, 2, objc, objv) != NS_OK
6915 || NsConnRequire(interp, NS_CONN_REQUIRE_ALL0x0007u, &conn) != NS_OK) {
6916 result = TCL_ERROR1;
6917
6918 } else {
6919 int size;
6920 unsigned char *data = Tcl_GetByteArrayFromObj(dataObj, &size);
6921
6922 if (data != NULL((void*)0)) {
6923 struct iovec vbuf;
6924 Ns_ReturnCode status;
6925
6926 vbuf.iov_base = (void *)data;
6927 vbuf.iov_len = (size_t)size;
6928
6929 status = NsWriterQueue(conn, (size_t)size, NULL((void*)0), NULL((void*)0), NS_INVALID_FD(-1),
6930 &vbuf, 1, NULL((void*)0), 0, NS_TRUE1);
6931 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(status == NS_OK ? 1 : 0)Tcl_NewIntObj((status == NS_OK ? 1 : 0)!=0));
6932 }
6933 }
6934 return result;
6935}
6936
6937/*
6938 *----------------------------------------------------------------------
6939 *
6940 * WriterCheckInputParams -
6941 *
6942 * Helper command for WriterSubmitFileObjCmd and WriterSubmitFilesObjCmd
6943 * to check validity of filename, offset and size.
6944 *
6945 * Results:
6946 * Standard Tcl result. Returns on success also fd and nrbytes.
6947 *
6948 * Side effects:
6949 * None.
6950 *
6951 *----------------------------------------------------------------------
6952 */
6953static int
6954WriterCheckInputParams(Tcl_Interp *interp, const char *filenameString,
6955 size_t size, off_t offset,
6956 int *fdPtr, size_t *nrbytesPtr)
6957{
6958 int result = TCL_OK0, rc;
6959 struct stat st;
6960
6961 Ns_Log(DriverDebug, "WriterCheckInputParams %s offset %" PROTd"l" "d" " size %" PRIdz"zd",
6962 filenameString, offset, size);
6963
6964 /*
6965 * Use stat() call to obtain information about the actual file to check
6966 * later the plausibility of the parameters.
6967 */
6968 rc = stat(filenameString, &st);
6969 if (unlikely(rc != 0)(__builtin_expect((rc != 0), 0))) {
6970 Ns_TclPrintfResult(interp, "file does not exist '%s'", filenameString);
6971 result = TCL_ERROR1;
6972
6973 } else {
6974 size_t nrbytes = 0u;
6975 int fd;
6976
6977 /*
6978 * Try to open the file and check offset and size parameters.
6979 */
6980 fd = ns_openopen(filenameString, O_RDONLY00 | O_CLOEXEC02000000, 0);
6981
6982 if (unlikely(fd == NS_INVALID_FD)(__builtin_expect((fd == (-1)), 0))) {
6983 Ns_TclPrintfResult(interp, "could not open file '%s'", filenameString);
6984 result = TCL_ERROR1;
6985
6986 } else if (unlikely(offset > st.st_size)(__builtin_expect((offset > st.st_size), 0)) || offset < 0) {
6987 Ns_TclPrintfResult(interp, "offset must be a positive value less or equal filesize");
6988 result = TCL_ERROR1;
6989
6990 } else if (size > 0) {
6991 if (unlikely((off_t)size + offset > st.st_size)(__builtin_expect(((off_t)size + offset > st.st_size), 0))) {
6992 Ns_TclPrintfResult(interp, "offset + size must be less or equal filesize");
6993 result = TCL_ERROR1;
6994 } else {
6995 nrbytes = (size_t)size;
6996 }
6997 } else {
6998 nrbytes = (size_t)st.st_size - (size_t)offset;
6999 }
7000
7001 /*
7002 * When an offset is provide, jump to this offset.
7003 */
7004 if (offset > 0 && result == TCL_OK0) {
7005 if (ns_lseeklseek(fd, (off_t)offset, SEEK_SET0) == -1) {
7006 Ns_TclPrintfResult(interp, "cannot seek to position %ld", (long)offset);
7007 result = TCL_ERROR1;
7008 }
7009 }
7010
7011 if (result == TCL_OK0) {
7012 *fdPtr = fd;
7013 *nrbytesPtr = nrbytes;
7014
7015 } else if (fd != NS_INVALID_FD(-1)) {
7016 /*
7017 * On invalid parameters, close the fd.
7018 */
7019 ns_closeclose(fd);
7020 }
7021 }
7022
7023 return result;
7024}
7025
7026/*
7027 *----------------------------------------------------------------------
7028 *
7029 * WriterSubmitFileObjCmd - subcommand of NsTclWriterObjCmd --
7030 *
7031 * Implements "ns_writer submitfile" command.
7032 * Send the provided file to the client.
7033 *
7034 * Results:
7035 * Standard Tcl result.
7036 *
7037 * Side effects:
7038 * None.
7039 *
7040 *----------------------------------------------------------------------
7041 */
7042static int
7043WriterSubmitFileObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
7044{
7045 int result = TCL_OK0;
7046 Ns_Conn *conn;
7047 char *fileNameString;
7048 int headers = 0;
7049 Tcl_WideInt offset = 0, size = 0;
7050 Ns_ObjvValueRange offsetRange = {0, LLONG_MAX9223372036854775807LL};
7051 Ns_ObjvValueRange sizeRange = {1, LLONG_MAX9223372036854775807LL};
7052 Ns_ObjvSpec lopts[] = {
7053 {"-headers", Ns_ObjvBool, &headers, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
7054 {"-offset", Ns_ObjvMemUnit, &offset, &offsetRange},
7055 {"-size", Ns_ObjvMemUnit, &size, &sizeRange},
7056 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
7057 };
7058 Ns_ObjvSpec args[] = {
7059 {"file", Ns_ObjvString, &fileNameString, NULL((void*)0)},
7060 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
7061 };
7062
7063 if (unlikely(Ns_ParseObjv(lopts, args, interp, 2, objc, objv) != NS_OK)(__builtin_expect((Ns_ParseObjv(lopts, args, interp, 2, objc,
objv) != NS_OK), 0))
7064 || NsConnRequire(interp, NS_CONN_REQUIRE_ALL0x0007u, &conn) != NS_OK) {
7065 result = TCL_ERROR1;
7066
7067 } else if (unlikely( Ns_ConnSockPtr(conn) == NULL )(__builtin_expect((Ns_ConnSockPtr(conn) == ((void*)0)), 0))) {
7068 Ns_Log(Warning,
7069 "NsWriterQueue: called without valid sockPtr, maybe connection already closed");
7070 Ns_TclPrintfResult(interp, "0");
7071 result = TCL_OK0;
7072
7073 } else {
7074 size_t nrbytes = 0u;
7075 int fd = NS_INVALID_FD(-1);
7076
7077 result = WriterCheckInputParams(interp, fileNameString,
7078 (size_t)size, (off_t)offset,
7079 &fd, &nrbytes);
7080
7081 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
7082 Ns_ReturnCode status;
7083
7084 /*
7085 * The caller requested that we build required headers
7086 */
7087
7088 if (headers != 0) {
7089 Ns_ConnSetTypeHeader(conn, Ns_GetMimeType(fileNameString));
7090 }
7091 status = NsWriterQueue(conn, nrbytes, NULL((void*)0), NULL((void*)0), fd, NULL((void*)0), 0, NULL((void*)0), 0, NS_TRUE1);
7092 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(status == NS_OK ? 1 : 0)Tcl_NewIntObj((status == NS_OK ? 1 : 0)!=0));
7093
7094 if (fd != NS_INVALID_FD(-1)) {
7095 (void) ns_closeclose(fd);
7096 } else {
7097 Ns_Log(Warning, "WriterSubmitFileObjCmd called with invalid fd");
7098 }
7099
7100 } else if (fd != NS_INVALID_FD(-1)) {
7101 (void) ns_closeclose(fd);
7102 }
7103 }
7104
7105 return result;
7106}
7107
7108/*
7109 *----------------------------------------------------------------------
7110 *
7111 * WriterGetMemunitFromDict --
7112 *
7113 * Helper function to obtain a memory unit from a dict structure,
7114 * optionally checking the value range.
7115 *
7116 * Results:
7117 * Standard Tcl result.
7118 *
7119 * Side effects:
7120 * On errors, an error message is left in the interpreter.
7121 *
7122 *----------------------------------------------------------------------
7123 */
7124static int
7125WriterGetMemunitFromDict(Tcl_Interp *interp, Tcl_Obj *dictObj, Tcl_Obj *keyObj,
7126 const Ns_ObjvValueRange *rangePtr, Tcl_WideInt *valuePtr)
7127{
7128 Tcl_Obj *intObj = NULL((void*)0);
7129 int result;
7130
7131 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
7132 NS_NONNULL_ASSERT(dictObj != NULL)((void) (0));
7133 NS_NONNULL_ASSERT(keyObj != NULL)((void) (0));
7134 NS_NONNULL_ASSERT(valuePtr != NULL)((void) (0));
7135
7136 result = Tcl_DictObjGet(interp, dictObj, keyObj, &intObj);
7137 if (result == TCL_OK0 && intObj != NULL((void*)0)) {
7138 result = Ns_TclGetMemUnitFromObj(interp, intObj, valuePtr);
7139 if (result == TCL_OK0 && rangePtr != NULL((void*)0)) {
7140 result = Ns_CheckWideRange(interp, Tcl_GetString(keyObj), rangePtr, *valuePtr);
7141 }
7142 }
7143
7144 return result;
7145}
7146
7147
7148/*
7149 *----------------------------------------------------------------------
7150 *
7151 * WriterSubmitFilesObjCmd - subcommand of NsTclWriterObjCmd --
7152 *
7153 * Implements "ns_writer submitfiles" command. Send the provided files
7154 * to the client. "files" are provided as a list of dicts, where every
7155 * dict must contain a "filename" element and can contain an "-offset"
7156 * and/or a "-length" element.
7157 *
7158 * Results:
7159 * Standard Tcl result.
7160 *
7161 * Side effects:
7162 * None.
7163 *
7164 *----------------------------------------------------------------------
7165 */
7166static int
7167WriterSubmitFilesObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
7168{
7169 int result = TCL_OK0;
7170 Ns_Conn *conn;
7171 int headers = 0, nrFiles;
7172 Tcl_Obj *filesObj = NULL((void*)0), **fileObjv;
7173 Ns_ObjvSpec lopts[] = {
7174 {"-headers", Ns_ObjvBool, &headers, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
7175 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
7176 };
7177 Ns_ObjvSpec args[] = {
7178 {"files", Ns_ObjvObj, &filesObj, NULL((void*)0)},
7179 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
7180 };
7181
7182 if (unlikely(Ns_ParseObjv(lopts, args, interp, 2, objc, objv) != NS_OK)(__builtin_expect((Ns_ParseObjv(lopts, args, interp, 2, objc,
objv) != NS_OK), 0))
7183 || NsConnRequire(interp, NS_CONN_REQUIRE_ALL0x0007u, &conn) != NS_OK) {
7184 result = TCL_ERROR1;
7185
7186 } else if (unlikely( Ns_ConnSockPtr(conn) == NULL )(__builtin_expect((Ns_ConnSockPtr(conn) == ((void*)0)), 0))) {
7187 Ns_Log(Warning,
7188 "NsWriterQueue: called without valid sockPtr, "
7189 "maybe connection already closed");
7190 Ns_TclPrintfResult(interp, "0");
7191 result = TCL_OK0;
7192
7193 } else if (Tcl_ListObjGetElements(interp, filesObj, &nrFiles, &fileObjv) != TCL_OK0) {
7194 Ns_TclPrintfResult(interp, "not a valid list of files: '%s'", Tcl_GetString(filesObj));
7195 result = TCL_ERROR1;
7196
7197 } else if (nrFiles == 0) {
7198 Ns_TclPrintfResult(interp, "The provided list has to contain at least one file spec");
7199 result = TCL_ERROR1;
7200
7201 } else {
7202 size_t totalbytes = 0u, i;
7203 Tcl_Obj *keys[3], *filenameObj = NULL((void*)0);
7204 Ns_FileVec *filebufs;
7205 const char *firstFilenameString = NULL((void*)0);
7206 Ns_ObjvValueRange offsetRange = {0, LLONG_MAX9223372036854775807LL};
7207 Ns_ObjvValueRange sizeRange = {1, LLONG_MAX9223372036854775807LL};
7208
7209 filebufs = (Ns_FileVec *)ns_calloc((size_t)nrFiles, sizeof(Ns_FileVec));
7210 keys[0] = Tcl_NewStringObj("filename", 8);
7211 keys[1] = Tcl_NewStringObj("-offset", 7);
7212 keys[2] = Tcl_NewStringObj("-size", 5);
7213
7214 Tcl_IncrRefCount(keys[0])++(keys[0])->refCount;
7215 Tcl_IncrRefCount(keys[1])++(keys[1])->refCount;
7216 Tcl_IncrRefCount(keys[2])++(keys[2])->refCount;
7217
7218 for (i = 0u; i < (size_t)nrFiles; i++) {
7219 filebufs[i].fd = NS_INVALID_FD(-1);
7220 }
7221
7222 /*
7223 * Iterate over the list of dicts.
7224 */
7225 for (i = 0u; i < (size_t)nrFiles; i++) {
7226 Tcl_WideInt offset = 0, size = 0;
7227 int rc, fd = NS_INVALID_FD(-1);
7228 const char *filenameString;
7229 size_t nrbytes;
7230
7231 /*
7232 * Get required "filename" element.
7233 */
7234 filenameObj = NULL((void*)0);
7235 rc = Tcl_DictObjGet(interp, fileObjv[i], keys[0], &filenameObj);
7236 if (rc != TCL_OK0 || filenameObj == NULL((void*)0)) {
7237 Ns_TclPrintfResult(interp, "missing filename in dict '%s'",
7238 Tcl_GetString(fileObjv[i]));
7239 result = TCL_ERROR1;
7240 break;
7241 }
7242
7243 filenameString = Tcl_GetString(filenameObj);
7244 if (firstFilenameString == NULL((void*)0)) {
7245 firstFilenameString = filenameString;
7246 }
7247
7248 /*
7249 * Get optional "-offset" and "-size" elements.
7250 */
7251 if (WriterGetMemunitFromDict(interp, fileObjv[i], keys[1], &offsetRange, &offset) != TCL_OK0) {
7252 result = TCL_ERROR1;
7253 break;
7254 }
7255 if (WriterGetMemunitFromDict(interp, fileObjv[i], keys[2], &sizeRange, &size) != TCL_OK0) {
7256 result = TCL_ERROR1;
7257 break;
7258 }
7259
7260 /*
7261 * Check validity of the provided values
7262 */
7263 result = WriterCheckInputParams(interp, Tcl_GetString(filenameObj),
7264 (size_t)size, (off_t)offset,
7265 &fd, &nrbytes);
7266 if (result != TCL_OK0) {
7267 break;
7268 }
7269
7270 filebufs[i].fd = fd;
7271 filebufs[i].offset = (off_t)offset;
7272 filebufs[i].length = nrbytes;
7273
7274 totalbytes = totalbytes + (size_t)nrbytes;
7275 }
7276 Tcl_DecrRefCount(keys[0])do { Tcl_Obj *_objPtr = (keys[0]); if (_objPtr->refCount--
<= 1) { TclFreeObj(_objPtr); } } while(0)
;
7277 Tcl_DecrRefCount(keys[1])do { Tcl_Obj *_objPtr = (keys[1]); if (_objPtr->refCount--
<= 1) { TclFreeObj(_objPtr); } } while(0)
;
7278 Tcl_DecrRefCount(keys[2])do { Tcl_Obj *_objPtr = (keys[2]); if (_objPtr->refCount--
<= 1) { TclFreeObj(_objPtr); } } while(0)
;
7279
7280 /*
7281 * If everything is ok, submit the request to the writer queue.
7282 */
7283 if (result == TCL_OK0) {
7284 Ns_ReturnCode status;
7285
7286 if (headers != 0 && firstFilenameString != NULL((void*)0)) {
7287 Ns_ConnSetTypeHeader(conn, Ns_GetMimeType(firstFilenameString));
7288 }
7289 status = NsWriterQueue(conn, totalbytes, NULL((void*)0), NULL((void*)0), NS_INVALID_FD(-1), NULL((void*)0), 0,
7290 filebufs, nrFiles, NS_TRUE1);
7291 /*
7292 * Provide a soft error like for "ns_writer submitfile".
7293 */
7294 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(status == NS_OK ? 1 : 0)Tcl_NewIntObj((status == NS_OK ? 1 : 0)!=0));
7295 }
7296
7297 /*
7298 * The NsWriterQueue() API makes the usual duplicates of the file
7299 * descriptors and the Ns_FileVec structure, so we have to cleanup
7300 * here.
7301 */
7302 for (i = 0u; i < (size_t)nrFiles; i++) {
7303 if (filebufs[i].fd != NS_INVALID_FD(-1)) {
7304 (void) ns_closeclose(filebufs[i].fd);
7305 }
7306 }
7307 ns_free(filebufs);
7308
7309 }
7310
7311 return result;
7312}
7313
7314
7315
7316/*
7317 *----------------------------------------------------------------------
7318 *
7319 * WriterListObjCmd - subcommand of NsTclWriterObjCmd --
7320 *
7321 * Implements "ns_writer list" command.
7322 * List the current writer jobs.
7323 *
7324 * Results:
7325 * Standard Tcl result.
7326 *
7327 * Side effects:
7328 * None.
7329 *
7330 *----------------------------------------------------------------------
7331 */
7332static int
7333WriterListObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
7334{
7335 int result = TCL_OK0;
7336 NsServer *servPtr = NULL((void*)0);
7337 Ns_ObjvSpec lopts[] = {
7338 {"-server", Ns_ObjvServer, &servPtr, NULL((void*)0)},
7339 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
7340 };
7341
7342 if (unlikely(Ns_ParseObjv(lopts, NULL, interp, 2, objc, objv) != NS_OK)(__builtin_expect((Ns_ParseObjv(lopts, ((void*)0), interp, 2,
objc, objv) != NS_OK), 0))
) {
7343 result = TCL_ERROR1;
7344
7345 } else {
7346 Tcl_DString ds, *dsPtr = &ds;
7347 const Driver *drvPtr;
7348 SpoolerQueue *queuePtr;
7349
7350 Tcl_DStringInit(dsPtr);
7351
7352 for (drvPtr = firstDrvPtr; drvPtr != NULL((void*)0); drvPtr = drvPtr->nextPtr) {
7353 const DrvWriter *wrPtr;
7354
7355 /*
7356 * If server was specified, list only results from this server.
7357 */
7358 if (servPtr != NULL((void*)0) && servPtr != drvPtr->servPtr) {
7359 continue;
7360 }
7361
7362 wrPtr = &drvPtr->writer;
7363 queuePtr = wrPtr->firstPtr;
7364 while (queuePtr != NULL((void*)0)) {
7365 const WriterSock *wrSockPtr;
7366
7367 Ns_MutexLock(&queuePtr->lock);
7368 wrSockPtr = queuePtr->curPtr;
7369 while (wrSockPtr != NULL((void*)0)) {
7370 char ipString[NS_IPADDR_SIZE46];
7371 struct Sock *sockPtr = wrSockPtr->sockPtr;
7372
7373 if (nsconf.reverseproxymode
7374 && ((struct sockaddr *)&sockPtr->clientsa)->sa_family != 0) {
7375 ns_inet_ntop((struct sockaddr *)&sockPtr->clientsa, ipString, sizeof(ipString));
7376 } else {
7377 ns_inet_ntop((struct sockaddr *)&sockPtr->sa, ipString, sizeof(ipString));
7378 }
7379
7380 (void) Ns_DStringNAppendTcl_DStringAppend(dsPtr, "{", 1);
7381 (void) Ns_DStringAppendTime(dsPtr, &wrSockPtr->startTime);
7382 (void) Ns_DStringNAppendTcl_DStringAppend(dsPtr, " ", 1);
7383 (void) Ns_DStringAppend(dsPtr, queuePtr->threadName)Tcl_DStringAppend((dsPtr), (queuePtr->threadName), -1);
7384 (void) Ns_DStringNAppendTcl_DStringAppend(dsPtr, " ", 1);
7385 (void) Ns_DStringAppend(dsPtr, drvPtr->threadName)Tcl_DStringAppend((dsPtr), (drvPtr->threadName), -1);
7386 (void) Ns_DStringNAppendTcl_DStringAppend(dsPtr, " ", 1);
7387 (void) Ns_DStringAppend(dsPtr, NsPoolName(wrSockPtr->poolPtr->pool))Tcl_DStringAppend((dsPtr), (NsPoolName(wrSockPtr->poolPtr->
pool)), -1)
;
7388 (void) Ns_DStringNAppendTcl_DStringAppend(dsPtr, " ", 1);
7389 (void) Ns_DStringAppend(dsPtr, ipString)Tcl_DStringAppend((dsPtr), (ipString), -1);
7390 (void) Ns_DStringPrintf(dsPtr, " %d %" PRIdz"zd" " %" TCL_LL_MODIFIER"l" "d %d %d ",
7391 wrSockPtr->fd,
7392 wrSockPtr->size,
7393 wrSockPtr->nsent,
7394 wrSockPtr->currentRate,
7395 wrSockPtr->rateLimit);
7396 (void) Ns_DStringAppendElementTcl_DStringAppendElement(dsPtr,
7397 (wrSockPtr->clientData != NULL((void*)0))
7398 ? wrSockPtr->clientData
7399 : NS_EMPTY_STRING);
7400 (void) Ns_DStringNAppendTcl_DStringAppend(dsPtr, "} ", 2);
7401 wrSockPtr = wrSockPtr->nextPtr;
7402 }
7403 Ns_MutexUnlock(&queuePtr->lock);
7404 queuePtr = queuePtr->nextPtr;
7405 }
7406 }
7407 Tcl_DStringResult(interp, &ds);
7408 }
7409 return result;
7410}
7411
7412/*
7413 *----------------------------------------------------------------------
7414 *
7415 * WriterSizeObjCmd - subcommand of NsTclWriterObjCmd --
7416 *
7417 * Implements "ns_writer size" command.
7418 * Sets or queries size limit for sending via writer.
7419 *
7420 * Results:
7421 * Standard Tcl result.
7422 *
7423 * Side effects:
7424 * None.
7425 *
7426 *----------------------------------------------------------------------
7427 */
7428static int
7429WriterSizeObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
7430{
7431 int result = TCL_OK0;
7432 Tcl_Obj *driverObj = NULL((void*)0);
7433 Ns_Conn *conn = NULL((void*)0);
7434 Tcl_WideInt intValue = -1;
7435 const char *firstArgString;
7436 Ns_ObjvValueRange range = {1024, INT_MAX2147483647};
7437 Ns_ObjvSpec *opts, optsNew[] = {
7438 {"-driver", Ns_ObjvObj, &driverObj, NULL((void*)0)},
7439 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
7440 };
7441 Ns_ObjvSpec *args, argsNew[] = {
7442 {"?value", Ns_ObjvMemUnit, &intValue, &range},
7443 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
7444 };
7445 Ns_ObjvSpec argsLegacy[] = {
7446 {"driver", Ns_ObjvObj, &driverObj, NULL((void*)0)},
7447 {"?value", Ns_ObjvMemUnit, &intValue, &range},
7448 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
7449 };
7450
7451 firstArgString = objc > 2 ? Tcl_GetString(objv[2]) : NULL((void*)0);
7452 if (firstArgString != NULL((void*)0)) {
7453 if (*firstArgString != '-'
7454 && ((objc == 3 && CHARTYPE(digit, *firstArgString)(((*__ctype_b_loc ())[(int) (((int)((unsigned char)(*firstArgString
))))] & (unsigned short int) _ISdigit))
== 0) ||
7455 objc == 4)) {
7456 args = argsLegacy;
7457 opts = NULL((void*)0);
7458 Ns_LogDeprecated(objv, objc, "ns_writer size ?-driver drv? ?size?", NULL((void*)0));
7459 } else {
7460 args = argsNew;
7461 opts = optsNew;
7462 }
7463 } else {
7464 args = argsNew;
7465 opts = optsNew;
7466 }
7467
7468 if (Ns_ParseObjv(opts, args, interp, 2, objc, objv) != NS_OK) {
7469 result = TCL_ERROR1;
7470
7471 } else if ((driverObj == NULL((void*)0))
7472 && NsConnRequire(interp, NS_CONN_REQUIRE_ALL0x0007u, &conn) != NS_OK) {
7473 result = TCL_ERROR1;
7474
7475 } else {
7476 DrvWriter *wrPtr;
7477
7478 if (DriverWriterFromObj(interp, driverObj, conn, &wrPtr) != NS_OK) {
7479 result = TCL_ERROR1;
7480
7481 } else if (intValue != -1) {
7482 /*
7483 * The optional argument was provided.
7484 */
7485 wrPtr->writersize = (size_t)intValue;
7486 }
7487
7488 if (result == TCL_OK0) {
7489 Tcl_SetObjResult(interp, Tcl_NewIntObj((int)wrPtr->writersize));
7490 }
7491 }
7492
7493 return result;
7494}
7495
7496/*
7497 *----------------------------------------------------------------------
7498 *
7499 * WriterStreamingObjCmd - subcommand of NsTclWriterObjCmd --
7500 *
7501 * Implements "ns_writer streaming" command.
7502 * Sets or queries streaming state of the writer.
7503 *
7504 * Results:
7505 * Standard Tcl result.
7506 *
7507 * Side effects:
7508 * None.
7509 *
7510 *----------------------------------------------------------------------
7511 */
7512static int
7513WriterStreamingObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp,
7514 int objc, Tcl_Obj *const* objv)
7515{
7516 int boolValue = -1, result = TCL_OK0;
7517 Tcl_Obj *driverObj = NULL((void*)0);
7518 Ns_Conn *conn = NULL((void*)0);
7519 const char *firstArgString;
7520 Ns_ObjvSpec *opts, optsNew[] = {
7521 {"-driver", Ns_ObjvObj, &driverObj, NULL((void*)0)},
7522 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
7523 };
7524 Ns_ObjvSpec *args, argsNew[] = {
7525 {"?value", Ns_ObjvBool, &boolValue, NULL((void*)0)},
7526 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
7527 };
7528 Ns_ObjvSpec argsLegacy[] = {
7529 {"driver", Ns_ObjvObj, &driverObj, NULL((void*)0)},
7530 {"?value", Ns_ObjvBool, &boolValue, NULL((void*)0)},
7531 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
7532 };
7533
7534 firstArgString = objc > 2 ? Tcl_GetString(objv[2]) : NULL((void*)0);
7535 if (firstArgString != NULL((void*)0)) {
7536 int argValue;
7537 if (*firstArgString != '-'
7538 && ((objc == 3 && Tcl_ExprBoolean(interp, firstArgString, &argValue) == TCL_OK0) ||
7539 objc == 4)) {
7540 args = argsLegacy;
7541 opts = NULL((void*)0);
7542 Ns_LogDeprecated(objv, objc, "ns_writer streaming ?-driver drv? ?value?", NULL((void*)0));
7543 } else {
7544 args = argsNew;
7545 opts = optsNew;
7546 }
7547 } else {
7548 args = argsNew;
7549 opts = optsNew;
7550 }
7551
7552 if (Ns_ParseObjv(opts, args, interp, 2, objc, objv) != NS_OK) {
7553 result = TCL_ERROR1;
7554
7555 } else if ((driverObj == NULL((void*)0))
7556 && NsConnRequire(interp, NS_CONN_REQUIRE_ALL0x0007u, &conn) != NS_OK) {
7557 result = TCL_ERROR1;
7558
7559 } else {
7560 DrvWriter *wrPtr;
7561
7562 if (DriverWriterFromObj(interp, driverObj, conn, &wrPtr) != NS_OK) {
7563 result = TCL_ERROR1;
7564
7565 } else if (boolValue != -1) {
7566 /*
7567 * The optional argument was provided.
7568 */
7569 wrPtr->doStream = (boolValue == 1 ? NS_WRITER_STREAM_ACTIVE : NS_WRITER_STREAM_NONE);
7570 }
7571
7572 if (result == TCL_OK0) {
7573 Tcl_SetObjResult(interp, Tcl_NewIntObj(wrPtr->doStream == NS_WRITER_STREAM_ACTIVE ? 1 : 0));
7574 }
7575 }
7576
7577 return result;
7578}
7579
7580
7581/*
7582 *----------------------------------------------------------------------
7583 *
7584 * NsTclWriterObjCmd --
7585 *
7586 * Implements "ns_writer". This command is used for submitting data to
7587 * the writer threads and to configure and query the state of the writer
7588 * threads at run time.
7589 *
7590 * Results:
7591 * Standard Tcl result.
7592 *
7593 * Side effects:
7594 * None.
7595 *
7596 *----------------------------------------------------------------------
7597 */
7598int
7599NsTclWriterObjCmd(ClientData clientData, Tcl_Interp *interp,
7600 int objc, Tcl_Obj *const* objv)
7601{
7602 const Ns_SubCmdSpec subcmds[] = {
7603 {"list", WriterListObjCmd},
7604 {"size", WriterSizeObjCmd},
7605 {"streaming", WriterStreamingObjCmd},
7606 {"submit", WriterSubmitObjCmd},
7607 {"submitfile", WriterSubmitFileObjCmd},
7608 {"submitfiles", WriterSubmitFilesObjCmd},
7609 {NULL((void*)0), NULL((void*)0)}
7610 };
7611
7612 return Ns_SubcmdObjv(subcmds, clientData, interp, objc, objv);
7613}
7614
7615/*
7616 *======================================================================
7617 * Async (log) writer: Write asynchronously to a disk
7618 *======================================================================
7619 */
7620
7621
7622/*
7623 *----------------------------------------------------------------------
7624 *
7625 * NsAsyncWriterQueueEnable --
7626 *
7627 * Enable async writing and start the AsyncWriterThread if
7628 * necessary
7629 *
7630 * Results:
7631 * None.
7632 *
7633 * Side effects:
7634 * Potentially starting a thread and set "stopped" to NS_FALSE.
7635 *
7636 *----------------------------------------------------------------------
7637 */
7638void
7639NsAsyncWriterQueueEnable(void)
7640{
7641 if (Ns_ConfigBool(NS_GLOBAL_CONFIG_PARAMETERS"ns/parameters", "asynclogwriter", NS_FALSE0) == NS_TRUE1) {
7642 SpoolerQueue *queuePtr;
7643
7644 /*
7645 * In case, the async writer has not started, the static variable
7646 * asyncWriter is NULL.
7647 */
7648 if (asyncWriter == NULL((void*)0)) {
7649 Ns_MutexLock(&reqLock);
7650 if (likely(asyncWriter == NULL)(__builtin_expect((asyncWriter == ((void*)0)), 1))) {
7651 /*
7652 * Allocate and initialize writer thread context.
7653 */
7654 asyncWriter = ns_calloc(1u, sizeof(AsyncWriter));
7655 Ns_MutexUnlock(&reqLock);
7656 Ns_MutexSetName2(&asyncWriter->lock, "ns:driver", "async-writer");
7657 /*
7658 * Allocate and initialize a Spooler Queue for this thread.
7659 */
7660 queuePtr = ns_calloc(1u, sizeof(SpoolerQueue));
7661 Ns_MutexSetName2(&queuePtr->lock, "ns:driver:async-writer", "queue");
7662 asyncWriter->firstPtr = queuePtr;
7663 /*
7664 * Start the spooler queue
7665 */
7666 SpoolerQueueStart(queuePtr, AsyncWriterThread);
7667
7668 } else {
7669 Ns_MutexUnlock(&reqLock);
7670 }
7671 }
7672
7673
7674 assert(asyncWriter != NULL)((void) (0));
7675 queuePtr = asyncWriter->firstPtr;
7676 assert(queuePtr != NULL)((void) (0));
7677
7678 Ns_MutexLock(&queuePtr->lock);
7679 queuePtr->stopped = NS_FALSE0;
7680 Ns_MutexUnlock(&queuePtr->lock);
7681 }
7682}
7683
7684/*
7685 *----------------------------------------------------------------------
7686 *
7687 * NsAsyncWriterQueueDisable --
7688 *
7689 * Disable async writing but don't touch the writer thread.
7690 *
7691 * Results:
7692 * None.
7693 *
7694 * Side effects:
7695 * Disable async writing by setting stopped to 1.
7696 *
7697 *----------------------------------------------------------------------
7698 */
7699void
7700NsAsyncWriterQueueDisable(bool_Bool shutdown)
7701{
7702 if (asyncWriter != NULL((void*)0)) {
7703 SpoolerQueue *queuePtr = asyncWriter->firstPtr;
7704 Ns_Time timeout;
7705
7706 assert(queuePtr != NULL)((void) (0));
7707
7708 Ns_GetTime(&timeout);
7709 Ns_IncrTime(&timeout, nsconf.shutdowntimeout.sec, nsconf.shutdowntimeout.usec);
7710
7711 Ns_MutexLock(&queuePtr->lock);
7712 queuePtr->stopped = NS_TRUE1;
7713 queuePtr->shutdown = shutdown;
7714
7715 /*
7716 * Trigger the AsyncWriter Thread to drain the spooler queue.
7717 */
7718 SockTrigger(queuePtr->pipe[1]);
7719 (void)Ns_CondTimedWait(&queuePtr->cond, &queuePtr->lock, &timeout);
7720
7721 Ns_MutexUnlock(&queuePtr->lock);
7722
7723 if (shutdown) {
7724 ns_free(queuePtr);
7725 ns_free(asyncWriter);
7726 asyncWriter = NULL((void*)0);
7727 }
7728 }
7729}
7730
7731
7732/*
7733 *----------------------------------------------------------------------
7734 *
7735 * NsAsyncWrite --
7736 *
7737 * Perform an asynchronous write operation via a writer thread in
7738 * case a writer thread is configured and running. The intention
7739 * of the asynchronous write operations is to reduce latencies in
7740 * connection threads.
7741 *
7742 * Results:
7743 * NS_OK, when write was performed via writer thread,
7744 * NS_ERROR otherwise (but data is written).
7745 *
7746 * Side effects:
7747 * I/O Operation.
7748 *
7749 *----------------------------------------------------------------------
7750 */
7751Ns_ReturnCode
7752NsAsyncWrite(int fd, const char *buffer, size_t nbyte)
7753{
7754 Ns_ReturnCode returnCode = NS_OK;
7755
7756 NS_NONNULL_ASSERT(buffer != NULL)((void) (0));
7757
7758 /*
7759 * If the async writer has not started or is deactivated, behave like a
7760 * ns_write() command. If the ns_write() fails, we can't do much, since
7761 * the writing of an error message to the log might bring us into an
7762 * infinite loop. So we print simple to stderr.
7763 */
7764 if (asyncWriter == NULL((void*)0) || asyncWriter->firstPtr->stopped) {
7765 ssize_t written = ns_writewrite(fd, buffer, nbyte);
7766
7767 if (unlikely(written != (ssize_t)nbyte)(__builtin_expect((written != (ssize_t)nbyte), 0))) {
7768 int retries = 100;
7769
7770 /*
7771 * Don't go into an infinite loop when multiple subsequent disk
7772 * write operations return 0 (maybe disk full).
7773 */
7774 returnCode = NS_ERROR;
7775 do {
7776 if (written < 0) {
7777 fprintf(stderr, "error during async write (fd %d): %s\n",__fprintf_chk (stderr, 2 - 1, "error during async write (fd %d): %s\n"
, fd, strerror((*__errno_location ())))
7778 fd, strerror(errno))__fprintf_chk (stderr, 2 - 1, "error during async write (fd %d): %s\n"
, fd, strerror((*__errno_location ())))
;
7779 break;
7780 }
7781 /*
7782 * All partial writes (written >= 0)
7783 */
7784 WriteWarningRaw("partial write", fd, nbyte, written);
7785 nbyte -= (size_t)written;
7786 buffer += written;
7787 written = ns_writewrite(fd, buffer, nbyte);
7788 if (written == (ssize_t)nbyte) {
7789 returnCode = NS_OK;
7790 break;
7791 }
7792 } while (retries-- > 0);
7793 }
7794
7795 } else {
7796 SpoolerQueue *queuePtr;
7797 bool_Bool trigger = NS_FALSE0;
7798 const AsyncWriteData *wdPtr;
7799 AsyncWriteData *newWdPtr;
7800
7801 /*
7802 * Allocate a writer cmd and initialize it. In order to provide an
7803 * interface compatible to ns_write(), we copy the provided data,
7804 * such it can be freed by the caller. When we would give up the
7805 * interface, we could free the memory block after writing, and
7806 * save a malloc/free operation on the data.
7807 */
7808 newWdPtr = ns_calloc(1u, sizeof(AsyncWriteData));
7809 newWdPtr->fd = fd;
7810 newWdPtr->bufsize = nbyte;
7811 newWdPtr->data = ns_malloc(nbyte + 1u);
7812 memcpy(newWdPtr->data, buffer, newWdPtr->bufsize);
7813 newWdPtr->buf = newWdPtr->data;
7814 newWdPtr->size = newWdPtr->bufsize;
7815
7816 /*
7817 * Now add new writer socket to the writer thread's queue. In most
7818 * cases, the queue will be empty.
7819 */
7820 queuePtr = asyncWriter->firstPtr;
7821 assert(queuePtr != NULL)((void) (0));
7822
7823 Ns_MutexLock(&queuePtr->lock);
7824 wdPtr = queuePtr->sockPtr;
7825 if (wdPtr != NULL((void*)0)) {
7826 newWdPtr->nextPtr = queuePtr->sockPtr;
7827 queuePtr->sockPtr = newWdPtr;
7828 } else {
7829 queuePtr->sockPtr = newWdPtr;
7830 trigger = NS_TRUE1;
7831 }
7832 Ns_MutexUnlock(&queuePtr->lock);
7833
7834 /*
7835 * Wake up writer thread if desired
7836 */
7837 if (trigger) {
7838 SockTrigger(queuePtr->pipe[1]);
7839 }
7840 }
7841
7842 return returnCode;
7843}
7844
7845/*
7846 *----------------------------------------------------------------------
7847 *
7848 * AsyncWriterRelease --
7849 *
7850 * Deallocate write data.
7851 *
7852 * Results:
7853 * None
7854 *
7855 * Side effects:
7856 * free memory
7857 *
7858 *----------------------------------------------------------------------
7859 */
7860static void
7861AsyncWriterRelease(AsyncWriteData *wdPtr)
7862{
7863 NS_NONNULL_ASSERT(wdPtr != NULL)((void) (0));
7864
7865 ns_free(wdPtr->data);
7866 ns_free(wdPtr);
7867}
7868
7869/*
7870 *----------------------------------------------------------------------
7871 *
7872 * AsyncWriterThread --
7873 *
7874 * Thread that implements nonblocking write operations to files
7875 *
7876 * Results:
7877 * None.
7878 *
7879 * Side effects:
7880 * Write to files.
7881 *
7882 *----------------------------------------------------------------------
7883 */
7884
7885static void
7886AsyncWriterThread(void *arg)
7887{
7888 SpoolerQueue *queuePtr = (SpoolerQueue*)arg;
7889 char charBuffer[1];
7890 int pollTimeout;
7891 Ns_ReturnCode status;
7892 bool_Bool stopping = NS_FALSE0;
7893 AsyncWriteData *curPtr, *nextPtr, *writePtr = NULL((void*)0);
7894 PollData pdata;
7895
7896 Ns_ThreadSetName("-asynclogwriter%d-", queuePtr->id);
7897 queuePtr->threadName = Ns_ThreadGetName();
7898
7899 /*
7900 * Allocate and initialize controlling variables
7901 */
7902
7903 PollCreate(&pdata);
7904
7905 /*
7906 * Loop forever until signaled to shutdown and all
7907 * connections are complete and gracefully closed.
7908 */
7909
7910 while (!stopping) {
7911
7912 /*
7913 * Always listen to the trigger pipe. We could as well perform
7914 * in the writer thread async write operations, but for the
7915 * effect of reducing latency in connection threads, this is
7916 * not an issue. To keep things simple, we perform the
7917 * typically small write operations without testing for POLLOUT.
7918 */
7919
7920 PollReset(&pdata);
7921 (void)PollSet(&pdata, queuePtr->pipe[0], (short)POLLIN0x001, NULL((void*)0));
7922
7923 if (writePtr == NULL((void*)0)) {
7924 pollTimeout = 30 * 1000;
7925 } else {
7926 pollTimeout = 0;
7927 }
7928
7929 /*
7930 * Wait for data
7931 */
7932 /*n =*/ (void) PollWait(&pdata, pollTimeout);
7933
7934 /*
7935 * Select and drain the trigger pipe if necessary.
7936 */
7937 if (PollIn(&pdata, 0)(((&pdata)->pfds[(0)].revents & 0x001) == 0x001 )) {
7938 if (ns_recvrecv(queuePtr->pipe[0], charBuffer, 1u, 0) != 1) {
7939 Ns_Fatal("asynclogwriter: trigger ns_recv() failed: %s",
7940 ns_sockstrerrorstrerror(ns_sockerrno(*__errno_location ())));
7941 }
7942 if (queuePtr->stopped) {
7943 /*
7944 * Drain the queue from everything
7945 */
7946 for (curPtr = writePtr; curPtr != NULL((void*)0); curPtr = curPtr->nextPtr) {
7947 ssize_t written = ns_writewrite(curPtr->fd, curPtr->buf, curPtr->bufsize);
7948 if (unlikely(written != (ssize_t)curPtr->bufsize)(__builtin_expect((written != (ssize_t)curPtr->bufsize), 0
))
) {
7949 WriteWarningRaw("drain writer", curPtr->fd, curPtr->bufsize, written);
7950 }
7951 }
7952 writePtr = NULL((void*)0);
7953
7954 for (curPtr = queuePtr->sockPtr; curPtr != NULL((void*)0); curPtr = curPtr->nextPtr) {
7955 ssize_t written = ns_writewrite(curPtr->fd, curPtr->buf, curPtr->bufsize);
7956 if (unlikely(written != (ssize_t)curPtr->bufsize)(__builtin_expect((written != (ssize_t)curPtr->bufsize), 0
))
) {
7957 WriteWarningRaw("drain queue", curPtr->fd, curPtr->bufsize, written);
7958 }
7959 }
7960 queuePtr->sockPtr = NULL((void*)0);
7961
7962 /*
7963 * Notify the caller (normally
7964 * NsAsyncWriterQueueDisable()) that we are done
7965 */
7966 Ns_CondBroadcast(&queuePtr->cond);
7967 }
7968 }
7969
7970 /*
7971 * Write to all available file descriptors
7972 */
7973
7974 curPtr = writePtr;
7975 writePtr = NULL((void*)0);
7976
7977 while (curPtr != NULL((void*)0)) {
7978 ssize_t written;
7979
7980 nextPtr = curPtr->nextPtr;
7981 status = NS_OK;
7982
7983 /*
7984 * Write the actual data and allow for partial write operations.
7985 */
7986 written = ns_writewrite(curPtr->fd, curPtr->buf, curPtr->bufsize);
7987 if (unlikely(written < 0)(__builtin_expect((written < 0), 0))) {
7988 status = NS_ERROR;
7989 } else {
7990 curPtr->size -= (size_t)written;
7991 curPtr->nsent += written;
7992 curPtr->bufsize -= (size_t)written;
7993 if (curPtr->data != NULL((void*)0)) {
7994 curPtr->buf += written;
7995 }
7996 }
7997
7998 if (unlikely(status != NS_OK)(__builtin_expect((status != NS_OK), 0))) {
7999 AsyncWriterRelease(curPtr);
8000 queuePtr->queuesize--;
8001 } else {
8002
8003 /*
8004 * The write operation was successful. Check if there
8005 * is some remaining data to write. If not we are done
8006 * with this request can release the write buffer.
8007 */
8008 if (curPtr->size > 0u) {
8009 Push(curPtr, writePtr)((curPtr)->nextPtr = (writePtr), (writePtr) = (curPtr));
8010 } else {
8011 AsyncWriterRelease(curPtr);
8012 queuePtr->queuesize--;
8013 }
8014 }
8015
8016 curPtr = nextPtr;
8017 }
8018
8019
8020 /*
8021 * Check for shutdown
8022 */
8023 stopping = queuePtr->shutdown;
8024 if (stopping) {
8025 curPtr = queuePtr->sockPtr;
8026 assert(writePtr == NULL)((void) (0));
8027 while (curPtr != NULL((void*)0)) {
8028 ssize_t written = ns_writewrite(curPtr->fd, curPtr->buf, curPtr->bufsize);
8029 if (unlikely(written != (ssize_t)curPtr->bufsize)(__builtin_expect((written != (ssize_t)curPtr->bufsize), 0
))
) {
8030 WriteWarningRaw("shutdown", curPtr->fd, curPtr->bufsize, written);
8031 }
8032 curPtr = curPtr->nextPtr;
8033 }
8034 } else {
8035 /*
8036 * Add fresh jobs to the writer queue. This means actually to
8037 * move jobs from queuePtr->sockPtr (kept name for being able
8038 * to use the same queue as above) to the currently active
8039 * jobs in queuePtr->curPtr.
8040 */
8041 Ns_MutexLock(&queuePtr->lock);
8042 curPtr = queuePtr->sockPtr;
8043 queuePtr->sockPtr = NULL((void*)0);
8044 while (curPtr != NULL((void*)0)) {
8045 nextPtr = curPtr->nextPtr;
8046 Push(curPtr, writePtr)((curPtr)->nextPtr = (writePtr), (writePtr) = (curPtr));
8047 queuePtr->queuesize++;
8048 curPtr = nextPtr;
8049 }
8050 queuePtr->curPtr = writePtr;
8051 Ns_MutexUnlock(&queuePtr->lock);
8052 }
8053
8054 }
8055
8056 PollFree(&pdata);
8057
8058 queuePtr->stopped = NS_TRUE1;
8059 Ns_Log(Notice, "exiting");
8060
8061}
8062
8063/*
8064 *----------------------------------------------------------------------
8065 *
8066 * AsyncLogfileWriteObjCmd -
8067 *
8068 * Implements "ns_asynclogfile write" command. Write to a file
8069 * descriptor via async writer thread. The command handles partial write
8070 * operations internally.
8071 *
8072 * Results:
8073 * Standard Tcl result.
8074 *
8075 * Side effects:
8076 * None.
8077 *
8078 *----------------------------------------------------------------------
8079 */
8080static int
8081AsyncLogfileWriteObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
8082{
8083 int result = TCL_OK0, binary = (int)NS_FALSE0, sanitize;
8084 Tcl_Obj *stringObj;
8085 int fd = 0;
8086 Ns_ObjvValueRange fd_range = {0, INT_MAX2147483647};
8087 Ns_ObjvValueRange sanitize_range = {0, 2};
8088 Ns_ObjvSpec opts[] = {
8089 {"-binary", Ns_ObjvBool, &binary, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
8090 {"-sanitize", Ns_ObjvInt, &sanitize, &sanitize_range},
8091 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
8092 };
8093 Ns_ObjvSpec args[] = {
8094 {"fd", Ns_ObjvInt, &fd, &fd_range},
8095 {"buffer", Ns_ObjvObj, &stringObj, NULL((void*)0)},
8096 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
8097 };
8098
8099 /*
8100 * Take the config value as default for "-sanitize", but let the used
8101 * override it on a per-case basis.
8102 */
8103 sanitize = nsconf.sanitize_logfiles;
8104
8105 if (unlikely(Ns_ParseObjv(opts, args, interp, 2, objc, objv) != NS_OK)(__builtin_expect((Ns_ParseObjv(opts, args, interp, 2, objc, objv
) != NS_OK), 0))
) {
8106 result = TCL_ERROR1;
8107
8108 } else {
8109 const char *buffer;
8110 int length;
8111 Ns_ReturnCode rc;
8112
8113 if (binary == (int)NS_TRUE1 || NsTclObjIsByteArray(stringObj)) {
8114 buffer = (const char *) Tcl_GetByteArrayFromObj(stringObj, &length);
8115 } else {
8116 buffer = Tcl_GetStringFromObj(stringObj, &length);
8117 }
8118 if (length > 0) {
8119 if (sanitize > 0) {
8120 Tcl_DString ds;
8121 bool_Bool lastCharNewline = (buffer[length-1] == '\n');
8122
8123 Tcl_DStringInit(&ds);
8124 if (lastCharNewline) {
8125 length --;
8126 }
8127 Ns_DStringAppendPrintable(&ds, sanitize == 2, buffer, (size_t)length);
8128 if (lastCharNewline) {
8129 Tcl_DStringAppend(&ds, "\n", 1);
8130 }
8131 rc = NsAsyncWrite(fd, ds.string, (size_t)ds.length);
8132 Tcl_DStringFree(&ds);
8133
8134 } else {
8135 rc = NsAsyncWrite(fd, buffer, (size_t)length);
8136 }
8137
8138 if (rc != NS_OK) {
8139 Ns_TclPrintfResult(interp, "ns_asynclogfile: error during write operation on fd %d: %s",
8140 fd, Tcl_PosixError(interp));
8141 result = TCL_ERROR1;
8142 }
8143 } else {
8144 result = TCL_OK0;
8145 }
8146 }
8147 return result;
8148}
8149
8150/*
8151 *----------------------------------------------------------------------
8152 *
8153 * AsyncLogfileOpenObjCmd -
8154 *
8155 * Implements "ns_asynclogfile open" command. The command opens a
8156 * write-only log file and return a thread-shareable handle (actually a
8157 * numeric file descriptor) which can be used in subsequent "write" or
8158 * "close" operations.
8159 *
8160 * Results:
8161 * Standard Tcl result.
8162 *
8163 * Side effects:
8164 * None.
8165 *
8166 *----------------------------------------------------------------------
8167 */
8168static int
8169AsyncLogfileOpenObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
8170{
8171 int result = TCL_OK0;
8172 unsigned int flags = O_APPEND02000;
8173 char *fileNameString;
8174 Tcl_Obj *flagsObj = NULL((void*)0);
8175 Ns_ObjvTable flagTable[] = {
8176 {"APPEND", O_APPEND02000},
8177 {"EXCL", O_EXCL0200},
8178#ifdef O_DSYNC010000
8179 {"DSYNC", O_DSYNC010000},
8180#endif
8181#ifdef O_SYNC04010000
8182 {"SYNC", O_SYNC04010000},
8183#endif
8184 {"TRUNC", O_TRUNC01000},
8185 {NULL((void*)0), 0u}
8186 };
8187 Ns_ObjvSpec args[] = {
8188 {"filename", Ns_ObjvString, &fileNameString, NULL((void*)0)},
8189 {"?flags", Ns_ObjvObj, &flagsObj, NULL((void*)0)},
8190 //{"mode", Ns_ObjvString, &mode, NULL},
8191 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
8192 };
8193
8194 if (unlikely(Ns_ParseObjv(NULL, args, interp, 2, objc, objv) != NS_OK)(__builtin_expect((Ns_ParseObjv(((void*)0), args, interp, 2, objc
, objv) != NS_OK), 0))
) {
8195 result = TCL_ERROR1;
8196
8197 } else if (flagsObj != NULL((void*)0)) {
8198 Tcl_Obj **ov;
8199 int oc;
8200
8201 result = Tcl_ListObjGetElements(interp, flagsObj, &oc, &ov);
8202 if (result == TCL_OK0 && oc > 0) {
8203 int i, opt;
8204
8205 flags = 0u;
8206 for (i = 0; i < oc; i++) {
8207 result = Tcl_GetIndexFromObjStruct(interp, ov[i], flagTable,
8208 (int)sizeof(flagTable[0]),
8209 "flag", 0, &opt);
8210 if (result != TCL_OK0) {
8211 break;
8212 } else {
8213 flags = flagTable[opt].value;
8214 }
8215 }
8216 }
8217 }
8218
8219 if (result == TCL_OK0) {
8220 int fd;
8221
8222 fd = ns_openopen(fileNameString, (int)(O_CREAT0100 | O_WRONLY01 | O_CLOEXEC02000000 | flags), 0644);
8223
8224 if (unlikely(fd == NS_INVALID_FD)(__builtin_expect((fd == (-1)), 0))) {
8225 Ns_TclPrintfResult(interp, "could not open file '%s': %s",
8226 fileNameString, Tcl_PosixError(interp));
8227 result = TCL_ERROR1;
8228 } else {
8229 Tcl_SetObjResult(interp, Tcl_NewIntObj(fd));
8230 }
8231 }
8232 return result;
8233}
8234
8235/*
8236 *----------------------------------------------------------------------
8237 *
8238 * AsyncLogfileCloseObjCmd -
8239 *
8240 * Implements "ns_asynclogfile close" command. Close the logfile
8241 * previously created via "ns_asynclogfile open".
8242 *
8243 * Results:
8244 * Standard Tcl result.
8245 *
8246 * Side effects:
8247 * None.
8248 *
8249 *----------------------------------------------------------------------
8250 */
8251static int
8252AsyncLogfileCloseObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
8253{
8254 int fd = 0, result = TCL_OK0;
8255 Ns_ObjvValueRange range = {0, INT_MAX2147483647};
8256 Ns_ObjvSpec args[] = {
8257 {"fd", Ns_ObjvInt, &fd, &range},
8258 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
8259 };
8260
8261 if (unlikely(Ns_ParseObjv(NULL, args, interp, 2, objc, objv) != NS_OK)(__builtin_expect((Ns_ParseObjv(((void*)0), args, interp, 2, objc
, objv) != NS_OK), 0))
) {
8262 result = TCL_ERROR1;
8263
8264 } else {
8265 int rc = ns_closeclose(fd);
8266
8267 if (rc != 0) {
8268 Ns_TclPrintfResult(interp, "could not close fd %d: %s",
8269 fd, Tcl_PosixError(interp));
8270 result = TCL_ERROR1;
8271 }
8272 }
8273 return result;
8274}
8275
8276/*
8277 *----------------------------------------------------------------------
8278 *
8279 * NsTclAsyncLogfileObjCmd -
8280 *
8281 * Wrapper for "ns_asynclogfile open|write|close" commands.
8282 *
8283 * Results:
8284 * Standard Tcl result.
8285 *
8286 * Side effects:
8287 * None.
8288 *
8289 *----------------------------------------------------------------------
8290 */
8291int
8292NsTclAsyncLogfileObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
8293{
8294 const Ns_SubCmdSpec subcmds[] = {
8295 {"open", AsyncLogfileOpenObjCmd},
8296 {"write", AsyncLogfileWriteObjCmd},
8297 {"close", AsyncLogfileCloseObjCmd},
8298 {NULL((void*)0), NULL((void*)0)}
8299 };
8300
8301 return Ns_SubcmdObjv(subcmds, clientData, interp, objc, objv);
8302}
8303
8304
8305
8306/*
8307 *----------------------------------------------------------------------
8308 *
8309 * LookupDriver --
8310 *
8311 * Find a matching driver for the specified protocol and optionally the
8312 * specified driver name.
8313 *
8314 * Results:
8315 * Driver pointer or NULL on failure.
8316 *
8317 * Side effects:
8318 * When no driver is found, an error is left in the interp result.
8319 *
8320 *----------------------------------------------------------------------
8321 */
8322
8323static Driver *
8324LookupDriver(Tcl_Interp *interp, const char* protocol, const char *driverName)
8325{
8326 Driver *drvPtr;
8327
8328 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
8329 NS_NONNULL_ASSERT(protocol != NULL)((void) (0));
8330
8331 for (drvPtr = firstDrvPtr; drvPtr != NULL((void*)0); drvPtr = drvPtr->nextPtr) {
8332 Ns_Log(DriverDebug, "... check Driver proto <%s> server %s name %s location %s",
8333 drvPtr->protocol, drvPtr->server, drvPtr->threadName, drvPtr->location);
8334
8335 if (STREQ(drvPtr->protocol, protocol)(((*(drvPtr->protocol)) == (*(protocol))) && (strcmp
((drvPtr->protocol),(protocol)) == 0))
) {
8336 if (driverName == NULL((void*)0)) {
8337 /*
8338 * If there is no driver name given, take the first driver
8339 * with the matching protocol.
8340 */
8341 break;
8342 } else if (STREQ(drvPtr->moduleName, driverName)(((*(drvPtr->moduleName)) == (*(driverName))) && (
strcmp((drvPtr->moduleName),(driverName)) == 0))
) {
8343 /*
8344 * The driver name (name of the loaded module) is equal
8345 */
8346 break;
8347 }
8348 }
8349 }
8350
8351 if (drvPtr == NULL((void*)0)) {
8352 if (driverName != NULL((void*)0)) {
8353 Ns_TclPrintfResult(interp, "no driver for protocol '%s' & driver name '%s' found", protocol, driverName);
8354 } else {
8355 Ns_TclPrintfResult(interp, "no driver for protocol '%s' found", protocol);
8356 }
8357 }
8358 return drvPtr;
8359}
8360/*
8361 *----------------------------------------------------------------------
8362 *
8363 * NSDriverClientOpen --
8364 *
8365 * Open a client HTTP connection using the driver interface
8366 *
8367 * Results:
8368 * Tcl return code.
8369 *
8370 * Side effects:
8371 * Opening a connection
8372 *
8373 *----------------------------------------------------------------------
8374 */
8375
8376int
8377NSDriverClientOpen(Tcl_Interp *interp, const char *driverName,
8378 const char *url, const char *httpMethod, const char *version,
8379 const Ns_Time *timeoutPtr, Sock **sockPtrPtr)
8380{
8381 char *url2;
8382 const char *errorMsg = NULL((void*)0);
8383 int result = TCL_OK0;
8384 Ns_URL u;
8385
8386 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
8387 NS_NONNULL_ASSERT(url != NULL)((void) (0));
8388 NS_NONNULL_ASSERT(httpMethod != NULL)((void) (0));
8389 NS_NONNULL_ASSERT(version != NULL)((void) (0));
8390 NS_NONNULL_ASSERT(sockPtrPtr != NULL)((void) (0));
8391
8392 url2 = ns_strdup(url);
8393
8394 /*
8395 * We need here a fully qualified URL, otherwise raise an error
8396 */
8397 if (unlikely(Ns_ParseUrl(url2, NS_FALSE, &u, &errorMsg) != NS_OK)(__builtin_expect((Ns_ParseUrl(url2, 0, &u, &errorMsg
) != NS_OK), 0))
8398 || u.protocol == NULL((void*)0) || u.host == NULL((void*)0) || u.path == NULL((void*)0) || u.tail == NULL((void*)0)) {
8399 Ns_Log(Notice, "driver: invalid URL '%s' passed to NSDriverClientOpen: %s", url2, errorMsg);
8400 result = TCL_ERROR1;
8401
8402 } else {
8403 Driver *drvPtr;
8404 unsigned short portNr = 0u; /* make static checker happy */
8405
8406 assert(u.protocol != NULL)((void) (0));
8407 assert(u.host != NULL)((void) (0));
8408 assert(u.path != NULL)((void) (0));
8409 assert(u.tail != NULL)((void) (0));
8410
8411 /*
8412 * Find a matching driver for the specified protocol and optionally
8413 * the specified driver name.
8414 */
8415 drvPtr = LookupDriver(interp, u.protocol, driverName);
8416 if (drvPtr == NULL((void*)0)) {
8417 result = TCL_ERROR1;
8418
8419 } else if (u.port != NULL((void*)0)) {
8420 portNr = (unsigned short) strtol(u.port, NULL((void*)0), 10);
8421
8422 } else if (drvPtr->defport != 0u) {
8423 /*
8424 * Get the default port from the driver structure;
8425 */
8426 portNr = drvPtr->defport;
8427
8428 } else {
8429 Ns_TclPrintfResult(interp, "no default port for protocol '%s' defined", u.protocol);
8430 result = TCL_ERROR1;
8431 }
8432
8433 if (result == TCL_OK0) {
8434 NS_SOCKETint sock;
8435 Ns_ReturnCode status;
8436
8437 sock = Ns_SockTimedConnect2(u.host, portNr, NULL((void*)0), 0u, timeoutPtr, &status);
8438
8439 if (sock == NS_INVALID_SOCKET(-1)) {
8440 Ns_SockConnectError(interp, u.host, portNr, status);
8441 result = TCL_ERROR1;
8442
8443 } else {
8444 Tcl_DString ds, *dsPtr = &ds;
8445 Request *reqPtr;
8446 Sock *sockPtr;
8447
8448 assert(drvPtr != NULL)((void) (0));
8449
8450 sockPtr = SockNew(drvPtr);
8451 sockPtr->sock = sock;
8452 sockPtr->servPtr = drvPtr->servPtr;
8453 if (sockPtr->servPtr == NULL((void*)0)) {
8454 const NsInterp *itPtr = NsGetInterpData(interp);
8455
8456 sockPtr->servPtr = itPtr->servPtr;
8457 }
8458
8459 sockPtr->reqPtr = RequestNew();
8460
8461 Ns_GetTime(&sockPtr->acceptTime);
8462 reqPtr = sockPtr->reqPtr;
8463
8464 Tcl_DStringInit(dsPtr);
8465 Ns_DStringAppend(dsPtr, httpMethod)Tcl_DStringAppend((dsPtr), (httpMethod), -1);
8466 Ns_StrToUpper(Ns_DStringValue(dsPtr)((dsPtr)->string));
8467 Tcl_DStringAppend(dsPtr, " /", 2);
8468 if (*u.path != '\0') {
8469 if (*u.path == '/') {
8470 u.path ++;
8471 }
8472 Tcl_DStringAppend(dsPtr, u.path, -1);
8473 Tcl_DStringAppend(dsPtr, "/", 1);
8474 }
8475 Tcl_DStringAppend(dsPtr, u.tail, -1);
8476 if (u.query != NULL((void*)0)) {
8477 Ns_DStringNAppendTcl_DStringAppend(dsPtr, "?", 1);
8478 Ns_DStringNAppendTcl_DStringAppend(dsPtr, u.query, -1);
8479 }
8480 if (u.fragment != NULL((void*)0)) {
8481 Ns_DStringNAppendTcl_DStringAppend(dsPtr, "#", 1);
8482 Ns_DStringNAppendTcl_DStringAppend(dsPtr, u.fragment, -1);
8483 }
8484
8485 Tcl_DStringAppend(dsPtr, " HTTP/", 6);
8486 Tcl_DStringAppend(dsPtr, version, -1);
8487
8488 reqPtr->request.line = Ns_DStringExport(dsPtr);
8489 reqPtr->request.method = ns_strdup(httpMethod);
8490 reqPtr->request.protocol = ns_strdup(u.protocol);
8491 reqPtr->request.host = ns_strdup(u.host);
8492 if (u.query != NULL((void*)0)) {
8493 reqPtr->request.query = ns_strdup(u.query+1);
8494 } else {
8495 reqPtr->request.query = NULL((void*)0);
8496 }
8497 /*Ns_Log(Notice, "REQUEST LINE <%s> query <%s>", reqPtr->request.line, reqPtr->request.query);*/
8498
8499 *sockPtrPtr = sockPtr;
8500 }
8501 }
8502 }
8503 ns_free(url2);
8504
8505 return result;
8506}
8507
8508/*
8509 *----------------------------------------------------------------------
8510 *
8511 * NSDriverSockNew --
8512 *
8513 * Create a Sock structure based on the driver interface
8514 *
8515 * Results:
8516 * Tcl return code.
8517 *
8518 * Side effects:
8519 * Accepting a connection
8520 *
8521 *----------------------------------------------------------------------
8522 */
8523
8524int
8525NSDriverSockNew(Tcl_Interp *interp, NS_SOCKETint sock,
8526 const char *protocol, const char *driverName, const char *methodName,
8527 Sock **sockPtrPtr)
8528{
8529 int result = TCL_OK0;
8530 Driver *drvPtr;
8531
8532 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
8533 NS_NONNULL_ASSERT(protocol != NULL)((void) (0));
8534 NS_NONNULL_ASSERT(methodName != NULL)((void) (0));
8535 NS_NONNULL_ASSERT(sockPtrPtr != NULL)((void) (0));
8536
8537 drvPtr = LookupDriver(interp, protocol, driverName);
8538 if (drvPtr == NULL((void*)0)) {
8539 result = TCL_ERROR1;
8540 } else {
8541 Sock *sockPtr;
8542 Tcl_DString ds, *dsPtr = &ds;
8543 Request *reqPtr;
8544
8545 sockPtr = SockNew(drvPtr);
8546 sockPtr->servPtr = drvPtr->servPtr;
8547 sockPtr->sock = sock;
8548
8549 sockPtr->reqPtr = RequestNew();
8550
8551 // peerAddr is missing
8552
8553 Ns_GetTime(&sockPtr->acceptTime);
8554 reqPtr = sockPtr->reqPtr;
8555
8556 Tcl_DStringInit(dsPtr);
8557 Ns_DStringAppend(dsPtr, methodName)Tcl_DStringAppend((dsPtr), (methodName), -1);
8558 Ns_StrToUpper(Ns_DStringValue(dsPtr)((dsPtr)->string));
8559
8560 reqPtr->request.line = Ns_DStringExport(dsPtr);
8561 reqPtr->request.method = ns_strdup(methodName);
8562 reqPtr->request.protocol = ns_strdup(protocol);
8563 reqPtr->request.host = NULL((void*)0);
8564 reqPtr->request.query = NULL((void*)0);
8565 /* Ns_Log(Notice, "REQUEST LINE <%s>", reqPtr->request.line);*/
8566
8567 *sockPtrPtr = sockPtr;
8568 }
8569
8570 return result;
8571}
8572
8573/*
8574 * Local Variables:
8575 * mode: c
8576 * c-basic-offset: 4
8577 * fill-column: 78
8578 * indent-tabs-mode: nil
8579 * End:
8580 */