Lines 20-25
Link Here
|
20 |
* Author: Larry Isaacs <larryi@apache.org> * |
20 |
* Author: Larry Isaacs <larryi@apache.org> * |
21 |
* Author: Ignacio J. Ortega <nacho@apache.org> * |
21 |
* Author: Ignacio J. Ortega <nacho@apache.org> * |
22 |
* Author: Mladen Turk <mturk@apache.org> * |
22 |
* Author: Mladen Turk <mturk@apache.org> * |
|
|
23 |
* Author: Tim Whittington <tim.whittington@orionhealth.com * |
23 |
* Version: $Revision: 1.49 $ * |
24 |
* Version: $Revision: 1.49 $ * |
24 |
***************************************************************************/ |
25 |
***************************************************************************/ |
25 |
|
26 |
|
Lines 69-74
Link Here
|
69 |
#define URI_SELECT_UNPARSED_VERB ("unparsed") |
70 |
#define URI_SELECT_UNPARSED_VERB ("unparsed") |
70 |
#define URI_SELECT_ESCAPED_VERB ("escaped") |
71 |
#define URI_SELECT_ESCAPED_VERB ("escaped") |
71 |
|
72 |
|
|
|
73 |
#define ENABLE_CHUNKED_ENCODING_TAG ("enable_chunked_encoding") |
74 |
|
75 |
/* Headers used in negotiating chunked encoding */ |
76 |
#define TRANSFER_ENCODING_HEADER_COMPLETE ("Transfer-Encoding: chunked") |
77 |
#define TRANSFER_ENCODING_HEADER_NAME ("Transfer-Encoding") |
78 |
#define CONTENT_LENGTH_HEADER_NAME ("Content-Length") |
79 |
#define CONNECTION_HEADER_NAME ("Connection") |
80 |
#define CONNECTION_CLOSE_VALUE ("Close") |
81 |
|
72 |
#define BAD_REQUEST -1 |
82 |
#define BAD_REQUEST -1 |
73 |
#define BAD_PATH -2 |
83 |
#define BAD_PATH -2 |
74 |
#define MAX_SERVERNAME 128 |
84 |
#define MAX_SERVERNAME 128 |
Lines 115-120
Link Here
|
115 |
(place) = def; \ |
125 |
(place) = def; \ |
116 |
} } while(0) |
126 |
} } while(0) |
117 |
|
127 |
|
|
|
128 |
/* HTTP protocol CRLF */ |
129 |
static char CRLF[3] = { (char)13, (char)10, '\0' }; |
130 |
|
118 |
static char ini_file_name[MAX_PATH]; |
131 |
static char ini_file_name[MAX_PATH]; |
119 |
static int using_ini_file = JK_FALSE; |
132 |
static int using_ini_file = JK_FALSE; |
120 |
static int is_inited = JK_FALSE; |
133 |
static int is_inited = JK_FALSE; |
Lines 133-138
Link Here
|
133 |
static int log_level = JK_LOG_EMERG_LEVEL; |
146 |
static int log_level = JK_LOG_EMERG_LEVEL; |
134 |
static char worker_file[MAX_PATH * 2]; |
147 |
static char worker_file[MAX_PATH * 2]; |
135 |
static char worker_mount_file[MAX_PATH * 2] = {0}; |
148 |
static char worker_mount_file[MAX_PATH * 2] = {0}; |
|
|
149 |
/* Whether chunked encoding has been enabled in the settings */ |
150 |
static int chunked_encoding_enabled = JK_FALSE; |
136 |
|
151 |
|
137 |
#define URI_SELECT_OPT_PARSED 0 |
152 |
#define URI_SELECT_OPT_PARSED 0 |
138 |
#define URI_SELECT_OPT_UNPARSED 1 |
153 |
#define URI_SELECT_OPT_UNPARSED 1 |
Lines 150-155
Link Here
|
150 |
int request_started; |
165 |
int request_started; |
151 |
unsigned int bytes_read_so_far; |
166 |
unsigned int bytes_read_so_far; |
152 |
LPEXTENSION_CONTROL_BLOCK lpEcb; |
167 |
LPEXTENSION_CONTROL_BLOCK lpEcb; |
|
|
168 |
int chunk_content; /* Whether we're responding with Transfer-Encoding: chunked content */ |
153 |
}; |
169 |
}; |
154 |
|
170 |
|
155 |
typedef struct isapi_log_data_t isapi_log_data_t; |
171 |
typedef struct isapi_log_data_t isapi_log_data_t; |
Lines 170-175
Link Here
|
170 |
|
186 |
|
171 |
static int JK_METHOD write(jk_ws_service_t *s, const void *b, unsigned int l); |
187 |
static int JK_METHOD write(jk_ws_service_t *s, const void *b, unsigned int l); |
172 |
|
188 |
|
|
|
189 |
static int JK_METHOD flush_response(jk_ws_service_t *s); |
190 |
|
173 |
static int init_ws_service(isapi_private_data_t * private_data, |
191 |
static int init_ws_service(isapi_private_data_t * private_data, |
174 |
jk_ws_service_t *s, char **worker_name); |
192 |
jk_ws_service_t *s, char **worker_name); |
175 |
|
193 |
|
Lines 469-476
Link Here
|
469 |
const char *const *header_values, |
487 |
const char *const *header_values, |
470 |
unsigned int num_of_headers) |
488 |
unsigned int num_of_headers) |
471 |
{ |
489 |
{ |
472 |
static char crlf[3] = { (char)13, (char)10, '\0' }; |
|
|
473 |
|
474 |
JK_TRACE_ENTER(logger); |
490 |
JK_TRACE_ENTER(logger); |
475 |
if (status < 100 || status > 1000) { |
491 |
if (status < 100 || status > 1000) { |
476 |
jk_log(logger, JK_LOG_ERROR, |
492 |
jk_log(logger, JK_LOG_ERROR, |
Lines 482-491
Link Here
|
482 |
|
498 |
|
483 |
if (s && s->ws_private) { |
499 |
if (s && s->ws_private) { |
484 |
isapi_private_data_t *p = s->ws_private; |
500 |
isapi_private_data_t *p = s->ws_private; |
|
|
501 |
int keep_alive = JK_FALSE; /* Whether the downstream or us can supply content length */ |
502 |
|
485 |
if (!p->request_started) { |
503 |
if (!p->request_started) { |
486 |
size_t len_of_status; |
504 |
HSE_SEND_HEADER_EX_INFO send_header_ex_info; |
487 |
char *status_str; |
505 |
char *status_str; |
488 |
char *headers_str; |
506 |
char *headers_str; |
|
|
507 |
int keep_alive = JK_FALSE; /* Whether the downstream or us can supply content length */ |
489 |
|
508 |
|
490 |
p->request_started = JK_TRUE; |
509 |
p->request_started = JK_TRUE; |
491 |
|
510 |
|
Lines 497-542
Link Here
|
497 |
} |
516 |
} |
498 |
status_str = (char *)_alloca((6 + strlen(reason)) * sizeof(char)); |
517 |
status_str = (char *)_alloca((6 + strlen(reason)) * sizeof(char)); |
499 |
sprintf(status_str, "%d %s", status, reason); |
518 |
sprintf(status_str, "%d %s", status, reason); |
500 |
len_of_status = strlen(status_str); |
|
|
501 |
|
519 |
|
502 |
/* |
520 |
/* |
503 |
* Create response headers string |
521 |
* Create response headers string |
504 |
*/ |
522 |
*/ |
505 |
if (num_of_headers) { |
523 |
if (num_of_headers) { |
|
|
524 |
int chunked_ok = JK_FALSE; /* Whether the downstream response allows chunking */ |
525 |
int http11_request = JK_FALSE; /* Whether the client is HTTP/1.1 */ |
526 |
|
506 |
size_t i, len_of_headers; |
527 |
size_t i, len_of_headers; |
|
|
528 |
|
529 |
/* Check if we've got an HTTP/1.1 response */ |
530 |
jk_log(logger, JK_LOG_DEBUG, |
531 |
"jk_ws_service_t::start_response, Request uses protocol %s\n", s->protocol); |
532 |
|
533 |
if (strcasecmp(s->protocol, "HTTP/1.1")==0) { |
534 |
http11_request = JK_TRUE; |
535 |
chunked_ok = JK_TRUE; /* Chunking only on HTTP/1.1 */ |
536 |
} |
537 |
|
507 |
for (i = 0, len_of_headers = 0; i < num_of_headers; i++) { |
538 |
for (i = 0, len_of_headers = 0; i < num_of_headers; i++) { |
508 |
len_of_headers += strlen(header_names[i]); |
539 |
len_of_headers += strlen(header_names[i]); |
509 |
len_of_headers += strlen(header_values[i]); |
540 |
len_of_headers += strlen(header_values[i]); |
510 |
len_of_headers += 4; /* extra for colon, space and crlf */ |
541 |
len_of_headers += 4; /* extra for colon, space and crlf */ |
511 |
} |
542 |
} |
512 |
|
543 |
|
|
|
544 |
/* Provide room in the buffer for the Transfer-Encoding header if we use it. */ |
545 |
len_of_headers += strlen(TRANSFER_ENCODING_HEADER_COMPLETE) + 2; |
546 |
|
513 |
len_of_headers += 3; /* crlf and terminating null char */ |
547 |
len_of_headers += 3; /* crlf and terminating null char */ |
514 |
headers_str = (char *)_alloca(len_of_headers * sizeof(char)); |
548 |
headers_str = (char *)_alloca(len_of_headers * sizeof(char)); |
515 |
headers_str[0] = '\0'; |
549 |
headers_str[0] = '\0'; |
516 |
|
550 |
|
517 |
for (i = 0; i < num_of_headers; i++) { |
551 |
for (i = 0; i < num_of_headers; i++) { |
|
|
552 |
/* Check the downstream response to see whether |
553 |
it's appropriate the chunk the response content |
554 |
and whether it supports keeping the connection open */ |
555 |
if(strcasecmp(TRANSFER_ENCODING_HEADER_NAME, header_names[i])==0) { |
556 |
keep_alive = http11_request; |
557 |
chunked_ok = JK_FALSE; |
558 |
} |
559 |
else if(strcasecmp(CONTENT_LENGTH_HEADER_NAME, header_names[i])==0) { |
560 |
keep_alive = http11_request; |
561 |
chunked_ok = JK_FALSE; |
562 |
} |
563 |
else if((strcasecmp(CONNECTION_HEADER_NAME, header_names[i])==0) |
564 |
&& (strcasecmp(CONNECTION_CLOSE_VALUE, header_values[i])==0)) { |
565 |
keep_alive = JK_FALSE; |
566 |
chunked_ok = JK_FALSE; |
567 |
} |
568 |
|
518 |
strcat(headers_str, header_names[i]); |
569 |
strcat(headers_str, header_names[i]); |
519 |
strcat(headers_str, ": "); |
570 |
strcat(headers_str, ": "); |
520 |
strcat(headers_str, header_values[i]); |
571 |
strcat(headers_str, header_values[i]); |
521 |
strcat(headers_str, crlf); |
572 |
strcat(headers_str, CRLF); |
522 |
} |
573 |
} |
523 |
strcat(headers_str, crlf); |
574 |
|
|
|
575 |
/* Check if we can send chunked content */ |
576 |
if (chunked_encoding_enabled && chunked_ok) { |
577 |
jk_log(logger, JK_LOG_DEBUG, |
578 |
"jk_ws_service_t::start_response, using Transfer-Encoding: chunked\n"); |
579 |
|
580 |
/** We will supply the transfer-encoding to allow IIS to keep the connection open */ |
581 |
keep_alive = JK_TRUE; |
582 |
|
583 |
p->chunk_content = JK_TRUE; |
584 |
/* Indicate to the client that the content will be chunked |
585 |
- We've already reserved space for this */ |
586 |
strcat(headers_str, TRANSFER_ENCODING_HEADER_COMPLETE); |
587 |
strcat(headers_str, CRLF); |
588 |
} |
589 |
else { |
590 |
p->chunk_content = JK_FALSE; |
591 |
} |
592 |
|
593 |
strcat(headers_str, CRLF); |
524 |
} |
594 |
} |
525 |
else { |
595 |
else { |
526 |
headers_str = crlf; |
596 |
headers_str = CRLF; |
527 |
} |
597 |
} |
528 |
|
598 |
|
529 |
if (!p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID, |
599 |
/** Fill in the response */ |
530 |
HSE_REQ_SEND_RESPONSE_HEADER, |
600 |
send_header_ex_info.pszStatus = status_str; |
531 |
status_str, |
601 |
send_header_ex_info.pszHeader = headers_str; |
532 |
(LPDWORD) &len_of_status, |
602 |
send_header_ex_info.cchStatus = strlen(status_str); |
533 |
(LPDWORD) headers_str)) { |
603 |
send_header_ex_info.cchHeader = strlen(headers_str); |
534 |
jk_log(logger, JK_LOG_ERROR, |
604 |
|
535 |
"HSE_REQ_SEND_RESPONSE_HEADER failed"); |
605 |
/* |
|
|
606 |
* Using the extended form of the API means we have to get this right, |
607 |
* i.e. IIS won't keep connections open if there's a Content-Length and close them if there isn't. |
608 |
*/ |
609 |
jk_log(logger, JK_LOG_DEBUG, |
610 |
"jk_ws_service_t::start_response, keep_alive = %d\n", keep_alive); |
611 |
|
612 |
send_header_ex_info.fKeepConn = keep_alive; |
613 |
|
614 |
if (!p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID, |
615 |
HSE_REQ_SEND_RESPONSE_HEADER_EX, |
616 |
&send_header_ex_info, |
617 |
NULL, |
618 |
NULL)) { |
619 |
jk_log(logger, JK_LOG_ERROR, |
620 |
"HSE_REQ_SEND_RESPONSE_HEADER failed"); |
536 |
JK_TRACE_EXIT(logger); |
621 |
JK_TRACE_EXIT(logger); |
537 |
return JK_FALSE; |
622 |
return JK_FALSE; |
538 |
} |
623 |
} |
539 |
} |
624 |
} |
|
|
625 |
|
540 |
JK_TRACE_EXIT(logger); |
626 |
JK_TRACE_EXIT(logger); |
541 |
return JK_TRUE; |
627 |
return JK_TRUE; |
542 |
|
628 |
|
Lines 602-607
Link Here
|
602 |
return JK_FALSE; |
688 |
return JK_FALSE; |
603 |
} |
689 |
} |
604 |
|
690 |
|
|
|
691 |
/* |
692 |
* Writes a buffer to the ISAPI response. |
693 |
*/ |
694 |
static int isapi_write_client(isapi_private_data_t *p, const char *buf, unsigned write_length) |
695 |
{ |
696 |
unsigned written = 0; |
697 |
DWORD try_to_write = 0; |
698 |
|
699 |
JK_TRACE_ENTER(logger); |
700 |
|
701 |
while (written < write_length) { |
702 |
try_to_write = (write_length - written); |
703 |
if (!p->lpEcb->WriteClient(p->lpEcb->ConnID, |
704 |
buf + written, &try_to_write, 0)) { |
705 |
jk_log(logger, JK_LOG_ERROR, |
706 |
"WriteClient failed with %08x", GetLastError()); |
707 |
JK_TRACE_EXIT(logger); |
708 |
return JK_FALSE; |
709 |
} |
710 |
written += try_to_write; |
711 |
} |
712 |
JK_TRACE_EXIT(logger); |
713 |
return JK_TRUE; |
714 |
} |
715 |
|
716 |
/* |
717 |
* Write content to the response. |
718 |
* If chunked encoding has been enabled and the client supports it |
719 |
*(and it's appropriate for the response), then this will write a |
720 |
* single "Transfer-Encoding: chunked" chunk |
721 |
* |
722 |
*/ |
605 |
static int JK_METHOD write(jk_ws_service_t *s, const void *b, unsigned int l) |
723 |
static int JK_METHOD write(jk_ws_service_t *s, const void *b, unsigned int l) |
606 |
{ |
724 |
{ |
607 |
JK_TRACE_ENTER(logger); |
725 |
JK_TRACE_ENTER(logger); |
Lines 610-638
Link Here
|
610 |
isapi_private_data_t *p = s->ws_private; |
728 |
isapi_private_data_t *p = s->ws_private; |
611 |
|
729 |
|
612 |
if (l) { |
730 |
if (l) { |
613 |
unsigned int written = 0; |
|
|
614 |
char *buf = (char *)b; |
731 |
char *buf = (char *)b; |
615 |
|
732 |
|
616 |
if (!p->request_started) { |
733 |
if (!p->request_started) { |
617 |
start_response(s, 200, NULL, NULL, NULL, 0); |
734 |
start_response(s, 200, NULL, NULL, NULL, 0); |
618 |
} |
735 |
} |
619 |
|
736 |
|
620 |
while (written < l) { |
737 |
/* Chunk header */ |
621 |
DWORD try_to_write = l - written; |
738 |
if (p->chunk_content) { |
622 |
if (!p->lpEcb->WriteClient(p->lpEcb->ConnID, |
739 |
char chunk_header[sizeof(unsigned)*2+3]; /* Hex of chunk length + CRLF + term. */ |
623 |
buf + written, &try_to_write, 0)) { |
740 |
jk_log(logger, JK_LOG_DEBUG, |
624 |
jk_log(logger, JK_LOG_ERROR, |
741 |
"Using chunked encoding - writing chunk header\n"); |
625 |
"WriteClient failed with %08x", GetLastError()); |
742 |
|
|
|
743 |
/* Construct chunk header : HEX CRLF*/ |
744 |
sprintf(chunk_header, "%X%s", l, CRLF); |
745 |
|
746 |
if (!isapi_write_client(p, chunk_header, strlen(chunk_header))) { |
747 |
jk_log(logger, JK_LOG_ERROR, |
748 |
"WriteClient for chunk header failed\n"); |
749 |
JK_TRACE_EXIT(logger); |
750 |
return JK_FALSE; |
751 |
} |
752 |
} |
753 |
|
754 |
/* Write chunk body */ |
755 |
if (!isapi_write_client(p, buf, l)) { |
756 |
jk_log(logger, JK_LOG_ERROR, |
757 |
"WriteClient for body chunk failed\n"); |
758 |
JK_TRACE_EXIT(logger); |
759 |
return JK_FALSE; |
760 |
} |
761 |
|
762 |
/* Write chunk trailer */ |
763 |
if (p->chunk_content) { |
764 |
jk_log(logger, JK_LOG_DEBUG, |
765 |
"Using chunked encoding - writing chunk trailer\n"); |
766 |
|
767 |
if (!isapi_write_client(p, CRLF, strlen(CRLF))) { |
768 |
jk_log(logger, JK_LOG_ERROR, |
769 |
"WriteClient for chunk trailer failed\n"); |
626 |
JK_TRACE_EXIT(logger); |
770 |
JK_TRACE_EXIT(logger); |
627 |
return JK_FALSE; |
771 |
return JK_FALSE; |
628 |
} |
772 |
} |
629 |
written += try_to_write; |
|
|
630 |
} |
773 |
} |
631 |
} |
774 |
} |
632 |
|
775 |
|
633 |
JK_TRACE_EXIT(logger); |
776 |
JK_TRACE_EXIT(logger); |
634 |
return JK_TRUE; |
777 |
return JK_TRUE; |
|
|
778 |
} |
779 |
|
780 |
JK_LOG_NULL_PARAMS(logger); |
781 |
JK_TRACE_EXIT(logger); |
782 |
return JK_FALSE; |
783 |
} |
784 |
|
785 |
/** |
786 |
* In the case of a Transfer-Encoding: chunked response, this will write the terminator chunk. |
787 |
*/ |
788 |
static int JK_METHOD flush_response(jk_ws_service_t *s) |
789 |
{ |
790 |
JK_TRACE_ENTER(logger); |
635 |
|
791 |
|
|
|
792 |
if (s && s->ws_private) { |
793 |
isapi_private_data_t *p = s->ws_private; |
794 |
|
795 |
/* Write last chunk + terminator */ |
796 |
if (p->chunk_content) { |
797 |
static char CHUNKED_ENCODING_TRAILER[6] = { '0', (char)13, (char)10, (char)13, (char)10, '\0' }; |
798 |
|
799 |
jk_log(logger, JK_LOG_DEBUG, |
800 |
"Terminating chunk encoded response.\n"); |
801 |
|
802 |
if (!isapi_write_client(p, CHUNKED_ENCODING_TRAILER, strlen(CHUNKED_ENCODING_TRAILER))) { |
803 |
jk_log(logger, JK_LOG_ERROR, |
804 |
"WriteClient for chunk response terminator failed\n"); |
805 |
JK_TRACE_EXIT(logger); |
806 |
return JK_FALSE; |
807 |
} |
808 |
} |
809 |
|
810 |
JK_TRACE_EXIT(logger); |
811 |
return JK_TRUE; |
636 |
} |
812 |
} |
637 |
|
813 |
|
638 |
JK_LOG_NULL_PARAMS(logger); |
814 |
JK_LOG_NULL_PARAMS(logger); |
Lines 992-997
Link Here
|
992 |
private_data.request_started = JK_FALSE; |
1168 |
private_data.request_started = JK_FALSE; |
993 |
private_data.bytes_read_so_far = 0; |
1169 |
private_data.bytes_read_so_far = 0; |
994 |
private_data.lpEcb = lpEcb; |
1170 |
private_data.lpEcb = lpEcb; |
|
|
1171 |
private_data.chunk_content = JK_FALSE; |
995 |
|
1172 |
|
996 |
s.ws_private = &private_data; |
1173 |
s.ws_private = &private_data; |
997 |
s.pool = &private_data.p; |
1174 |
s.pool = &private_data.p; |
Lines 1139-1144
Link Here
|
1139 |
jk_log(logger, JK_LOG_DEBUG, "Using worker mount file %s.", |
1316 |
jk_log(logger, JK_LOG_DEBUG, "Using worker mount file %s.", |
1140 |
worker_mount_file); |
1317 |
worker_mount_file); |
1141 |
jk_log(logger, JK_LOG_DEBUG, "Using uri select %d.", uri_select_option); |
1318 |
jk_log(logger, JK_LOG_DEBUG, "Using uri select %d.", uri_select_option); |
|
|
1319 |
jk_log(logger, JK_LOG_DEBUG, "Using chunked encoding? %d.\n", chunked_encoding_enabled); |
1142 |
} |
1320 |
} |
1143 |
if (uri_worker_map_alloc(&uw_map, NULL, logger)) { |
1321 |
if (uri_worker_map_alloc(&uw_map, NULL, logger)) { |
1144 |
rc = JK_FALSE; |
1322 |
rc = JK_FALSE; |
Lines 1256-1261
Link Here
|
1256 |
ok = JK_FALSE; |
1434 |
ok = JK_FALSE; |
1257 |
} |
1435 |
} |
1258 |
} |
1436 |
} |
|
|
1437 |
chunked_encoding_enabled = jk_map_get_bool(map, ENABLE_CHUNKED_ENCODING_TAG, JK_FALSE); |
1259 |
|
1438 |
|
1260 |
} |
1439 |
} |
1261 |
else { |
1440 |
else { |
Lines 1323-1328
Link Here
|
1323 |
} |
1502 |
} |
1324 |
} |
1503 |
} |
1325 |
|
1504 |
|
|
|
1505 |
if (get_registry_config_parameter(hkey, |
1506 |
ENABLE_CHUNKED_ENCODING_TAG, |
1507 |
tmpbuf, sizeof(tmpbuf))) { |
1508 |
if (strcasecmp(tmpbuf, "true") == 0 || |
1509 |
*tmpbuf == 'Y' || *tmpbuf == 'y' || *tmpbuf == '1') { |
1510 |
chunked_encoding_enabled = JK_TRUE; |
1511 |
} |
1512 |
} |
1513 |
|
1326 |
RegCloseKey(hkey); |
1514 |
RegCloseKey(hkey); |
1327 |
} |
1515 |
} |
1328 |
return ok; |
1516 |
return ok; |
Lines 1356-1362
Link Here
|
1356 |
s->start_response = start_response; |
1544 |
s->start_response = start_response; |
1357 |
s->read = read; |
1545 |
s->read = read; |
1358 |
s->write = write; |
1546 |
s->write = write; |
1359 |
s->flush = NULL; |
1547 |
|
|
|
1548 |
/* We want to flush at end of content to terminate chunked encoding */ |
1549 |
s->flush = flush_response; |
1550 |
s->flush_packets = JK_FALSE; |
1360 |
|
1551 |
|
1361 |
/* Clear RECO status */ |
1552 |
/* Clear RECO status */ |
1362 |
s->reco_status = RECO_NONE; |
1553 |
s->reco_status = RECO_NONE; |