Bug Summary

File:d/connio.c
Warning:line 1046, column 31
Access out-of-bound array element (buffer overflow)

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 connio.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 connio.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/*
32 * connio.c --
33 *
34 * Handle connection I/O.
35 */
36
37#include "nsd.h"
38
39/*
40 * The following is used to allocate a buffer on the stack for
41 * encoding character data and for transferring data from disk to the
42 * network, and so defines the chunk size of writes to the network.
43 */
44
45#define IOBUFSZ8192 8192
46
47/*
48 * The chunked encoding header consists of a hex number followed by
49 * CRLF (see e.g. RFC 2616 section 3.6.1). It has to fit the maximum
50 * number of digits of a 64 byte number is 8, plus CRLF + NULL.
51 */
52#define MAX_CHARS_CHUNK_HEADER12 12
53
54
55/*
56 * Local functions defined in this file
57 */
58
59static Ns_ReturnCode ConnSend(Ns_Conn *conn, ssize_t nsend, Tcl_Channel chan,
60 FILE *fp, int fd)
61 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
62
63static Ns_ReturnCode ConnCopy(const Ns_Conn *conn, size_t toCopy, Tcl_Channel chan,
64 FILE *fp, int fd)
65 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
66
67static bool_Bool CheckKeep(const Conn *connPtr)
68 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
69
70static int CheckCompress(const Conn *connPtr, const struct iovec *bufs, int nbufs, unsigned int ioflags)
71 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
72
73static bool_Bool HdrEq(const Ns_Set *set, const char *name, const char *value)
74 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3)));
75
76
77/*
78 *----------------------------------------------------------------------
79 *
80 * Ns_ConnWriteChars, Ns_ConnWriteVChars --
81 *
82 * This will write a string buffer to the conn. The distinction
83 * being that the given data is explicitly a UTF8 character
84 * string, and will be put out in an 'encoding-aware' manner. It
85 * promises to write all of it.
86 *
87 * Results:
88 * NS_OK if all data written, NS_ERROR otherwise.
89 *
90 * Side effects:
91 * See Ns_ConnWriteVData().
92 *
93 *----------------------------------------------------------------------
94 */
95
96Ns_ReturnCode
97Ns_ConnWriteChars(Ns_Conn *conn, const char *buf, size_t toWrite, unsigned int flags)
98{
99 struct iovec sbuf;
100
101 sbuf.iov_base = (void *) buf;
102 sbuf.iov_len = toWrite;
103 return Ns_ConnWriteVChars(conn, &sbuf, 1, flags);
104}
105
106Ns_ReturnCode
107Ns_ConnWriteVChars(Ns_Conn *conn, struct iovec *bufs, int nbufs, unsigned int flags)
108{
109 Conn *connPtr = (Conn *) conn;
110 Ns_DStringTcl_DString encDs, gzDs;
111 struct iovec iov;
112 Ns_ReturnCode status;
113
114 Ns_DStringInitTcl_DStringInit(&encDs);
115 Ns_DStringInitTcl_DStringInit(&gzDs);
116
117 /*
118 * Transcode to charset if necessary. In earlier versions, the
119 * transcoding was guarded by "!NsEncodingIsUtf8()", which was an
120 * optimization. However, we cannot assume that the internal Tcl
121 * UTF-8 is the same as an external, especially for emoji and
122 * other multibyte characters.
123 */
124
125 if (connPtr->outputEncoding != NULL((void*)0)
126 && nbufs > 0
127 && bufs[0].iov_len > 0u) {
128 int i;
129
130 for (i = 0; i < nbufs; i++) {
131 const char *utfBytes;
132 size_t utfLen;
133
134 utfBytes = bufs[i].iov_base;
135 utfLen = bufs[i].iov_len;
136
137 if (utfLen > 0u) {
138 (void) Tcl_UtfToExternalDString(connPtr->outputEncoding,
139 utfBytes, (int)utfLen, &encDs);
140 }
141 }
142 (void)Ns_SetVec(&iov, 0, encDs.string, (size_t)encDs.length);
143 bufs = &iov;
144 nbufs = 1;
145 }
146
147 /*
148 * Compress if possible.
149 */
150
151 if (connPtr->compress < 0) {
152 connPtr->compress = CheckCompress(connPtr, bufs, nbufs, flags);
153 }
154 if (connPtr->compress > 0
155 && (nbufs > 0 || (flags & NS_CONN_STREAM_CLOSE0x080u) != 0u)
156 ) {
157 bool_Bool flush = ((flags & NS_CONN_STREAM0x040u) == 0u);
158
159 if (Ns_CompressBufsGzip(&connPtr->cStream, bufs, nbufs, &gzDs,
160 connPtr->compress, flush) == NS_OK) {
161 /* NB: Compression will always succeed. */
162 (void)Ns_SetVec(&iov, 0, gzDs.string, (size_t)gzDs.length);
163 bufs = &iov;
164 nbufs = 1;
165 }
166 }
167
168 status = Ns_ConnWriteVData(conn, bufs, nbufs, flags);
169
170 Ns_DStringFreeTcl_DStringFree(&encDs);
171 Ns_DStringFreeTcl_DStringFree(&gzDs);
172
173 return status;
174}
175
176
177
178/*
179 *----------------------------------------------------------------------
180 *
181 * CheckCompress --
182 *
183 * Is compression enabled, and at what level.
184 *
185 * Results:
186 * compress level 0-9
187 *
188 * Side effects:
189 * May set the Content-Encoding and Vary headers.
190 *
191 *----------------------------------------------------------------------
192 */
193
194static int
195CheckCompress(const Conn *connPtr, const struct iovec *bufs, int nbufs, unsigned int ioflags)
196{
197 const Ns_Conn *conn = (Ns_Conn *) connPtr;
198 const NsServer *servPtr;
199 int configuredCompressionLevel, compressionLevel = 0;
200
201 NS_NONNULL_ASSERT(connPtr != NULL)((void) (0));
202
203 servPtr = connPtr->poolPtr->servPtr;
204
205 /*
206 * Check the default setting and explicit override.
207 */
208 configuredCompressionLevel = Ns_ConnGetCompression(conn);
209
210 if (configuredCompressionLevel > 0) {
211 /*
212 * Make sure the length is above the minimum threshold, or
213 * we're streaming (assume length is long enough for streams).
214 */
215 if (((ioflags & NS_CONN_STREAM0x040u) != 0u)
216 || (bufs != NULL((void*)0) && Ns_SumVec(bufs, nbufs) >= (size_t)servPtr->compress.minsize)
217 || connPtr->responseLength >= servPtr->compress.minsize) {
218 /*
219 * We won't be compressing if there are no headers or body.
220 */
221 if (((connPtr->flags & NS_CONN_SENTHDRS0x010u) == 0u)
222 && ((connPtr->flags & NS_CONN_SKIPBODY0x004u) == 0u)) {
223 Ns_ConnSetHeaders(conn, "Vary", "Accept-Encoding");
224
225 if ((connPtr->flags & NS_CONN_ZIPACCEPTED0x10000u) != 0u) {
226 Ns_ConnSetHeaders(conn, "Content-Encoding", "gzip");
227 compressionLevel = configuredCompressionLevel;
228 }
229 }
230 }
231 }
232 return compressionLevel;
233}
234
235
236/*
237 *----------------------------------------------------------------------
238 *
239 * Ns_ConnWriteData, Ns_ConnWriteVData --
240 *
241 * Send zero or more buffers of raw bytes to the client, possibly
242 * using the HTTP chunked encoding if flags includes
243 * NS_CONN_STREAM.
244 *
245 * Ns_ConnWriteVData() is called with (nbufs == 0) to flush
246 * headers.
247 *
248 * Results:
249 * NS_OK if all data written, NS_ERROR otherwise.
250 *
251 * Side effects:
252 * HTTP headers are constructed and sent on first call.
253 *
254 *----------------------------------------------------------------------
255 */
256
257Ns_ReturnCode
258Ns_ConnWriteData(Ns_Conn *conn, const void *buf, size_t toWrite, unsigned int flags)
259{
260 struct iovec vbuf;
261
262 vbuf.iov_base = (void *) buf;
263 vbuf.iov_len = toWrite;
264
265 return Ns_ConnWriteVData(conn, &vbuf, 1, flags);
266}
267
268Ns_ReturnCode
269Ns_ConnWriteVData(Ns_Conn *conn, struct iovec *bufs, int nbufs, unsigned int flags)
270{
271 Ns_DStringTcl_DString ds;
272 int nsbufs, sbufIdx;
273 size_t bodyLength, toWrite, neededBufs;
274 ssize_t nwrote;
275 struct iovec sbufs[32], *sbufPtr;
276 char hdr[MAX_CHARS_CHUNK_HEADER12]; /* Address of this
277 variable might be
278 used in
279 Ns_ConnSend(),
280 therefore, we cannot
281 reduce scope. */
282
283 NS_NONNULL_ASSERT(conn != NULL)((void) (0));
284 //NS_NONNULL_ASSERT(bufs != NULL);
285
286 Ns_DStringInitTcl_DStringInit(&ds);
287
288 /*
289 * Make sure there's enough send buffers to contain the given
290 * buffers, a set of optional HTTP headers, and an optional HTTP
291 * chunked header/footer pair. Use the stack if possible.
292 */
293
294 neededBufs = (size_t)nbufs + 2u + 1u;
295 if (neededBufs > (sizeof(sbufs) / sizeof(struct iovec))) {
296 sbufPtr = ns_calloc(neededBufs, sizeof(struct iovec));
297 } else {
298 sbufPtr = sbufs;
299 }
300 nsbufs = 0;
301 sbufIdx = 0;
302
303 /*
304 * Work out the body length for non-chunking case.
305 */
306
307 bodyLength = (bufs != NULL((void*)0)) ? Ns_SumVec(bufs, nbufs) : 0u;
308 toWrite = 0u;
309
310 if ((flags & NS_CONN_STREAM0x040u) != 0u) {
311 conn->flags |= NS_CONN_STREAM0x040u;
312 }
313
314 /*
315 * Send headers if not already sent.
316 */
317
318 if (((conn->flags & NS_CONN_SENTHDRS0x010u) == 0u)) {
319 conn->flags |= NS_CONN_SENTHDRS0x010u;
320 if (Ns_CompleteHeaders(conn, bodyLength, flags, &ds) == NS_TRUE1) {
321 toWrite += Ns_SetVec(sbufPtr, sbufIdx++,
322 Ns_DStringValue(&ds)((&ds)->string),
323 (size_t)Ns_DStringLength(&ds)((&ds)->length));
324 nsbufs++;
325 }
326 }
327
328 /*
329 * Send body.
330 */
331
332 if ((conn->flags & NS_CONN_SKIPBODY0x004u) == 0u) {
333
334 if ((conn->flags & NS_CONN_CHUNK0x100u) == 0u) {
335
336 /*
337 * Output content without chunking header/trailers.
338 */
339
340 if (sbufIdx == 0) {
341 sbufPtr = bufs;
342 nsbufs = nbufs;
343 } else if (nbufs > 0) {
344 NS_NONNULL_ASSERT(bufs != NULL)((void) (0));
345 (void) memcpy(sbufPtr + sbufIdx, bufs, (size_t)nbufs * sizeof(struct iovec));
346 nsbufs += nbufs;
347 }
348 toWrite += bodyLength;
349
350 } else {
351
352 /*
353 * Output content with chunking header/trailers.
354 */
355
356 if (bodyLength > 0u) {
357 size_t len;
358
359 assert(nbufs > 0)((void) (0));
360 assert(bufs != NULL)((void) (0));
361
362 /*
363 * Output length header followed by content and then
364 * trailer.
365 */
366 len = (size_t)snprintf(hdr, sizeof(hdr), "%lx\r\n", (unsigned long)bodyLength)__builtin___snprintf_chk (hdr, sizeof(hdr), 2 - 1, __builtin_object_size
(hdr, 2 > 1), "%lx\r\n", (unsigned long)bodyLength)
;
367 toWrite += Ns_SetVec(sbufPtr, sbufIdx++, hdr, len);
368
369 (void) memcpy(sbufPtr + sbufIdx, bufs, (size_t)nbufs * sizeof(struct iovec));
370 sbufIdx += nbufs;
371 toWrite += bodyLength;
372
373 toWrite += Ns_SetVec(sbufPtr, sbufIdx++, "\r\n", (size_t)2);
374
375 nsbufs += nbufs + 2;
376 }
377
378 if ((flags & NS_CONN_STREAM_CLOSE0x080u) != 0u) {
379 /*
380 * Output end-of-content trailer for chunked encoding
381 */
382
383 toWrite += Ns_SetVec(sbufPtr, sbufIdx, "0\r\n\r\n", (size_t)5);
384
385 nsbufs += 1;
386 conn->flags &= ~NS_CONN_STREAM0x040u;
387 conn->flags |= NS_CONN_SENT_LAST_CHUNK0x200u;
388 }
389 }
390 }
391
392 /*
393 * Write the output buffer.
394 */
395
396 nwrote = Ns_ConnSend(conn, sbufPtr, nsbufs);
397
398 Ns_DStringFreeTcl_DStringFree(&ds);
399 if (sbufPtr != sbufs && sbufPtr != bufs) {
400 ns_free(sbufPtr);
401 }
402
403 return (nwrote < (ssize_t)toWrite) ? NS_ERROR : NS_OK;
404}
405
406
407/*
408 *----------------------------------------------------------------------
409 *
410 * Ns_ConnSendChannel, Fp, Fd --
411 *
412 * Send some number of bytes to an open channel, FILE or fd.
413 * If the number is negative, send until EOF condition on source.
414 *
415 * Results:
416 * NS_OK/NS_ERROR.
417 *
418 * Side effects:
419 * See ConnSend().
420 *
421 *----------------------------------------------------------------------
422 */
423
424Ns_ReturnCode
425Ns_ConnSendChannel(Ns_Conn *conn, Tcl_Channel chan, ssize_t nsend)
426{
427 return ConnSend(conn, nsend, chan, NULL((void*)0), -1);
428}
429
430Ns_ReturnCode
431Ns_ConnSendFp(Ns_Conn *conn, FILE *fp, ssize_t nsend)
432{
433 return ConnSend(conn, nsend, NULL((void*)0), fp, -1);
434}
435
436Ns_ReturnCode
437Ns_ConnSendFd(Ns_Conn *conn, int fd, ssize_t nsend)
438{
439 return ConnSend(conn, nsend, NULL((void*)0), NULL((void*)0), fd);
440}
441
442
443/*
444 *----------------------------------------------------------------------
445 *
446 * ConnSend --
447 *
448 * Send an open channel, FILE or fd. Read the content from the
449 * various sources into a buffer and send the data to the client
450 * via Ns_ConnWriteVData(). Stop transmission on error, when all
451 * requested data was sent or EOF condition on channel/FILE/fd.
452 *
453 * Results:
454 * NS_OK/NS_ERROR.
455 *
456 * Side effects:
457 * Send data to client
458 *
459 *----------------------------------------------------------------------
460 */
461static Ns_ReturnCode
462ConnSend(Ns_Conn *conn, ssize_t nsend, Tcl_Channel chan, FILE *fp, int fd)
463{
464 Ns_ReturnCode status;
465 unsigned int flags = 0;
466
467 NS_NONNULL_ASSERT(conn != NULL)((void) (0));
468 assert(chan != NULL || fp != NULL || fd > -1)((void) (0));
469
470 if (nsend == 0) {
471 /*
472 * Even if no data to send, ensure HTTP response headers get written.
473 */
474 status = Ns_ConnWriteVData(conn, NULL((void*)0), 0, flags);
475
476 } else {
477 bool_Bool stream = NS_FALSE0, eod = NS_FALSE0;
478 char buf[IOBUFSZ8192];
479 struct iovec vbuf;
480
481 vbuf.iov_base = (void *)buf;
482
483 /*
484 * Turn-on http-streaming for unknown content/data length
485 */
486 if (nsend == -1) {
487 stream = NS_TRUE1;
488 flags |= NS_CONN_STREAM0x040u;
489 }
490
491 /*
492 * Read from disk and send in (max) IOBUFSZ chunks until
493 * all requested data was sent or until EOF condition on source.
494 */
495
496 status = NS_OK;
497
498 while (status == NS_OK && (nsend > 0 || (stream && !eod))) {
499 ssize_t nread = 0;
500 size_t toRead = 0;
501
502 if (stream) {
503 toRead = sizeof(buf);
504 } else {
505 toRead = ((size_t)nsend > sizeof(buf)) ? sizeof(buf) : (size_t)nsend;
506 }
507 if (chan != NULL((void*)0)) {
508 nread = Tcl_Read(chan, buf, (int)toRead);
509 if (stream && Tcl_Eof(chan)) {
510 eod = NS_TRUE1;
511 }
512 } else if (fp != NULL((void*)0)) {
513 nread = (int)fread(buf, 1u, toRead, fp);
514 if (ferror(fp)) {
515 nread = -1;
516 } else if (stream && feof(fp)) {
517 eod = NS_TRUE1;
518 }
519 } else if (fd > -1) {
520 nread = ns_readread(fd, buf, toRead);
521 if (stream && nread == 0) {
522 eod = NS_TRUE1;
523 }
524 } else {
525 status = NS_ERROR; /* Should never be reached */
526 }
527 if (nread == -1 || (!stream && nread == 0) /* NB: truncated file */) {
528 status = NS_ERROR;
529 } else if (nread > 0) {
530 vbuf.iov_len = (size_t)nread;
531 status = Ns_ConnWriteVData(conn, &vbuf, 1, flags);
532 if (status == NS_OK && !stream) {
533 nsend -= nread;
534 }
535 }
536 }
537 }
538
539 return status;
540}
541
542
543/*
544 *----------------------------------------------------------------------
545 *
546 * Ns_ConnSendFileVec --
547 *
548 * Send a vector of file buffers directly to the connection socket.
549 * Promises to send all of the data.
550 *
551 * Results:
552 * NS_OK if all data sent, NS_ERROR otherwise.
553 *
554 * Side effects:
555 * Will update connPtr->nContentSent.
556 *
557 *----------------------------------------------------------------------
558 */
559
560Ns_ReturnCode
561Ns_ConnSendFileVec(Ns_Conn *conn, Ns_FileVec *bufs, int nbufs)
562{
563 Conn *connPtr;
564 Sock *sockPtr;
565 int i;
566 size_t nwrote = 0u, towrite = 0u;
567 Ns_Time waitTimeout;
568
569 NS_NONNULL_ASSERT(conn != NULL)((void) (0));
570 NS_NONNULL_ASSERT(bufs != NULL)((void) (0));
571
572 connPtr = (Conn *)conn;
573 sockPtr = (Sock *)connPtr->sockPtr;
574
575 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
576 NS_NONNULL_ASSERT(sockPtr->drvPtr != NULL)((void) (0));
577
578 waitTimeout.sec = sockPtr->drvPtr->sendwait.sec;
579 waitTimeout.usec = sockPtr->drvPtr->sendwait.usec;
580
581 for (i = 0; i < nbufs; i++) {
582 towrite += bufs[i].length;
583 }
584
585 while (nwrote < towrite) {
586 ssize_t sent;
587
588 sent = NsDriverSendFile(sockPtr, bufs, nbufs, 0u);
589 if (sent == -1) {
590 break;
591 }
592 nwrote += (size_t)sent;
593 if (nwrote < towrite) {
594 Ns_Sock *sock = (Ns_Sock *)sockPtr;
595
596 if (sent > 0) {
597 (void)Ns_ResetFileVec(bufs, nbufs, (size_t)sent);
598 }
599 if (Ns_SockTimedWait(sock->sock, NS_SOCK_WRITE,
600 &waitTimeout) != NS_OK) {
601 break;
602 }
603 }
604 }
605
606 if (likely(nwrote > 0u)(__builtin_expect((nwrote > 0u), 1))) {
607 connPtr->nContentSent += nwrote;
608 }
609
610 return (nwrote != towrite) ? NS_ERROR : NS_OK;
611}
612
613
614
615/*
616 *----------------------------------------------------------------------
617 *
618 * Ns_ConnPuts --
619 *
620 * Write a null-terminated string directly to the conn; no
621 * trailing newline will be appended despite the name.
622 *
623 * Results:
624 * NS_OK or NS_ERROR.
625 *
626 * Side effects:
627 * See Ns_ConnWriteVData().
628 *
629 *----------------------------------------------------------------------
630 */
631
632Ns_ReturnCode
633Ns_ConnPuts(Ns_Conn *conn, const char *s)
634{
635 struct iovec vbuf;
636
637 NS_NONNULL_ASSERT(conn != NULL)((void) (0));
638 NS_NONNULL_ASSERT(s != NULL)((void) (0));
639
640 vbuf.iov_base = (void *) s;
641 vbuf.iov_len = strlen(s);
642
643 return Ns_ConnWriteVData(conn, &vbuf, 1, NS_CONN_STREAM0x040u);
644}
645
646
647/*
648 *----------------------------------------------------------------------
649 *
650 * Ns_ConnSendDString --
651 *
652 * Write contents of a DString directly to the conn.
653 *
654 * Results:
655 * NS_OK or NS_ERROR.
656 *
657 * Side effects:
658 * See Ns_ConnWriteVData().
659 *
660 *----------------------------------------------------------------------
661 */
662
663Ns_ReturnCode
664Ns_ConnSendDString(Ns_Conn *conn, const Ns_DStringTcl_DString *dsPtr)
665{
666 struct iovec vbuf;
667
668 NS_NONNULL_ASSERT(conn != NULL)((void) (0));
669 NS_NONNULL_ASSERT(dsPtr != NULL)((void) (0));
670
671 vbuf.iov_base = dsPtr->string;
672 vbuf.iov_len = (size_t)dsPtr->length;
673
674 return Ns_ConnWriteVData(conn, &vbuf, 1, NS_CONN_STREAM0x040u);
675}
676
677
678/*
679 *----------------------------------------------------------------------
680 *
681 * Ns_ConnSend --
682 *
683 * Send buffers to the connection socket efficiently.
684 * It promises to send all data.
685 *
686 * Results:
687 * Number of bytes sent, -1 on error.
688 *
689 * Side effects:
690 * Will update connPtr->nContentSent.
691 *
692 *----------------------------------------------------------------------
693 */
694
695ssize_t
696Ns_ConnSend(Ns_Conn *conn, struct iovec *bufs, int nbufs)
697{
698 ssize_t sent;
699 int i;
700 size_t towrite = 0u;
701
702 for (i = 0; i < nbufs; i++) {
703 towrite += bufs[i].iov_len;
704 }
705
706 if (towrite == 0u) {
707 sent = 0;
708
709 } else if (NsWriterQueue(conn, towrite, NULL((void*)0), NULL((void*)0), NS_INVALID_FD(-1),
710 bufs, nbufs, NULL((void*)0), 0, NS_FALSE0) == NS_OK) {
711 Ns_Log(Debug, "==== writer sent %" PRIuz"zu" " bytes\n", towrite);
712 sent = (ssize_t)towrite;
713
714 } else {
715 Ns_Time waitTimeout;
716 Conn *connPtr;
717 Sock *sockPtr;
718
719 NS_NONNULL_ASSERT(conn != NULL)((void) (0));
720
721 connPtr = (Conn *)conn;
722 sockPtr = connPtr->sockPtr;
723
724 NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0));
725 NS_NONNULL_ASSERT(sockPtr->drvPtr != NULL)((void) (0));
726
727 waitTimeout.sec = sockPtr->drvPtr->sendwait.sec;
728 waitTimeout.usec = sockPtr->drvPtr->sendwait.usec;
729
730 sent = Ns_SockSendBufs((Ns_Sock*)sockPtr, bufs, nbufs, &waitTimeout, 0u);
731
732 if (likely(sent > 0)(__builtin_expect((sent > 0), 1))) {
733 connPtr->nContentSent += (size_t)sent;
734 }
735 NsPoolAddBytesSent(((Conn *)conn)->poolPtr, (Tcl_WideInt)connPtr->nContentSent);
736 }
737
738 return sent;
739}
740
741
742/*
743 *----------------------------------------------------------------------
744 *
745 * Ns_ConnFlushContent --
746 *
747 * Finish reading waiting content.
748 *
749 * Results:
750 * NS_OK.
751 *
752 * Side effects:
753 * None.
754 *
755 *----------------------------------------------------------------------
756 */
757
758Ns_ReturnCode
759Ns_ConnFlushContent(const Ns_Conn *conn)
760{
761 const Conn *connPtr = (const Conn *) conn;
762 Request *reqPtr = connPtr->reqPtr;
763 Ns_ReturnCode status = NS_OK;
764
765 if (connPtr->sockPtr == NULL((void*)0)) {
766 status = NS_ERROR;
767 } else {
768 reqPtr->next += reqPtr->avail;
769 reqPtr->avail = 0u;
770 }
771 return status;
772}
773
774
775/*
776 *-----------------------------------------------------------------
777 *
778 * Ns_ConnClose --
779 *
780 * Return a connection to the driver thread for close or
781 * keep-alive.
782 *
783 * Results:
784 * Always NS_OK.
785 *
786 * Side effects:
787 * May trigger writing http-chunked trailer.
788 * Tcl at-close callbacks may run.
789 *
790 *-----------------------------------------------------------------
791 */
792
793Ns_ReturnCode
794Ns_ConnClose(Ns_Conn *conn)
795{
796 Conn *connPtr;
797
798 NS_NONNULL_ASSERT(conn != NULL)((void) (0));
799
800 connPtr = (Conn *) conn;
801 Ns_Log(Debug, "Ns_ConnClose %p stream %.6x chunk %.6x via writer %.6x sockPtr %p",
802 (void *)connPtr,
803 connPtr->flags & NS_CONN_STREAM0x040u,
804 connPtr->flags & NS_CONN_CHUNK0x100u,
805 connPtr->flags & NS_CONN_SENT_VIA_WRITER0x400u,
806 (void *)connPtr->sockPtr);
807
808 if (connPtr->sockPtr != NULL((void*)0)) {
809
810 if ((connPtr->flags & NS_CONN_STREAM0x040u) != 0u
811 && ((connPtr->flags & NS_CONN_CHUNK0x100u) != 0u
812 || (connPtr->compress > 0)
813 )) {
814 /*
815 * Streaming:
816 * In chunked mode, write the end-of-content trailer.
817 * If compressing, write the gzip footer.
818 */
819 (void) Ns_ConnWriteVChars(conn, NULL((void*)0), 0, NS_CONN_STREAM_CLOSE0x080u);
820 }
821
822 /*
823 * Close the connection to the client either here or in the
824 * writer thread.
825 */
826 if ((connPtr->flags & NS_CONN_SENT_VIA_WRITER0x400u) == 0u) {
827 NsSockClose(connPtr->sockPtr, connPtr->keep);
828 }
829
830
831 connPtr->sockPtr = NULL((void*)0);
832 connPtr->flags |= NS_CONN_CLOSED0x001u;
833 Ns_Log(Ns_LogRequestDebug, "connection closed");
834
835 if (connPtr->itPtr != NULL((void*)0)) {
836 NsTclRunAtClose(connPtr->itPtr);
837 }
838 }
839
840 return NS_OK;
841}
842
843
844/*
845 *----------------------------------------------------------------------
846 *
847 * Ns_ConnWrite, Ns_WriteConn, Ns_WriteCharConn --
848 *
849 * Deprecated.
850 *
851 * Results:
852 * #bytes / NS_OK / NS_ERROR
853 *
854 * Side effects:
855 * See Ns_ConnWrite*
856 *
857 *----------------------------------------------------------------------
858 */
859
860int
861Ns_ConnWrite(Ns_Conn *conn, const void *buf, size_t toWrite)
862{
863 const Conn *connPtr = (const Conn *) conn;
864 size_t n;
865 Ns_ReturnCode status;
866 int result;
867 struct iovec vbuf;
868
869 vbuf.iov_base = (void *) buf;
870 vbuf.iov_len = toWrite;
871
872 n = connPtr->nContentSent;
873 status = Ns_ConnWriteVData(conn, &vbuf, 1, 0u);
874 if (status == NS_OK) {
875 result = (int)connPtr->nContentSent - (int)n;
876 } else {
877 result = -1;
878 }
879 return result;
880}
881
882Ns_ReturnCode
883Ns_WriteConn(Ns_Conn *conn, const char *buf, size_t toWrite)
884{
885 struct iovec vbuf;
886
887 /* Deprecated for Ns_ConnWriteVData */
888
889 NS_NONNULL_ASSERT(conn != NULL)((void) (0));
890
891 vbuf.iov_base = (void *) buf;
892 vbuf.iov_len = toWrite;
893
894 return Ns_ConnWriteVData(conn, &vbuf, 1, NS_CONN_STREAM0x040u);
895}
896
897Ns_ReturnCode
898Ns_WriteCharConn(Ns_Conn *conn, const char *buf, size_t toWrite)
899{
900 struct iovec sbuf;
901
902 sbuf.iov_base = (void *)buf;
903 sbuf.iov_len = toWrite;
904
905 return Ns_ConnWriteVChars(conn, &sbuf, 1, NS_CONN_STREAM0x040u);
906}
907
908
909/*
910 *----------------------------------------------------------------------
911 *
912 * Ns_ConnGets --
913 *
914 * Read in a string from a connection, stopping when either we've
915 * run out of data, hit a newline, or had an error.
916 *
917 * Results:
918 * Pointer to given buffer or NULL on error.
919 *
920 * Side effects:
921 *
922 *
923 *----------------------------------------------------------------------
924 */
925
926char *
927Ns_ConnGets(char *buf, size_t bufsize, const Ns_Conn *conn)
928{
929 char *p, *result = buf;
930
931 NS_NONNULL_ASSERT(buf != NULL)((void) (0));
932 NS_NONNULL_ASSERT(conn != NULL)((void) (0));
933
934 p = buf;
935 while (bufsize > 1u) {
936 if (Ns_ConnRead(conn, p, 1u) != 0u) {
937 result = NULL((void*)0);
938 break;
939 }
940 if (*p++ == '\n') {
941 break;
942 }
943 --bufsize;
944 }
945 if (likely(result != NULL)(__builtin_expect((result != ((void*)0)), 1))) {
946 *p = '\0';
947 }
948
949 return result;
950}
951
952
953/*
954 *----------------------------------------------------------------------
955 *
956 * Ns_ConnRead --
957 *
958 * Copy data from read-ahead buffers.
959 *
960 * Results:
961 * Number of bytes copied.
962 *
963 * Side effects:
964 * None.
965 *
966 *----------------------------------------------------------------------
967 */
968
969size_t
970Ns_ConnRead(const Ns_Conn *conn, void *vbuf, size_t toRead)
971{
972 const Conn *connPtr = (const Conn *) conn;
973 Request *reqPtr = connPtr->reqPtr;
974
975 if (connPtr->sockPtr == NULL((void*)0)) {
976 toRead = 0u;
977 } else {
978 if (toRead > reqPtr->avail) {
979 toRead = reqPtr->avail;
980 }
981 memcpy(vbuf, reqPtr->next, toRead);
982 reqPtr->next += toRead;
983 reqPtr->avail -= toRead;
984 }
985
986 return toRead;
987}
988
989
990/*
991 *----------------------------------------------------------------------
992 *
993 * Ns_ConnReadLine --
994 *
995 * Read a line (\r\n or \n terminated) from the conn.
996 *
997 * Results:
998 * NS_OK if a line was read. NS_ERROR if no line ending
999 * was found or the line would be too long.
1000 *
1001 * Side effects:
1002 * Stuff may be read
1003 *
1004 *----------------------------------------------------------------------
1005 */
1006
1007Ns_ReturnCode
1008Ns_ConnReadLine(const Ns_Conn *conn, Ns_DStringTcl_DString *dsPtr, size_t *nreadPtr)
1009{
1010 const Conn *connPtr;
1011 Request *reqPtr;
1012 const Driver *drvPtr;
1013 const char *eol;
1014 Ns_ReturnCode status;
1015
1016 NS_NONNULL_ASSERT(conn != NULL)((void) (0));
1017 NS_NONNULL_ASSERT(dsPtr != NULL)((void) (0));
1018
1019 connPtr = (const Conn *) conn;
1020 reqPtr = connPtr->reqPtr;
1021 assert(reqPtr != NULL)((void) (0));
1022
1023 drvPtr = connPtr->drvPtr;
1024 eol = strchr(reqPtr->next, INTCHAR('\n')((int)((unsigned char)(('\n')))));
1025
1026 if ((connPtr->sockPtr == NULL((void*)0)) || (eol == NULL((void*)0))) {
4
Assuming field 'sockPtr' is not equal to NULL
5
Assuming 'eol' is not equal to NULL
6
Taking false branch
1027 status = NS_ERROR;
1028 } else {
1029 ptrdiff_t nread = eol - reqPtr->next;
1030
1031 if (nread > drvPtr->maxline) {
7
Assuming 'nread' is <= field 'maxline'
8
Taking false branch
1032 status = NS_ERROR;
1033 } else {
1034 ptrdiff_t ncopy = nread;
1035
1036 ++nread;
1037 if (nreadPtr
8.1
'nreadPtr' is not equal to NULL
!= NULL((void*)0)) {
9
Taking true branch
1038 *nreadPtr = (size_t)nread;
1039 }
1040
1041 /*
1042 * Read from the end of the buffer until we either reach
1043 * ncopy == 0 (this means the start of the buffer), or
1044 * until we find a '\r'.
1045 */
1046 if (ncopy > 0u && *(eol-1) == '\r') {
10
Assuming 'ncopy' is > 0
11
Access out-of-bound array element (buffer overflow)
1047 --ncopy;
1048 }
1049 Ns_DStringNAppendTcl_DStringAppend(dsPtr, reqPtr->next, (int)ncopy);
1050 reqPtr->next += nread;
1051 reqPtr->avail -= (size_t)nread;
1052
1053 status = NS_OK;
1054 }
1055 }
1056 return status;
1057}
1058
1059
1060/*
1061 *----------------------------------------------------------------------
1062 *
1063 * Ns_ConnReadHeaders --
1064 *
1065 * Read the headers and insert them into the passed-in set.
1066 *
1067 * Results:
1068 * NS_OK/NS_ERROR
1069 *
1070 * Side effects:
1071 * Stuff will be read from the conn.
1072 *
1073 *----------------------------------------------------------------------
1074 */
1075
1076Ns_ReturnCode
1077Ns_ConnReadHeaders(const Ns_Conn *conn, Ns_Set *set, size_t *nreadPtr)
1078{
1079 Ns_DStringTcl_DString ds;
1080 const Conn *connPtr = (const Conn *) conn;
1081 size_t nread, maxhdr;
1082 Ns_ReturnCode status = NS_OK;
1083
1084 Ns_DStringInitTcl_DStringInit(&ds);
1085 nread = 0u;
1086 maxhdr = (size_t)connPtr->drvPtr->maxheaders;
1087 while (nread < maxhdr && status == NS_OK) {
1
Assuming 'nread' is < 'maxhdr'
2
Loop condition is true. Entering loop body
1088 size_t nline;
1089
1090 Ns_DStringSetLengthTcl_DStringSetLength(&ds, 0);
1091 status = Ns_ConnReadLine(conn, &ds, &nline);
3
Calling 'Ns_ConnReadLine'
1092 if (status == NS_OK) {
1093 nread += nline;
1094 if (nread > maxhdr) {
1095 status = NS_ERROR;
1096 } else {
1097 if (ds.string[0] == '\0') {
1098 break;
1099 }
1100 status = Ns_ParseHeader(set, ds.string, NULL((void*)0),
1101 connPtr->poolPtr->servPtr->opts.hdrcase,
1102 NULL((void*)0));
1103 }
1104 }
1105 }
1106 if (nreadPtr != NULL((void*)0)) {
1107 *nreadPtr = nread;
1108 }
1109 Ns_DStringFreeTcl_DStringFree(&ds);
1110
1111 return status;
1112}
1113
1114
1115/*
1116 *----------------------------------------------------------------------
1117 *
1118 * Ns_ConnCopyToDString --
1119 *
1120 * Copy data from a connection to a dstring.
1121 *
1122 * Results:
1123 * NS_OK or NS_ERROR
1124 *
1125 * Side effects:
1126 * None.
1127 *
1128 *----------------------------------------------------------------------
1129 */
1130
1131Ns_ReturnCode
1132Ns_ConnCopyToDString(const Ns_Conn *conn, size_t toCopy, Ns_DStringTcl_DString *dsPtr)
1133{
1134 const Conn *connPtr;
1135 Request *reqPtr;
1136 Ns_ReturnCode status = NS_OK;
1137
1138 NS_NONNULL_ASSERT(conn != NULL)((void) (0));
1139 NS_NONNULL_ASSERT(dsPtr != NULL)((void) (0));
1140
1141 connPtr = (const Conn *)conn;
1142 reqPtr = connPtr->reqPtr;
1143
1144 if (connPtr->sockPtr == NULL((void*)0) || reqPtr->avail < toCopy) {
1145 status = NS_ERROR;
1146 } else {
1147 Ns_DStringNAppendTcl_DStringAppend(dsPtr, reqPtr->next, (int)toCopy);
1148 reqPtr->next += toCopy;
1149 reqPtr->avail -= toCopy;
1150 }
1151
1152 return status;
1153}
1154
1155
1156/*
1157 *----------------------------------------------------------------------
1158 *
1159 * Ns_ConnCopyToFile, Fd, Channel --
1160 *
1161 * Copy data from a connection to a channel, FILE, or fd.
1162 *
1163 * Results:
1164 * NS_OK or NS_ERROR
1165 *
1166 * Side effects:
1167 * See ConnCopy().
1168 *
1169 *----------------------------------------------------------------------
1170 */
1171
1172Ns_ReturnCode
1173Ns_ConnCopyToChannel(const Ns_Conn *conn, size_t ncopy, Tcl_Channel chan)
1174{
1175 return ConnCopy(conn, ncopy, chan, NULL((void*)0), -1);
1176}
1177
1178Ns_ReturnCode
1179Ns_ConnCopyToFile(const Ns_Conn *conn, size_t ncopy, FILE *fp)
1180{
1181 return ConnCopy(conn, ncopy, NULL((void*)0), fp, -1);
1182}
1183
1184Ns_ReturnCode
1185Ns_ConnCopyToFd(const Ns_Conn *conn, size_t ncopy, int fd)
1186{
1187 return ConnCopy(conn, ncopy, NULL((void*)0), NULL((void*)0), fd);
1188}
1189
1190static Ns_ReturnCode
1191ConnCopy(const Ns_Conn *conn, size_t toCopy, Tcl_Channel chan, FILE *fp, int fd)
1192{
1193 const Conn *connPtr;
1194 Request *reqPtr;
1195 size_t ncopy = toCopy;
1196 Ns_ReturnCode status = NS_OK;
1197
1198 NS_NONNULL_ASSERT(conn != NULL)((void) (0));
1199
1200 connPtr = (const Conn *) conn;
1201 reqPtr = connPtr->reqPtr;
1202 assert(reqPtr != NULL)((void) (0));
1203
1204 if (connPtr->sockPtr == NULL((void*)0) || reqPtr->avail < toCopy) {
1205 status = NS_ERROR;
1206 } else {
1207 /*
1208 * There is data to copy.
1209 */
1210 while (ncopy > 0u) {
1211 ssize_t nwrote;
1212
1213 /*
1214 * Write it to the chan, or fp, or fd, depending on what
1215 * was provided.
1216 */
1217 if (chan != NULL((void*)0)) {
1218 nwrote = Tcl_Write(chan, reqPtr->next, (int)ncopy);
1219 } else if (fp != NULL((void*)0)) {
1220 nwrote = (ssize_t)fwrite(reqPtr->next, 1u, ncopy, fp);
1221 if (ferror(fp) != 0) {
1222 nwrote = -1;
1223 }
1224 } else {
1225 nwrote = ns_writewrite(fd, reqPtr->next, ncopy);
1226 }
1227 if (nwrote < 0) {
1228 status = NS_ERROR;
1229 break;
1230 } else {
1231 ncopy -= (size_t)nwrote;
1232 reqPtr->next += nwrote;
1233 reqPtr->avail -= (size_t)nwrote;
1234 }
1235 }
1236 }
1237 return status;
1238}
1239
1240
1241/*
1242 *----------------------------------------------------------------------
1243 *
1244 * Ns_CompleteHeaders --
1245 *
1246 * Construct a set of headers including length, connection and
1247 * transfer-encoding and then dump them to the dstring.
1248 *
1249 * Results:
1250 * 1 if headers were dumped to dstring, 0 otherwise.
1251 *
1252 * Side effects:
1253 * The connections STREAM and/or CHUNK flags may be set.
1254 *
1255 *----------------------------------------------------------------------
1256 */
1257
1258bool_Bool
1259Ns_CompleteHeaders(Ns_Conn *conn, size_t dataLength,
1260 unsigned int flags, Ns_DStringTcl_DString *dsPtr)
1261{
1262 Conn *connPtr = (Conn *) conn;
1263 bool_Bool success;
1264
1265 NS_NONNULL_ASSERT(conn != NULL)((void) (0));
1266 NS_NONNULL_ASSERT(dsPtr != NULL)((void) (0));
1267
1268 if ((conn->flags & NS_CONN_SKIPHDRS0x002u) != 0u) {
1269 /*
1270 * Pre-HTTP/1.0 has no headers, and no keep-alive
1271 */
1272 if (conn->request.version < 1.0) {
1273 connPtr->keep = 0;
1274 }
1275 success = NS_FALSE0;
1276 } else {
1277 const char *keepString;
1278
1279 /*
1280 * Check for streaming vs. non-streaming.
1281 */
1282
1283 if ((flags & NS_CONN_STREAM0x040u) != 0u) {
1284
1285 conn->flags |= NS_CONN_STREAM0x040u;
1286
1287 if ((connPtr->responseLength < 0)
1288 && (conn->request.version > 1.0)
1289 && (connPtr->keep != 0)
1290 && (HdrEq(connPtr->outputheaders, "Content-Type",
1291 "multipart/byteranges") == NS_FALSE0)) {
1292 conn->flags |= NS_CONN_CHUNK0x100u;
1293 }
1294
1295 } else if (connPtr->responseLength < 0) {
1296 Ns_ConnSetLengthHeader(conn, dataLength, NS_FALSE0);
1297 }
1298
1299 /*
1300 * Set and construct the headers.
1301 */
1302
1303 connPtr->keep = (CheckKeep(connPtr) ? 1 : 0);
1304 if (connPtr->keep != 0) {
1305 keepString = "keep-alive";
1306 } else {
1307 keepString = "close";
1308 }
1309 Ns_ConnSetHeaders(conn, "Connection", keepString);
1310
1311 if ((conn->flags & NS_CONN_CHUNK0x100u) != 0u) {
1312 Ns_ConnSetHeaders(conn, "Transfer-Encoding", "chunked");
1313 }
1314 Ns_ConnConstructHeaders(conn, dsPtr);
1315 success = NS_TRUE1;
1316 }
1317
1318 return success;
1319}
1320
1321
1322/*
1323 *----------------------------------------------------------------------
1324 *
1325 * CheckKeep --
1326 *
1327 * Should the Connection header be set to keep-alive or close.
1328 *
1329 * Results:
1330 * NS_TRUE if keep-alive is allowed, NS_FALSE otherwise.
1331 *
1332 * Side effects:
1333 * None.
1334 *
1335 *----------------------------------------------------------------------
1336 */
1337
1338static bool_Bool
1339CheckKeep(const Conn *connPtr)
1340{
1341 bool_Bool result = NS_FALSE0;
1342
1343 NS_NONNULL_ASSERT(connPtr != NULL)((void) (0));
1344
1345 do {
1346 if (connPtr->drvPtr->keepwait.sec > 0 || connPtr->drvPtr->keepwait.usec > 0 ) {
1347 /*
1348 * Check for manual keep-alive override.
1349 */
1350
1351 if (connPtr->keep > 0) {
1352 result = NS_TRUE1;
1353 break;
1354 }
1355
1356 /*
1357 * Apply default rules.
1358 */
1359 if ((connPtr->keep == -1)
1360 && (connPtr->request.line != NULL((void*)0))) {
1361
1362 /*
1363 * HTTP 1.0/1.1 keep-alive header checks.
1364 */
1365 if (( (connPtr->request.version == 1.0)
1366 && (HdrEq(connPtr->headers, "connection", "keep-alive") == NS_TRUE1) )
1367 || ( (connPtr->request.version > 1.0)
1368 && (HdrEq(connPtr->headers, "connection", "close") == NS_FALSE0) )
1369 ) {
1370
1371 /*
1372 * POST, PUT etc. require a content-length header
1373 * to allow keep-alive.
1374 */
1375 if ((connPtr->contentLength > 0u)
1376 && (Ns_SetIGet(connPtr->headers, "Content-Length") == NULL((void*)0))) {
1377 /*
1378 * No content length -> disallow.
1379 */
1380 break;
1381 }
1382
1383 if ( (connPtr->drvPtr->keepmaxuploadsize > 0u)
1384 && (connPtr->contentLength > connPtr->drvPtr->keepmaxuploadsize) ) {
1385 Ns_Log(Notice,
1386 "Disallow keep-alive: content-Length %" PRIdz"zd"
1387 " larger keepmaxuploadsize %" PRIdz"zd" ": %s",
1388 connPtr->contentLength, connPtr->drvPtr->keepmaxuploadsize,
1389 connPtr->request.line);
1390 break;
1391 } else if ( (connPtr->drvPtr->keepmaxdownloadsize > 0u)
1392 && (connPtr->responseLength > 0)
1393 && ((size_t)connPtr->responseLength > connPtr->drvPtr->keepmaxdownloadsize) ) {
1394 Ns_Log(Notice,
1395 "Disallow keep-alive: response length %" PRIdz"zd" " "
1396 "larger keepmaxdownloadsize %" PRIdz"zd" ": %s",
1397 connPtr->responseLength, connPtr->drvPtr->keepmaxdownloadsize,
1398 connPtr->request.line);
1399 break;
1400 }
1401
1402 /*
1403 * We allow keep-alive for chunked encoding
1404 * variants or a valid content-length header.
1405 */
1406 if (((connPtr->flags & NS_CONN_CHUNK0x100u) != 0u)
1407 || (Ns_SetIGet(connPtr->outputheaders, "Content-Length") != NULL((void*)0))
1408 || (HdrEq(connPtr->outputheaders, "Content-Type", "multipart/byteranges") == NS_TRUE1)) {
1409
1410 result = NS_TRUE1;
1411 break;
1412 }
1413 }
1414 }
1415 }
1416 } while (NS_FALSE0); /* loop construct just for breaks */
1417
1418 /*
1419 * Don't allow keep-alive.
1420 */
1421 return result;
1422}
1423
1424
1425/*
1426 *----------------------------------------------------------------------
1427 *
1428 * HdrEq --
1429 *
1430 * Test if given set contains a key which matches given value.
1431 * Value is matched at the beginning of the header value only.
1432 *
1433 * Results:
1434 * NS_TRUE if there is a match, NS_FALSE otherwise.
1435 *
1436 * Side effects:
1437 * None.
1438 *
1439 *----------------------------------------------------------------------
1440 */
1441
1442static bool_Bool
1443HdrEq(const Ns_Set *set, const char *name, const char *value)
1444{
1445 const char *hdrvalue;
1446
1447 NS_NONNULL_ASSERT(set != NULL)((void) (0));
1448 NS_NONNULL_ASSERT(name != NULL)((void) (0));
1449 NS_NONNULL_ASSERT(value != NULL)((void) (0));
1450
1451 hdrvalue = Ns_SetIGet(set, name);
1452
1453 return ((hdrvalue != NULL((void*)0)) && (strncasecmp(hdrvalue, value, strlen(value)) == 0));
1454}
1455
1456/*
1457 * Local Variables:
1458 * mode: c
1459 * c-basic-offset: 4
1460 * fill-column: 70
1461 * indent-tabs-mode: nil
1462 * End:
1463 */