| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 | /* response.inl * * Bufferring for HTTP headers for HTTP response. * This function are only intended to be used at the server side. * Optional for HTTP/1.0 and HTTP/1.1, mandatory for HTTP/2. * * This file is part of the CivetWeb project. */#if defined(NO_RESPONSE_BUFFERING) && defined(USE_HTTP2)#error "HTTP2 works only if NO_RESPONSE_BUFFERING is not set"#endif/* Internal function to free header list */static voidfree_buffered_response_header_list(struct mg_connection *conn){#if !defined(NO_RESPONSE_BUFFERING)	while (conn->response_info.num_headers > 0) {		conn->response_info.num_headers--;		mg_free((void *)conn->response_info		            .http_headers[conn->response_info.num_headers]		            .name);		conn->response_info.http_headers[conn->response_info.num_headers].name =		    0;		mg_free((void *)conn->response_info		            .http_headers[conn->response_info.num_headers]		            .value);		conn->response_info.http_headers[conn->response_info.num_headers]		    .value = 0;	}#else	(void)conn; /* Nothing to do */#endif}/* Send first line of HTTP/1.x response */static intsend_http1_response_status_line(struct mg_connection *conn){	const char *status_txt;	const char *http_version = conn->request_info.http_version;	int status_code = conn->status_code;	if ((status_code < 100) || (status_code > 999)) {		/* Set invalid status code to "500 Internal Server Error" */		status_code = 500;	}	if (!http_version) {		http_version = "1.0";	}	/* mg_get_response_code_text will never return NULL */	status_txt = mg_get_response_code_text(conn, conn->status_code);	if (mg_printf(	        conn, "HTTP/%s %i %s\r\n", http_version, status_code, status_txt)	    < 10) {		/* Network sending failed */		return 0;	}	return 1;}/* Initialize a new HTTP response * Parameters: *   conn: Current connection handle. *   status: HTTP status code (e.g., 200 for "OK"). * Return: *   0:    ok *  -1:    parameter error *  -2:    invalid connection type *  -3:    invalid connection status *  -4:    network error (only if built with NO_RESPONSE_BUFFERING) */intmg_response_header_start(struct mg_connection *conn, int status){	int ret = 0;	if ((conn == NULL) || (status < 100) || (status > 999)) {		/* Parameter error */		return -1;	}	if ((conn->connection_type != CONNECTION_TYPE_REQUEST)	    || (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) {		/* Only allowed in server context */		return -2;	}	if (conn->request_state != 0) {		/* only allowed if nothing was sent up to now */		return -3;	}	conn->status_code = status;	conn->request_state = 1;	/* Buffered response is stored, unbuffered response will be sent directly,	 * but we can only send HTTP/1.x response here */#if !defined(NO_RESPONSE_BUFFERING)	free_buffered_response_header_list(conn);#else	if (!send_http1_response_status_line(conn)) {		ret = -4;	};	conn->request_state = 1; /* Reset from 10 to 1 */#endif	return ret;}/* Add a new HTTP response header line * Parameters: *   conn: Current connection handle. *   header: Header name. *   value: Header value. *   value_len: Length of header value, excluding the terminating zero. *              Use -1 for "strlen(value)". * Return: *    0:    ok *   -1:    parameter error *   -2:    invalid connection type *   -3:    invalid connection status *   -4:    too many headers *   -5:    out of memory */intmg_response_header_add(struct mg_connection *conn,                       const char *header,                       const char *value,                       int value_len){#if !defined(NO_RESPONSE_BUFFERING)	int hidx;#endif	if ((conn == NULL) || (header == NULL) || (value == NULL)) {		/* Parameter error */		return -1;	}	if ((conn->connection_type != CONNECTION_TYPE_REQUEST)	    || (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) {		/* Only allowed in server context */		return -2;	}	if (conn->request_state != 1) {		/* only allowed if mg_response_header_start has been called before */		return -3;	}#if !defined(NO_RESPONSE_BUFFERING)	hidx = conn->response_info.num_headers;	if (hidx >= MG_MAX_HEADERS) {		/* Too many headers */		return -4;	}	/* Alloc new element */	conn->response_info.http_headers[hidx].name =	    mg_strdup_ctx(header, conn->phys_ctx);	if (value_len >= 0) {		char *hbuf =		    (char *)mg_malloc_ctx((unsigned)value_len + 1, conn->phys_ctx);		if (hbuf) {			memcpy(hbuf, value, (unsigned)value_len);			hbuf[value_len] = 0;		}		conn->response_info.http_headers[hidx].value = hbuf;	} else {		conn->response_info.http_headers[hidx].value =		    mg_strdup_ctx(value, conn->phys_ctx);	}	if ((conn->response_info.http_headers[hidx].name == 0)	    || (conn->response_info.http_headers[hidx].value == 0)) {		/* Out of memory */		mg_free((void *)conn->response_info.http_headers[hidx].name);		conn->response_info.http_headers[hidx].name = 0;		mg_free((void *)conn->response_info.http_headers[hidx].value);		conn->response_info.http_headers[hidx].value = 0;		return -5;	}	/* OK, header stored */	conn->response_info.num_headers++;#else	if (value_len >= 0) {		mg_printf(conn, "%s: %.*s\r\n", header, (int)value_len, value);	} else {		mg_printf(conn, "%s: %s\r\n", header, value);	}	conn->request_state = 1; /* Reset from 10 to 1 */#endif	return 0;}/* forward */static int parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS]);/* Add a complete header string (key + value). * Parameters: *   conn: Current connection handle. *   http1_headers: Header line(s) in the form "name: value". * Return: *  >=0:   no error, number of header lines added *  -1:    parameter error *  -2:    invalid connection type *  -3:    invalid connection status *  -4:    too many headers *  -5:    out of memory */intmg_response_header_add_lines(struct mg_connection *conn,                             const char *http1_headers){	struct mg_header add_hdr[MG_MAX_HEADERS];	int num_hdr, i, ret;	char *workbuffer, *parse;	/* We need to work on a copy of the work buffer, sice parse_http_headers	 * will modify */	workbuffer = mg_strdup_ctx(http1_headers, conn->phys_ctx);	if (!workbuffer) {		/* Out of memory */		return -5;	}	/* Call existing method to split header buffer */	parse = workbuffer;	num_hdr = parse_http_headers(&parse, add_hdr);	ret = num_hdr;	for (i = 0; i < num_hdr; i++) {		int lret =		    mg_response_header_add(conn, add_hdr[i].name, add_hdr[i].value, -1);		if ((ret > 0) && (lret < 0)) {			/* Store error return value */			ret = lret;		}	}	/* mg_response_header_add created a copy, so we can free the original */	mg_free(workbuffer);	return ret;}#if defined(USE_HTTP2)static int http2_send_response_headers(struct mg_connection *conn);#endif/* Send http response * Parameters: *   conn: Current connection handle. * Return: *   0:    ok *  -1:    parameter error *  -2:    invalid connection type *  -3:    invalid connection status *  -4:    network send failed */intmg_response_header_send(struct mg_connection *conn){#if !defined(NO_RESPONSE_BUFFERING)	int i;	int has_date = 0;	int has_connection = 0;#endif	if (conn == NULL) {		/* Parameter error */		return -1;	}	if ((conn->connection_type != CONNECTION_TYPE_REQUEST)	    || (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) {		/* Only allowed in server context */		return -2;	}	if (conn->request_state != 1) {		/* only allowed if mg_response_header_start has been called before */		return -3;	}	/* State: 2 */	conn->request_state = 2;#if !defined(NO_RESPONSE_BUFFERING)#if defined(USE_HTTP2)	if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {		int ret = http2_send_response_headers(conn);		return (ret ? 0 : -4);	}#endif	/* Send */	if (!send_http1_response_status_line(conn)) {		return -4;	};	for (i = 0; i < conn->response_info.num_headers; i++) {		mg_printf(conn,		          "%s: %s\r\n",		          conn->response_info.http_headers[i].name,		          conn->response_info.http_headers[i].value);		/* Check for some special headers */		if (!mg_strcasecmp("Date", conn->response_info.http_headers[i].name)) {			has_date = 1;		}		if (!mg_strcasecmp("Connection",		                   conn->response_info.http_headers[i].name)) {			has_connection = 1;		}	}	if (!has_date) {		time_t curtime = time(NULL);		char date[64];		gmt_time_string(date, sizeof(date), &curtime);		mg_printf(conn, "Date: %s\r\n", date);	}	if (!has_connection) {		mg_printf(conn, "Connection: %s\r\n", suggest_connection_header(conn));	}#endif	mg_write(conn, "\r\n", 2);	conn->request_state = 3;	/* ok */	return 0;}
 |