int http_response_write_header(server *srv, connection *con) { buffer *b; size_t i; int have_date = 0; int have_server = 0; b = chunkqueue_get_prepend_buffer(con->write_queue); if (con->request.http_version == HTTP_VERSION_1_1) { buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.1 ")); } else { buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.0 ")); } buffer_append_long(b, con->http_status); buffer_append_string_len(b, CONST_STR_LEN(" ")); buffer_append_string(b, get_http_status_name(con->http_status)); /* disable keep-alive if requested */ if (con->request_count > con->conf.max_keep_alive_requests || 0 == con->conf.max_keep_alive_idle) { con->keep_alive = 0; } else { con->keep_alive_idle = con->conf.max_keep_alive_idle; } if (con->request.http_version != HTTP_VERSION_1_1 || con->keep_alive == 0) { if (con->keep_alive) { response_header_overwrite(srv, con, CONST_STR_LEN("Connection"), CONST_STR_LEN("keep-alive")); } else { response_header_overwrite(srv, con, CONST_STR_LEN("Connection"), CONST_STR_LEN("close")); } } if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { response_header_overwrite(srv, con, CONST_STR_LEN("Transfer-Encoding"), CONST_STR_LEN("chunked")); } /* add all headers */ for (i = 0; i < con->response.headers->used; i++) { data_string *ds; ds = (data_string *)con->response.headers->data[i]; if (ds->value->used && ds->key->used && 0 != strncasecmp(ds->key->ptr, CONST_STR_LEN("X-LIGHTTPD-")) && 0 != strncasecmp(ds->key->ptr, CONST_STR_LEN("X-Sendfile"))) { if (0 == strcasecmp(ds->key->ptr, "Date")) have_date = 1; if (0 == strcasecmp(ds->key->ptr, "Server")) have_server = 1; if (0 == strcasecmp(ds->key->ptr, "Content-Encoding") && 304 == con->http_status) continue; buffer_append_string_len(b, CONST_STR_LEN("\r\n")); buffer_append_string_buffer(b, ds->key); buffer_append_string_len(b, CONST_STR_LEN(": ")); #if 0 /** * the value might contain newlines, encode them with at least one white-space */ buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_HTTP_HEADER); #else buffer_append_string_buffer(b, ds->value); #endif } } if (!have_date) { /* HTTP/1.1 requires a Date: header */ buffer_append_string_len(b, CONST_STR_LEN("\r\nDate: ")); /* cache the generated timestamp */ if (srv->cur_ts != srv->last_generated_date_ts) { buffer_prepare_copy(srv->ts_date_str, 255); strftime(srv->ts_date_str->ptr, srv->ts_date_str->size - 1, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(srv->cur_ts))); srv->ts_date_str->used = strlen(srv->ts_date_str->ptr) + 1; srv->last_generated_date_ts = srv->cur_ts; } buffer_append_string_buffer(b, srv->ts_date_str); } if (!have_server) { if (buffer_is_empty(con->conf.server_tag)) { buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: " PACKAGE_DESC)); } else if (con->conf.server_tag->used > 1) { buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: ")); buffer_append_string_encoded(b, CONST_BUF_LEN(con->conf.server_tag), ENCODING_HTTP_HEADER); } } //- Jerry add 20110923 #if EMBEDDED_EANBLE char * ddns_host_n = nvram_get_ddns_host_name(); if(ddns_host_n){ buffer_append_string_len(b, CONST_STR_LEN("\r\nDDNS: ")); buffer_append_string(b, ddns_host_n); }else #endif buffer_append_string_len(b, CONST_STR_LEN("\r\nDDNS: ")); buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n")); con->bytes_header = b->used - 1; if (con->conf.log_response_header) { log_error_write(srv, __FILE__, __LINE__, "sSb", "Response-Header:", "\n", b); } return 0; }
static int connection_handle_write_prepare(server *srv, connection *con) { if (con->mode == DIRECT) { /* static files */ switch(con->request.http_method) { case HTTP_METHOD_GET: case HTTP_METHOD_POST: case HTTP_METHOD_HEAD: break; case HTTP_METHOD_OPTIONS: /* * 400 is coming from the request-parser BEFORE uri.path is set * 403 is from the response handler when noone else catched it * * */ if ((!con->http_status || con->http_status == 200) && !buffer_string_is_empty(con->uri.path) && con->uri.path->ptr[0] != '*') { response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; con->parsed_response &= ~HTTP_CONTENT_LENGTH; con->http_status = 200; con->file_finished = 1; chunkqueue_reset(con->write_queue); } break; default: if (0 == con->http_status) { con->http_status = 501; } break; } } if (con->http_status == 0) { con->http_status = 403; } switch(con->http_status) { case 204: /* class: header only */ case 205: case 304: /* disable chunked encoding again as we have no body */ con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; con->parsed_response &= ~HTTP_CONTENT_LENGTH; chunkqueue_reset(con->write_queue); con->file_finished = 1; break; default: /* class: header + body */ if (con->mode != DIRECT) break; /* only custom body for 4xx and 5xx */ if (con->http_status < 400 || con->http_status >= 600) break; con->file_finished = 0; connection_handle_errdoc_init(srv, con); /* try to send static errorfile */ if (!buffer_string_is_empty(con->conf.errorfile_prefix)) { stat_cache_entry *sce = NULL; buffer_copy_buffer(con->physical.path, con->conf.errorfile_prefix); buffer_append_int(con->physical.path, con->http_status); buffer_append_string_len(con->physical.path, CONST_STR_LEN(".html")); if (0 == http_chunk_append_file(srv, con, con->physical.path)) { con->file_finished = 1; if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); } } } if (!con->file_finished) { buffer *b; buffer_reset(con->physical.path); con->file_finished = 1; b = buffer_init(); /* build default error-page */ buffer_copy_string_len(b, CONST_STR_LEN( "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" " <head>\n" " <title>")); buffer_append_int(b, con->http_status); buffer_append_string_len(b, CONST_STR_LEN(" - ")); buffer_append_string(b, get_http_status_name(con->http_status)); buffer_append_string_len(b, CONST_STR_LEN( "</title>\n" " </head>\n" " <body>\n" " <h1>")); buffer_append_int(b, con->http_status); buffer_append_string_len(b, CONST_STR_LEN(" - ")); buffer_append_string(b, get_http_status_name(con->http_status)); buffer_append_string_len(b, CONST_STR_LEN("</h1>\n" " </body>\n" "</html>\n" )); (void)http_chunk_append_buffer(srv, con, b); buffer_free(b); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); } break; } /* Allow filter plugins to change response headers before they are written. */ switch(plugins_call_handle_response_start(srv, con)) { case HANDLER_GO_ON: case HANDLER_FINISHED: break; default: log_error_write(srv, __FILE__, __LINE__, "s", "response_start plugin failed"); return -1; } if (con->file_finished) { /* we have all the content and chunked encoding is not used, set a content-length */ if ((!(con->parsed_response & HTTP_CONTENT_LENGTH)) && (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0) { off_t qlen = chunkqueue_length(con->write_queue); /** * The Content-Length header only can be sent if we have content: * - HEAD doesn't have a content-body (but have a content-length) * - 1xx, 204 and 304 don't have a content-body (RFC 2616 Section 4.3) * * Otherwise generate a Content-Length header as chunked encoding is not * available */ if ((con->http_status >= 100 && con->http_status < 200) || con->http_status == 204 || con->http_status == 304) { data_string *ds; /* no Content-Body, no Content-Length */ if (NULL != (ds = (data_string*) array_get_element(con->response.headers, "Content-Length"))) { buffer_reset(ds->value); /* Headers with empty values are ignored for output */ } } else if (qlen > 0 || con->request.http_method != HTTP_METHOD_HEAD) { /* qlen = 0 is important for Redirects (301, ...) as they MAY have * a content. Browsers are waiting for a Content otherwise */ buffer_copy_int(srv->tmp_buf, qlen); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Length"), CONST_BUF_LEN(srv->tmp_buf)); } } } else { /** * the file isn't finished yet, but we have all headers * * to get keep-alive we either need: * - Content-Length: ... (HTTP/1.0 and HTTP/1.0) or * - Transfer-Encoding: chunked (HTTP/1.1) */ if (((con->parsed_response & HTTP_CONTENT_LENGTH) == 0) && ((con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0)) { if (con->request.http_version == HTTP_VERSION_1_1) { off_t qlen = chunkqueue_length(con->write_queue); con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; if (qlen) { /* create initial Transfer-Encoding: chunked segment */ buffer *b = srv->tmp_chunk_len; buffer_string_set_length(b, 0); buffer_append_uint_hex(b, (uintmax_t)qlen); buffer_append_string_len(b, CONST_STR_LEN("\r\n")); chunkqueue_prepend_buffer(con->write_queue, b); chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("\r\n")); } } else { con->keep_alive = 0; } } /** * if the backend sent a Connection: close, follow the wish * * NOTE: if the backend sent Connection: Keep-Alive, but no Content-Length, we * will close the connection. That's fine. We can always decide the close * the connection * * FIXME: to be nice we should remove the Connection: ... */ if (con->parsed_response & HTTP_CONNECTION) { /* a subrequest disable keep-alive although the client wanted it */ if (con->keep_alive && !con->response.keep_alive) { con->keep_alive = 0; } } } if (con->request.http_method == HTTP_METHOD_HEAD) { /** * a HEAD request has the same as a GET * without the content */ con->file_finished = 1; chunkqueue_reset(con->write_queue); con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; } http_response_write_header(srv, con); return 0; }
int http_response_finish_header(server *srv, connection *con) { if (NULL == srv || NULL == con || NULL == con -> write_queue) { return -1; } /* * 这个header有安全问题。通产默认不添加。 */ //http_response_insert_header(srv, con, CONST_STR_LEN("Server"), CONST_STR_LEN("Swiftd/0.1 written by hcy")); buffer *b = chunkqueue_get_prepend_buffer(con -> write_queue); if (NULL == b) { log_error_write(srv, __FILE__, __LINE__, "s", "chunkqueue_get_prepend_buffer failed."); return -1; } buffer_reset(b); /* * Status-Line: * HTTP-Version SP Status-Code SP Reason-Phrase CRLF */ buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.1")); buffer_append_string_len(b, CONST_STR_LEN(" ")); //SP buffer_append_long(b, con -> http_status); buffer_append_string_len(b, CONST_STR_LEN(" ")); //SP buffer_append_string(b, get_http_status_name(con -> http_status)); buffer_append_string_len(b, CONST_STR_LEN(CRLF)); //CRLF = '\r\n' defined in base.h if(con -> keep_alive) { http_response_insert_header(srv, con, CONST_STR_LEN("Connection") , CONST_STR_LEN("keep-alive")); } else { http_response_insert_header(srv, con, CONST_STR_LEN("Connection") , CONST_STR_LEN("close")); } /* * Headers: * Key:Value CRLF */ size_t i; data_string *ds; for (i = 0; i < con -> response.headers -> used; ++i) { ds = (data_string *)con -> response.headers -> data[i]; if (NULL == ds) { continue; } buffer_append_string_buffer(b, ds -> key); buffer_append_string_len(b, CONST_STR_LEN(": ")); buffer_append_string_buffer(b, ds -> value); buffer_append_string_len(b, CONST_STR_LEN(CRLF)); } /* * header 和 message body之间的CRLF。 */ buffer_append_string_len(b, CONST_STR_LEN(CRLF)); /* * 最后一个'\0'不能发送出去!! */ -- b -> used; log_error_write(srv, __FILE__, __LINE__, "sb", "Response Headers:", b); array_reset(con -> response.headers); return 0; }
/* 0: everything ok, -1: error, -2: con closed */ static int connection_handle_read(server *srv, connection *con) { int len; buffer *b; int toread, read_offset; if (con->conf.is_ssl) { return connection_handle_read_ssl(srv, con); } b = (NULL != con->read_queue->last) ? con->read_queue->last->mem : NULL; /* default size for chunks is 4kb; only use bigger chunks if FIONREAD tells * us more than 4kb is available * if FIONREAD doesn't signal a big chunk we fill the previous buffer * if it has >= 1kb free */ #if defined(__WIN32) if (NULL == b || b->size - b->used < 1024) { b = chunkqueue_get_append_buffer(con->read_queue); buffer_prepare_copy(b, 4 * 1024); } read_offset = (b->used == 0) ? 0 : b->used - 1; len = recv(con->fd, b->ptr + read_offset, b->size - 1 - read_offset, 0); #else #ifdef HAVE_LIBMTCP /* toread = MAX_READ_LIMIT; */ if (mtcp_socket_ioctl(srv->mctx, con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) { #else if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) { #endif if (NULL == b || b->size - b->used < 1024) { b = chunkqueue_get_append_buffer(con->read_queue); buffer_prepare_copy(b, 4 * 1024); } } else { if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT; b = chunkqueue_get_append_buffer(con->read_queue); buffer_prepare_copy(b, toread + 1); } read_offset = (b->used == 0) ? 0 : b->used - 1; #ifdef HAVE_LIBMTCP len = mtcp_read(srv->mctx, con->fd, b->ptr + read_offset, b->size - 1 - read_offset); #else len = read(con->fd, b->ptr + read_offset, b->size - 1 - read_offset); #endif #endif if (len < 0) { con->is_readable = 0; if (errno == EAGAIN) return 0; if (errno == EINTR) { /* we have been interrupted before we could read */ con->is_readable = 1; return 0; } if (errno != ECONNRESET) { /* expected for keep-alive */ log_error_write(srv, __FILE__, __LINE__, "ssd", "connection closed - read failed: ", strerror(errno), errno); } connection_set_state(srv, con, CON_STATE_ERROR); return -1; } else if (len == 0) { con->is_readable = 0; /* the other end close the connection -> KEEP-ALIVE */ /* pipelining */ return -2; } else if ((size_t)len < b->size - 1) { /* we got less then expected, wait for the next fd-event */ con->is_readable = 0; } if (b->used > 0) b->used--; b->used += len; b->ptr[b->used++] = '\0'; con->bytes_read += len; #if 0 dump_packet(b->ptr, len); #endif return 0; } static int connection_handle_write_prepare(server *srv, connection *con) { if (con->mode == DIRECT) { /* static files */ switch(con->request.http_method) { case HTTP_METHOD_GET: case HTTP_METHOD_POST: case HTTP_METHOD_HEAD: case HTTP_METHOD_PUT: case HTTP_METHOD_PATCH: case HTTP_METHOD_MKCOL: case HTTP_METHOD_DELETE: case HTTP_METHOD_COPY: case HTTP_METHOD_MOVE: case HTTP_METHOD_PROPFIND: case HTTP_METHOD_PROPPATCH: case HTTP_METHOD_LOCK: case HTTP_METHOD_UNLOCK: break; case HTTP_METHOD_OPTIONS: /* * 400 is coming from the request-parser BEFORE uri.path is set * 403 is from the response handler when noone else catched it * * */ if ((!con->http_status || con->http_status == 200) && con->uri.path->used && con->uri.path->ptr[0] != '*') { response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; con->parsed_response &= ~HTTP_CONTENT_LENGTH; con->http_status = 200; con->file_finished = 1; chunkqueue_reset(con->write_queue); } break; default: switch(con->http_status) { case 400: /* bad request */ case 401: /* authorization required */ case 414: /* overload request header */ case 505: /* unknown protocol */ case 207: /* this was webdav */ break; default: con->http_status = 501; break; } break; } } if (con->http_status == 0) { con->http_status = 403; } switch(con->http_status) { case 204: /* class: header only */ case 205: case 304: /* disable chunked encoding again as we have no body */ con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; con->parsed_response &= ~HTTP_CONTENT_LENGTH; chunkqueue_reset(con->write_queue); con->file_finished = 1; break; default: /* class: header + body */ if (con->mode != DIRECT) break; /* only custom body for 4xx and 5xx */ if (con->http_status < 400 || con->http_status >= 600) break; con->file_finished = 0; buffer_reset(con->physical.path); /* try to send static errorfile */ if (!buffer_is_empty(con->conf.errorfile_prefix)) { stat_cache_entry *sce = NULL; buffer_copy_string_buffer(con->physical.path, con->conf.errorfile_prefix); buffer_append_long(con->physical.path, con->http_status); buffer_append_string_len(con->physical.path, CONST_STR_LEN(".html")); if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { con->file_finished = 1; http_chunk_append_file(srv, con, con->physical.path, 0, sce->st.st_size); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); } } if (!con->file_finished) { buffer *b; buffer_reset(con->physical.path); con->file_finished = 1; b = chunkqueue_get_append_buffer(con->write_queue); /* build default error-page */ buffer_copy_string_len(b, CONST_STR_LEN( "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" " <head>\n" " <title>")); buffer_append_long(b, con->http_status); buffer_append_string_len(b, CONST_STR_LEN(" - ")); buffer_append_string(b, get_http_status_name(con->http_status)); buffer_append_string_len(b, CONST_STR_LEN( "</title>\n" " </head>\n" " <body>\n" " <h1>")); buffer_append_long(b, con->http_status); buffer_append_string_len(b, CONST_STR_LEN(" - ")); buffer_append_string(b, get_http_status_name(con->http_status)); buffer_append_string_len(b, CONST_STR_LEN("</h1>\n" " </body>\n" "</html>\n" )); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); } break; } if (con->file_finished) { /* we have all the content and chunked encoding is not used, set a content-length */ if ((!(con->parsed_response & HTTP_CONTENT_LENGTH)) && (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0) { off_t qlen = chunkqueue_length(con->write_queue); /** * The Content-Length header only can be sent if we have content: * - HEAD doesn't have a content-body (but have a content-length) * - 1xx, 204 and 304 don't have a content-body (RFC 2616 Section 4.3) * * Otherwise generate a Content-Length header as chunked encoding is not * available */ if ((con->http_status >= 100 && con->http_status < 200) || con->http_status == 204 || con->http_status == 304) { data_string *ds; /* no Content-Body, no Content-Length */ if (NULL != (ds = (data_string*) array_get_element(con->response.headers, "Content-Length"))) { buffer_reset(ds->value); /* Headers with empty values are ignored for output */ } } else if (qlen > 0 || con->request.http_method != HTTP_METHOD_HEAD) { /* qlen = 0 is important for Redirects (301, ...) as they MAY have * a content. Browsers are waiting for a Content otherwise */ buffer_copy_off_t(srv->tmp_buf, qlen); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Length"), CONST_BUF_LEN(srv->tmp_buf)); } } } else { /** * the file isn't finished yet, but we have all headers * * to get keep-alive we either need: * - Content-Length: ... (HTTP/1.0 and HTTP/1.0) or * - Transfer-Encoding: chunked (HTTP/1.1) */ if (((con->parsed_response & HTTP_CONTENT_LENGTH) == 0) && ((con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0)) { con->keep_alive = 0; } /** * if the backend sent a Connection: close, follow the wish * * NOTE: if the backend sent Connection: Keep-Alive, but no Content-Length, we * will close the connection. That's fine. We can always decide the close * the connection * * FIXME: to be nice we should remove the Connection: ... */ if (con->parsed_response & HTTP_CONNECTION) { /* a subrequest disable keep-alive although the client wanted it */ if (con->keep_alive && !con->response.keep_alive) { con->keep_alive = 0; } } } if (con->request.http_method == HTTP_METHOD_HEAD) { /** * a HEAD request has the same as a GET * without the content */ con->file_finished = 1; chunkqueue_reset(con->write_queue); con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; } http_response_write_header(srv, con); return 0; } static int connection_handle_write(server *srv, connection *con) { switch(network_write_chunkqueue(srv, con, con->write_queue, MAX_WRITE_LIMIT)) { case 0: con->write_request_ts = srv->cur_ts; if (con->file_finished) { connection_set_state(srv, con, CON_STATE_RESPONSE_END); joblist_append(srv, con); } break; case -1: /* error on our side */ log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed: write failed on fd", con->fd); connection_set_state(srv, con, CON_STATE_ERROR); joblist_append(srv, con); break; case -2: /* remote close */ connection_set_state(srv, con, CON_STATE_ERROR); joblist_append(srv, con); break; case 1: con->write_request_ts = srv->cur_ts; con->is_writable = 0; /* not finished yet -> WRITE */ break; } return 0; } connection *connection_init(server *srv) { connection *con; UNUSED(srv); con = calloc(1, sizeof(*con)); con->fd = 0; con->ndx = -1; con->fde_ndx = -1; con->bytes_written = 0; con->bytes_read = 0; con->bytes_header = 0; con->loops_per_request = 0; #define CLEAN(x) \ con->x = buffer_init(); CLEAN(request.uri); CLEAN(request.request_line); CLEAN(request.request); CLEAN(request.pathinfo); CLEAN(request.orig_uri); CLEAN(uri.scheme); CLEAN(uri.authority); CLEAN(uri.path); CLEAN(uri.path_raw); CLEAN(uri.query); CLEAN(physical.doc_root); CLEAN(physical.path); CLEAN(physical.basedir); CLEAN(physical.rel_path); CLEAN(physical.etag); CLEAN(parse_request); CLEAN(authed_user); CLEAN(server_name); CLEAN(error_handler); CLEAN(dst_addr_buf); #if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT CLEAN(tlsext_server_name); #endif #undef CLEAN con->write_queue = chunkqueue_init(); con->read_queue = chunkqueue_init(); con->request_content_queue = chunkqueue_init(); chunkqueue_set_tempdirs(con->request_content_queue, srv->srvconf.upload_tempdirs); con->request.headers = array_init(); con->response.headers = array_init(); con->environment = array_init(); /* init plugin specific connection structures */ con->plugin_ctx = calloc(1, (srv->plugins.used + 1) * sizeof(void *)); con->cond_cache = calloc(srv->config_context->used, sizeof(cond_cache_t)); config_setup_connection(srv, con); return con; }