in native/iis/jk_isapi_plugin.c [892:1108]
static int JK_METHOD start_response(jk_ws_service_t *s,
int status,
const char *reason,
const char *const *header_names,
const char *const *header_values,
unsigned int num_of_headers)
{
int rv = JK_TRUE;
isapi_private_data_t *p;
jk_log_context_t *l = s->log_ctx;
JK_TRACE_ENTER(l);
if (s == NULL || s->ws_private == NULL) {
JK_LOG_NULL_PARAMS(l);
JK_TRACE_EXIT(l);
return JK_FALSE;
}
if (status < 100 || status > 1000) {
jk_log(l, JK_LOG_ERROR,
"invalid status %d",
status);
JK_TRACE_EXIT(l);
return JK_FALSE;
}
p = s->ws_private;
/* If we use proxy error pages, still pass
* through context headers needed for special status codes.
*/
if (s->extension.use_server_error_pages &&
status >= s->extension.use_server_error_pages) {
if (status == JK_HTTP_UNAUTHORIZED) {
int found = JK_FALSE;
unsigned int h;
for (h = 0; h < num_of_headers; h++) {
if (!strcasecmp(header_names[h], "WWW-Authenticate")) {
p->err_hdrs = jk_pool_strcatv(&p->p,
"WWW-Authenticate:",
header_values[h], CRLF, NULL);
found = JK_TRUE;
}
}
if (found == JK_FALSE) {
jk_log(l, JK_LOG_INFO,
"origin server sent 401 without"
" WWW-Authenticate header");
}
}
return JK_TRUE;
}
if (!s->response_started) {
char *status_str = NULL;
char *headers_str = NULL;
BOOL keep_alive = FALSE; /* Whether the downstream or us can supply content length */
BOOL rc;
size_t i, len_of_headers = 0;
s->response_started = JK_TRUE;
if (JK_IS_DEBUG_LEVEL(l)) {
jk_log(l, JK_LOG_DEBUG, "Starting response for URI '%s' (protocol %s)",
s->req_uri, s->protocol);
}
/*
* Create the status line
*/
if (!reason) {
reason = status_reason(status);
}
status_str = (char *)malloc((6 + strlen(reason)));
StringCbPrintf(status_str, 6 + strlen(reason), "%d %s", status, reason);
if (chunked_encoding_enabled) {
/* Check if we've got an HTTP/1.1 response */
if (!strcasecmp(s->protocol, "HTTP/1.1")) {
keep_alive = TRUE;
/* Chunking only when HTTP/1.1 client and enabled */
p->chunk_content = JK_TRUE;
}
}
/*
* Create response headers string
*/
/* Calculate length of headers block */
for (i = 0; i < num_of_headers; i++) {
len_of_headers += strlen(header_names[i]);
len_of_headers += strlen(header_values[i]);
len_of_headers += 4; /* extra for colon, space and crlf */
}
/*
* Exclude status codes that MUST NOT include message bodies
*/
if ((status == 204) || (status == 205) || (status == 304)) {
p->chunk_content = JK_FALSE;
/* Keep alive is still possible */
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG, "Response status %d implies no message body", status);
}
if (p->chunk_content) {
for (i = 0; i < num_of_headers; i++) {
/* Check the downstream response to see whether
* it's appropriate to chunk the response content
* and whether it supports keeping the connection open.
* This implements the rules for HTTP/1.1 message length determination
* with the exception of multipart/byteranges media types.
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4
*/
if (!strcasecmp(CONTENT_LENGTH_HEADER_NAME, header_names[i])) {
p->chunk_content = JK_FALSE;
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG, "Response specifies Content-Length");
}
else if (!strcasecmp(CONNECTION_HEADER_NAME, header_names[i])
&& !strcasecmp(CONNECTION_CLOSE_VALUE, header_values[i])) {
keep_alive = FALSE;
p->chunk_content = JK_FALSE;
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG, "Response specifies Connection: Close");
}
else if (!strcasecmp(TRANSFER_ENCODING_HEADER_NAME, header_names[i])
&& !strcasecmp(TRANSFER_ENCODING_IDENTITY_VALUE, header_values[i])) {
/* HTTP states that this must include 'chunked' as the last value.
* 'identity' is the same as absence of the header */
p->chunk_content = JK_FALSE;
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG, "Response specifies Transfer-Encoding");
}
}
/* Provide room in the buffer for the Transfer-Encoding header if we use it. */
len_of_headers += TRANSFER_ENCODING_CHUNKED_HEADER_COMPLETE_LEN + 2;
}
/* Allocate and init the headers string */
len_of_headers += 3; /* crlf and terminating null char */
headers_str = (char *)malloc(len_of_headers);
headers_str[0] = '\0';
/* Copy headers into headers block for sending */
for (i = 0; i < num_of_headers; i++) {
StringCbCat(headers_str, len_of_headers, header_names[i]);
StringCbCat(headers_str, len_of_headers, ": ");
StringCbCat(headers_str, len_of_headers, header_values[i]);
StringCbCat(headers_str, len_of_headers, CRLF);
}
if (p->chunk_content) {
/* Configure the response if chunked encoding is used */
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG, "Using Transfer-Encoding: chunked");
/** We will supply the transfer-encoding to allow IIS to keep the connection open */
keep_alive = TRUE;
/* Indicate to the client that the content will be chunked
- We've already reserved space for this */
StringCbCat(headers_str, len_of_headers, TRANSFER_ENCODING_CHUNKED_HEADER_COMPLETE);
StringCbCat(headers_str, len_of_headers, CRLF);
}
/* Terminate the headers */
StringCbCat(headers_str, len_of_headers, CRLF);
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG, "%ssing Keep-Alive", (keep_alive ? "U" : "Not u"));
if (keep_alive) {
HSE_SEND_HEADER_EX_INFO hi;
/* Fill in the response */
hi.pszStatus = status_str;
hi.pszHeader = headers_str;
hi.cchStatus = lstrlenA(status_str);
hi.cchHeader = lstrlenA(headers_str);
/*
* Using the extended form of the API means we have to get this right,
* i.e. IIS won't keep connections open if there's a Content-Length and close them if there isn't.
*/
hi.fKeepConn = keep_alive;
/* Send the response to the client */
rc = p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER_EX,
&hi,
NULL, NULL);
}
else {
/* Old style response - forces Connection: close if Tomcat response doesn't
specify necessary details to allow keep alive */
rc = p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER,
status_str,
0,
(LPDWORD)headers_str);
}
if (!rc) {
jk_log(l, JK_LOG_ERROR,
"HSE_REQ_SEND_RESPONSE_HEADER%s failed with error=%d (0x%08x)",
(keep_alive ? "_EX" : ""), GetLastError(), GetLastError());
rv = JK_FALSE;
}
if (headers_str)
free(headers_str);
if (status_str)
free(status_str);
}
JK_TRACE_EXIT(l);
return rv;
}