mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-04 20:04:50 +00:00
HTTP: Add persistent connection support.
Persistent HTTP connection support is needed due to the increased usage of the Asterisk core HTTP transport and the frequency at which REST API calls are going to be issued. * Add http.conf session_keep_alive option to enable persistent connections. * Parse and discard optional chunked body extension information and trailing request headers. * Increased the maximum application/json and application/x-www-form-urlencoded body size allowed to 4k. The previous 1k was kind of small. * Removed a couple inlined versions of ast_http_manid_from_vars() by calling the function. manager.c:generic_http_callback() and res_http_post.c:http_post_callback() * Add missing va_end() in ast_ari_response_error(). * Eliminated unnecessary RAII_VAR() use in http.c:auth_create(). ASTERISK-23552 #close Reported by: Scott Griepentrog Review: https://reviewboard.asterisk.org/r/3691/ ........ Merged revisions 417880 from http://svn.asterisk.org/svn/asterisk/branches/12 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@417901 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -158,6 +158,11 @@ HTTP:
|
||||
- Added http.conf session_inactivity timer option to close HTTP connections
|
||||
that aren't doing anything.
|
||||
|
||||
- Added support for persistent HTTP connections. To enable persistent
|
||||
HTTP connections configure the keep alive time between HTTP requests. The
|
||||
keep alive time between HTTP requests is configured in http.conf with the
|
||||
session_keep_alive parameter.
|
||||
|
||||
ODBC:
|
||||
- The compatibility setting, allow_empty_string_in_nontext, has been removed.
|
||||
Empty column values will be stored as empty strings during realtime updates.
|
||||
|
@@ -45,7 +45,14 @@ bindaddr=127.0.0.1
|
||||
; Default: 30000
|
||||
;session_inactivity=30000
|
||||
;
|
||||
; Whether Asterisk should serve static content from http-static
|
||||
; session_keep_alive specifies the number of milliseconds to wait for
|
||||
; the next HTTP request over a persistent connection.
|
||||
;
|
||||
; Set to 0 to disable persistent HTTP connections.
|
||||
; Default: 15000
|
||||
;session_keep_alive=15000
|
||||
;
|
||||
; Whether Asterisk should serve static content from static-http
|
||||
; Default is no.
|
||||
;
|
||||
;enablestatic=yes
|
||||
@@ -80,6 +87,9 @@ bindaddr=127.0.0.1
|
||||
;
|
||||
;[post_mappings]
|
||||
;
|
||||
; NOTE: You need a valid HTTP AMI mansession_id cookie with the manager
|
||||
; config permission to POST files.
|
||||
;
|
||||
; In this example, if the prefix option is set to "asterisk", then using the
|
||||
; POST URL: /asterisk/uploads will put files in /var/lib/asterisk/uploads/.
|
||||
;uploads = /var/lib/asterisk/uploads/
|
||||
|
@@ -66,28 +66,34 @@ enum ast_http_method {
|
||||
|
||||
struct ast_http_uri;
|
||||
|
||||
/*! \brief HTTP Callbacks
|
||||
/*!
|
||||
* \brief HTTP Callbacks
|
||||
*
|
||||
* \note The callback function receives server instance, uri, http method,
|
||||
* get method (if present in URI), and http headers as arguments and should
|
||||
* use the ast_http_send() function for sending content allocated with ast_str
|
||||
* and/or content from an opened file descriptor.
|
||||
* \param ser TCP/TLS session object
|
||||
* \param urih Registered URI handler struct for the URI.
|
||||
* \param uri Remaining request URI path (also with the get_params removed).
|
||||
* \param method enum ast_http_method GET, POST, etc.
|
||||
* \param get_params URI argument list passed with the HTTP request.
|
||||
* \param headers HTTP request header-name/value pair list
|
||||
*
|
||||
* \note Should use the ast_http_send() function for sending content
|
||||
* allocated with ast_str and/or content from an opened file descriptor.
|
||||
*
|
||||
* Status and status text should be sent as arguments to the ast_http_send()
|
||||
* function to reflect the status of the request (200 or 304, for example).
|
||||
* Content length is calculated by ast_http_send() automatically.
|
||||
*
|
||||
* Static content may be indicated to the ast_http_send() function, to indicate
|
||||
* that it may be cached.
|
||||
* Static content may be indicated to the ast_http_send() function,
|
||||
* to indicate that it may be cached.
|
||||
*
|
||||
* \verbatim
|
||||
* The return value may include additional headers at the front and MUST
|
||||
* include a blank line with \r\n to provide separation between user headers
|
||||
* and content (even if no content is specified)
|
||||
* \endverbatim
|
||||
* For a need authentication response, the ast_http_auth() function
|
||||
* should be used.
|
||||
*
|
||||
* For an error response, the ast_http_error() function may be used.
|
||||
*/
|
||||
* For an error response, the ast_http_error() function should be used.
|
||||
*
|
||||
* \retval 0 Continue and process the next HTTP request.
|
||||
* \retval -1 Fatal HTTP connection error. Force the HTTP connection closed.
|
||||
*/
|
||||
typedef int (*ast_http_callback)(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers);
|
||||
|
||||
/*! \brief Definition of a URI handler */
|
||||
@@ -141,26 +147,30 @@ void ast_http_uri_unlink(struct ast_http_uri *urihandler);
|
||||
/*! \brief Unregister all handlers with matching key */
|
||||
void ast_http_uri_unlink_all_with_key(const char *key);
|
||||
|
||||
/*!\brief Return http method name string
|
||||
/*!
|
||||
* \brief Return http method name string
|
||||
* \since 1.8
|
||||
*/
|
||||
const char *ast_get_http_method(enum ast_http_method method) attribute_pure;
|
||||
|
||||
/*!\brief Return mime type based on extension
|
||||
/*!
|
||||
* \brief Return mime type based on extension
|
||||
* \param ftype filename extension
|
||||
* \return String containing associated MIME type
|
||||
* \since 1.8
|
||||
*/
|
||||
const char *ast_http_ftype2mtype(const char *ftype) attribute_pure;
|
||||
|
||||
/*!\brief Return manager id, if exist, from request headers
|
||||
/*!
|
||||
* \brief Return manager id, if exist, from request headers
|
||||
* \param headers List of HTTP headers
|
||||
* \return 32-bit associated manager session identifier
|
||||
* \since 1.8
|
||||
*/
|
||||
uint32_t ast_http_manid_from_vars(struct ast_variable *headers) attribute_pure;
|
||||
|
||||
/*! \brief Generic function for sending http/1.1 response.
|
||||
/*!
|
||||
* \brief Generic function for sending HTTP/1.1 response.
|
||||
* \param ser TCP/TLS session object
|
||||
* \param method GET/POST/HEAD
|
||||
* \param status_code HTTP response code (200/401/403/404/500)
|
||||
@@ -186,12 +196,14 @@ uint32_t ast_http_manid_from_vars(struct ast_variable *headers) attribute_pure;
|
||||
*
|
||||
* \since 1.8
|
||||
*/
|
||||
void ast_http_send(struct ast_tcptls_session_instance *ser, enum ast_http_method method, int status_code, const char *status_title, struct ast_str *http_header, struct ast_str *out, const int fd, unsigned int static_content);
|
||||
void ast_http_send(struct ast_tcptls_session_instance *ser, enum ast_http_method method,
|
||||
int status_code, const char *status_title, struct ast_str *http_header,
|
||||
struct ast_str *out, int fd, unsigned int static_content);
|
||||
|
||||
/*!\brief Send http "401 Unauthorized" response and close socket */
|
||||
/*! \brief Send http "401 Unauthorized" response and close socket */
|
||||
void ast_http_auth(struct ast_tcptls_session_instance *ser, const char *realm, const unsigned long nonce, const unsigned long opaque, int stale, const char *text);
|
||||
|
||||
/*!\brief Send HTTP error message and close socket */
|
||||
/*! \brief Send HTTP error message and close socket */
|
||||
void ast_http_error(struct ast_tcptls_session_instance *ser, int status, const char *title, const char *text);
|
||||
|
||||
/*!
|
||||
@@ -202,8 +214,42 @@ void ast_http_error(struct ast_tcptls_session_instance *ser, int status, const c
|
||||
*/
|
||||
void ast_http_prefix(char *buf, int len);
|
||||
|
||||
/*!
|
||||
* \brief Request the HTTP connection be closed after this HTTP request.
|
||||
* \since 12.4.0
|
||||
*
|
||||
* \param ser HTTP TCP/TLS session object.
|
||||
*
|
||||
* \note Call before ast_http_error() to make the connection close.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
void ast_http_request_close_on_completion(struct ast_tcptls_session_instance *ser);
|
||||
|
||||
/*!\brief Get post variables from client Request Entity-Body, if content type is application/x-www-form-urlencoded.
|
||||
/*!
|
||||
* \brief Update the body read success status.
|
||||
* \since 12.4.0
|
||||
*
|
||||
* \param ser HTTP TCP/TLS session object.
|
||||
* \param read_success TRUE if body was read successfully.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
void ast_http_body_read_status(struct ast_tcptls_session_instance *ser, int read_success);
|
||||
|
||||
/*!
|
||||
* \brief Read and discard any unread HTTP request body.
|
||||
* \since 12.4.0
|
||||
*
|
||||
* \param ser HTTP TCP/TLS session object.
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on error.
|
||||
*/
|
||||
int ast_http_body_discard(struct ast_tcptls_session_instance *ser);
|
||||
|
||||
/*!
|
||||
* \brief Get post variables from client Request Entity-Body, if content type is application/x-www-form-urlencoded.
|
||||
* \param ser TCP/TLS session object
|
||||
* \param headers List of HTTP headers
|
||||
* \return List of variables within the POST body
|
||||
@@ -214,7 +260,8 @@ struct ast_variable *ast_http_get_post_vars(struct ast_tcptls_session_instance *
|
||||
|
||||
struct ast_json;
|
||||
|
||||
/*!\brief Get JSON from client Request Entity-Body, if content type is
|
||||
/*!
|
||||
* \brief Get JSON from client Request Entity-Body, if content type is
|
||||
* application/json.
|
||||
* \param ser TCP/TLS session object
|
||||
* \param headers List of HTTP headers
|
||||
|
@@ -210,7 +210,6 @@ struct ast_tcptls_session_instance {
|
||||
FILE *f; /*!< fopen/funopen result */
|
||||
int fd; /*!< the socket returned by accept() */
|
||||
SSL *ssl; /*!< ssl state */
|
||||
/* iint (*ssl_setup)(SSL *); */
|
||||
int client;
|
||||
struct ast_sockaddr remote_address;
|
||||
struct ast_tcptls_session_args *parent;
|
||||
@@ -222,6 +221,8 @@ struct ast_tcptls_session_instance {
|
||||
struct ast_str *overflow_buf;
|
||||
/*! ao2 FILE stream cookie object associated with f. */
|
||||
struct ast_tcptls_stream *stream_cookie;
|
||||
/*! ao2 object private data of parent->worker_fn */
|
||||
void *private_data;
|
||||
};
|
||||
|
||||
#if defined(HAVE_FUNOPEN)
|
||||
|
967
main/http.c
967
main/http.c
File diff suppressed because it is too large
Load Diff
@@ -6848,9 +6848,10 @@ static int generic_http_callback(struct ast_tcptls_session_instance *ser,
|
||||
{
|
||||
struct mansession s = { .session = NULL, .tcptls_session = ser };
|
||||
struct mansession_session *session = NULL;
|
||||
uint32_t ident = 0;
|
||||
uint32_t ident;
|
||||
int blastaway = 0;
|
||||
struct ast_variable *v, *cookies, *params = get_params;
|
||||
struct ast_variable *v;
|
||||
struct ast_variable *params = get_params;
|
||||
char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
|
||||
struct ast_str *http_header = NULL, *out = NULL;
|
||||
struct message m = { 0 };
|
||||
@@ -6859,19 +6860,10 @@ static int generic_http_callback(struct ast_tcptls_session_instance *ser,
|
||||
|
||||
if (method != AST_HTTP_GET && method != AST_HTTP_HEAD && method != AST_HTTP_POST) {
|
||||
ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
cookies = ast_http_get_cookies(headers);
|
||||
for (v = cookies; v; v = v->next) {
|
||||
if (!strcasecmp(v->name, "mansession_id")) {
|
||||
sscanf(v->value, "%30x", &ident);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cookies) {
|
||||
ast_variables_destroy(cookies);
|
||||
}
|
||||
ident = ast_http_manid_from_vars(headers);
|
||||
|
||||
if (!(session = find_session(ident, 1))) {
|
||||
|
||||
@@ -6880,18 +6872,21 @@ static int generic_http_callback(struct ast_tcptls_session_instance *ser,
|
||||
* While it is not in the list we don't need any locking
|
||||
*/
|
||||
if (!(session = build_mansession(remote_address))) {
|
||||
ast_http_request_close_on_completion(ser);
|
||||
ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
ao2_lock(session);
|
||||
session->send_events = 0;
|
||||
session->inuse = 1;
|
||||
/*!\note There is approximately a 1 in 1.8E19 chance that the following
|
||||
/*!
|
||||
* \note There is approximately a 1 in 1.8E19 chance that the following
|
||||
* calculation will produce 0, which is an invalid ID, but due to the
|
||||
* properties of the rand() function (and the constantcy of s), that
|
||||
* won't happen twice in a row.
|
||||
*/
|
||||
while ((session->managerid = ast_random() ^ (unsigned long) session) == 0);
|
||||
while ((session->managerid = ast_random() ^ (unsigned long) session) == 0) {
|
||||
}
|
||||
session->last_ev = grab_last();
|
||||
AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
|
||||
}
|
||||
@@ -6903,6 +6898,7 @@ static int generic_http_callback(struct ast_tcptls_session_instance *ser,
|
||||
ast_mutex_init(&s.lock);
|
||||
|
||||
if (http_header == NULL || out == NULL) {
|
||||
ast_http_request_close_on_completion(ser);
|
||||
ast_http_error(ser, 500, "Server Error", "Internal Server Error (ast_str_create() out of memory)\n");
|
||||
goto generic_callback_out;
|
||||
}
|
||||
@@ -6924,19 +6920,22 @@ static int generic_http_callback(struct ast_tcptls_session_instance *ser,
|
||||
|
||||
if (method == AST_HTTP_POST) {
|
||||
params = ast_http_get_post_vars(ser, headers);
|
||||
}
|
||||
|
||||
if (!params) {
|
||||
switch (errno) {
|
||||
case EFBIG:
|
||||
ast_http_send(ser, AST_HTTP_POST, 413, "Request Entity Too Large", NULL, NULL, 0, 0);
|
||||
break;
|
||||
ast_http_error(ser, 413, "Request Entity Too Large", "Body too large");
|
||||
close_mansession_file(&s);
|
||||
goto generic_callback_out;
|
||||
case ENOMEM:
|
||||
ast_http_send(ser, AST_HTTP_POST, 500, "Internal Server Error", NULL, NULL, 0, 0);
|
||||
break;
|
||||
ast_http_request_close_on_completion(ser);
|
||||
ast_http_error(ser, 500, "Server Error", "Out of memory");
|
||||
close_mansession_file(&s);
|
||||
goto generic_callback_out;
|
||||
case EIO:
|
||||
ast_http_send(ser, AST_HTTP_POST, 400, "Bad Request", NULL, NULL, 0, 0);
|
||||
break;
|
||||
ast_http_error(ser, 400, "Bad Request", "Error parsing request body");
|
||||
close_mansession_file(&s);
|
||||
goto generic_callback_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6973,7 +6972,6 @@ static int generic_http_callback(struct ast_tcptls_session_instance *ser,
|
||||
|
||||
ast_str_append(&http_header, 0,
|
||||
"Content-type: text/%s\r\n"
|
||||
"Cache-Control: no-cache;\r\n"
|
||||
"Set-Cookie: mansession_id=\"%08x\"; Version=1; Max-Age=%d\r\n"
|
||||
"Pragma: SuppressEvents\r\n",
|
||||
contenttype[format],
|
||||
@@ -7038,7 +7036,8 @@ static int generic_http_callback(struct ast_tcptls_session_instance *ser,
|
||||
ao2_unlock(session);
|
||||
|
||||
ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
|
||||
http_header = out = NULL;
|
||||
http_header = NULL;
|
||||
out = NULL;
|
||||
|
||||
generic_callback_out:
|
||||
ast_mutex_destroy(&s.lock);
|
||||
@@ -7073,7 +7072,7 @@ static int auth_http_callback(struct ast_tcptls_session_instance *ser,
|
||||
struct ast_variable *v, *params = get_params;
|
||||
char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
|
||||
struct ast_str *http_header = NULL, *out = NULL;
|
||||
size_t result_size = 512;
|
||||
size_t result_size;
|
||||
struct message m = { 0 };
|
||||
unsigned int idx;
|
||||
size_t hdrlen;
|
||||
@@ -7093,7 +7092,7 @@ static int auth_http_callback(struct ast_tcptls_session_instance *ser,
|
||||
|
||||
if (method != AST_HTTP_GET && method != AST_HTTP_HEAD && method != AST_HTTP_POST) {
|
||||
ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find "Authorization: " header */
|
||||
@@ -7109,8 +7108,9 @@ static int auth_http_callback(struct ast_tcptls_session_instance *ser,
|
||||
|
||||
/* Digest found - parse */
|
||||
if (ast_string_field_init(&d, 128)) {
|
||||
ast_http_request_close_on_completion(ser);
|
||||
ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ast_parse_digest(v->value, &d, 0, 1)) {
|
||||
@@ -7137,8 +7137,9 @@ static int auth_http_callback(struct ast_tcptls_session_instance *ser,
|
||||
if (user->acl && !ast_apply_acl(user->acl, remote_address, "Manager User ACL:")) {
|
||||
AST_RWLIST_UNLOCK(&users);
|
||||
ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_sockaddr_stringify_addr(&session->addr), d.username);
|
||||
ast_http_request_close_on_completion(ser);
|
||||
ast_http_error(ser, 403, "Permission denied", "Permission denied\n");
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --- We have auth, so check it */
|
||||
@@ -7187,8 +7188,9 @@ static int auth_http_callback(struct ast_tcptls_session_instance *ser,
|
||||
* While it is not in the list we don't need any locking
|
||||
*/
|
||||
if (!(session = build_mansession(remote_address))) {
|
||||
ast_http_request_close_on_completion(ser);
|
||||
ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
ao2_lock(session);
|
||||
|
||||
@@ -7268,6 +7270,23 @@ static int auth_http_callback(struct ast_tcptls_session_instance *ser,
|
||||
|
||||
if (method == AST_HTTP_POST) {
|
||||
params = ast_http_get_post_vars(ser, headers);
|
||||
if (!params) {
|
||||
switch (errno) {
|
||||
case EFBIG:
|
||||
ast_http_error(ser, 413, "Request Entity Too Large", "Body too large");
|
||||
close_mansession_file(&s);
|
||||
goto auth_callback_out;
|
||||
case ENOMEM:
|
||||
ast_http_request_close_on_completion(ser);
|
||||
ast_http_error(ser, 500, "Server Error", "Out of memory");
|
||||
close_mansession_file(&s);
|
||||
goto auth_callback_out;
|
||||
case EIO:
|
||||
ast_http_error(ser, 400, "Bad Request", "Error parsing request body");
|
||||
close_mansession_file(&s);
|
||||
goto auth_callback_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (v = params; v && m.hdrcount < ARRAY_LEN(m.headers); v = v->next) {
|
||||
@@ -7296,15 +7315,14 @@ static int auth_http_callback(struct ast_tcptls_session_instance *ser,
|
||||
m.headers[idx] = NULL;
|
||||
}
|
||||
|
||||
if (s.f) {
|
||||
result_size = ftell(s.f); /* Calculate approx. size of result */
|
||||
}
|
||||
|
||||
http_header = ast_str_create(80);
|
||||
out = ast_str_create(result_size * 2 + 512);
|
||||
|
||||
if (http_header == NULL || out == NULL) {
|
||||
ast_http_request_close_on_completion(ser);
|
||||
ast_http_error(ser, 500, "Server Error", "Internal Server Error (ast_str_create() out of memory)\n");
|
||||
close_mansession_file(&s);
|
||||
goto auth_callback_out;
|
||||
}
|
||||
|
||||
@@ -7334,7 +7352,8 @@ static int auth_http_callback(struct ast_tcptls_session_instance *ser,
|
||||
}
|
||||
|
||||
ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
|
||||
http_header = out = NULL;
|
||||
http_header = NULL;
|
||||
out = NULL;
|
||||
|
||||
auth_callback_out:
|
||||
ast_mutex_destroy(&s.lock);
|
||||
|
@@ -543,6 +543,7 @@ static void session_instance_destructor(void *obj)
|
||||
i->stream_cookie = NULL;
|
||||
}
|
||||
ast_free(i->overflow_buf);
|
||||
ao2_cleanup(i->private_data);
|
||||
}
|
||||
|
||||
/*! \brief
|
||||
|
@@ -262,6 +262,7 @@ void ast_ari_response_error(struct ast_ari_response *response,
|
||||
|
||||
va_start(ap, message_fmt);
|
||||
message = ast_json_vstringf(message_fmt, ap);
|
||||
va_end(ap);
|
||||
response->message = ast_json_pack("{s: o}",
|
||||
"message", ast_json_ref(message));
|
||||
response->response_code = response_code;
|
||||
@@ -884,23 +885,25 @@ static int ast_ari_callback(struct ast_tcptls_session_instance *ser,
|
||||
* with us.
|
||||
*/
|
||||
post_vars = ast_http_get_post_vars(ser, headers);
|
||||
if (get_params == NULL) {
|
||||
if (!post_vars) {
|
||||
switch (errno) {
|
||||
case EFBIG:
|
||||
ast_ari_response_error(&response, 413,
|
||||
"Request Entity Too Large",
|
||||
"Request body too large");
|
||||
break;
|
||||
goto request_failed;
|
||||
case ENOMEM:
|
||||
ast_ari_response_error(&response, 500,
|
||||
"Internal Server Error",
|
||||
"Error processing request");
|
||||
break;
|
||||
goto request_failed;
|
||||
case EIO:
|
||||
ast_ari_response_error(&response, 400,
|
||||
"Bad Request", "Error parsing request body");
|
||||
break;
|
||||
goto request_failed;
|
||||
}
|
||||
}
|
||||
if (get_params == NULL) {
|
||||
get_params = post_vars;
|
||||
} else if (get_params && post_vars) {
|
||||
/* Has both post_vars and get_params */
|
||||
@@ -963,6 +966,7 @@ static int ast_ari_callback(struct ast_tcptls_session_instance *ser,
|
||||
return 0;
|
||||
}
|
||||
|
||||
request_failed:
|
||||
/* If you explicitly want to have no content, set message to
|
||||
* ast_json_null().
|
||||
*/
|
||||
|
@@ -213,7 +213,7 @@ static int find_sequence(char * inbuf, int inlen, char * matchbuf, int matchlen)
|
||||
* This function has two modes. The first to find a boundary marker. The
|
||||
* second is to find the filename immediately after the boundary.
|
||||
*/
|
||||
static int readmimefile(FILE * fin, FILE * fout, char * boundary, int contentlen)
|
||||
static int readmimefile(FILE *fin, FILE *fout, char *boundary, int contentlen)
|
||||
{
|
||||
int find_filename = 0;
|
||||
char buf[4096];
|
||||
@@ -313,53 +313,41 @@ static int readmimefile(FILE * fin, FILE * fout, char * boundary, int contentlen
|
||||
|
||||
static int http_post_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
|
||||
{
|
||||
struct ast_variable *var, *cookies;
|
||||
unsigned long ident = 0;
|
||||
struct ast_variable *var;
|
||||
uint32_t ident;
|
||||
FILE *f;
|
||||
int content_len = 0;
|
||||
struct ast_str *post_dir;
|
||||
GMimeMessage *message;
|
||||
char * boundary_marker = NULL;
|
||||
char *boundary_marker = NULL;
|
||||
|
||||
if (method != AST_HTTP_POST) {
|
||||
ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!astman_is_authed(ast_http_manid_from_vars(headers))) {
|
||||
ast_http_error(ser, 403, "Access Denied", "Sorry, I cannot let you do that, Dave.");
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!urih) {
|
||||
ast_http_error(ser, 400, "Missing URI handle", "There was an error parsing the request");
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
cookies = ast_http_get_cookies(headers);
|
||||
for (var = cookies; var; var = var->next) {
|
||||
if (!strcasecmp(var->name, "mansession_id")) {
|
||||
sscanf(var->value, "%30lx", &ident);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cookies) {
|
||||
ast_variables_destroy(cookies);
|
||||
ident = ast_http_manid_from_vars(headers);
|
||||
if (!ident || !astman_is_authed(ident)) {
|
||||
ast_http_request_close_on_completion(ser);
|
||||
ast_http_error(ser, 403, "Access Denied", "Sorry, I cannot let you do that, Dave.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ident == 0) {
|
||||
ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request.");
|
||||
return -1;
|
||||
}
|
||||
if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
|
||||
ast_http_request_close_on_completion(ser);
|
||||
ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request.");
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(f = tmpfile())) {
|
||||
ast_log(LOG_ERROR, "Could not create temp file.\n");
|
||||
ast_http_error(ser, 500, "Internal server error", "Could not create temp file.");
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (var = headers; var; var = var->next) {
|
||||
@@ -369,8 +357,9 @@ static int http_post_callback(struct ast_tcptls_session_instance *ser, const str
|
||||
if ((sscanf(var->value, "%30u", &content_len)) != 1) {
|
||||
ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n");
|
||||
fclose(f);
|
||||
ast_http_error(ser, 500, "Internal server error", "Invalid Content-Length in POST request!");
|
||||
return -1;
|
||||
ast_http_request_close_on_completion(ser);
|
||||
ast_http_error(ser, 400, "Bad Request", "Invalid Content-Length in POST request!");
|
||||
return 0;
|
||||
}
|
||||
ast_debug(1, "Got a Content-Length of %d\n", content_len);
|
||||
} else if (!strcasecmp(var->name, "Content-Type")) {
|
||||
@@ -380,42 +369,50 @@ static int http_post_callback(struct ast_tcptls_session_instance *ser, const str
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(f, "\r\n");
|
||||
|
||||
/*
|
||||
* Always mark the body read as failed.
|
||||
*
|
||||
* XXX Should change readmimefile() to always be sure to read
|
||||
* the entire body so we can update the read status and
|
||||
* potentially keep the connection open.
|
||||
*/
|
||||
ast_http_body_read_status(ser, 0);
|
||||
|
||||
if (0 > readmimefile(ser->f, f, boundary_marker, content_len)) {
|
||||
ast_debug(1, "Cannot find boundary marker in POST request.\n");
|
||||
fclose(f);
|
||||
|
||||
return -1;
|
||||
ast_http_error(ser, 400, "Bad Request", "Cannot find boundary marker in POST request.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fseek(f, SEEK_SET, 0)) {
|
||||
ast_log(LOG_ERROR, "Failed to seek temp file back to beginning.\n");
|
||||
fclose(f);
|
||||
ast_http_error(ser, 500, "Internal server error", "Failed to seek temp file back to beginning.");
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
post_dir = urih->data;
|
||||
|
||||
message = parse_message(f); /* Takes ownership and will close f */
|
||||
|
||||
if (!message) {
|
||||
ast_log(LOG_ERROR, "Error parsing MIME data\n");
|
||||
|
||||
ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request.");
|
||||
return -1;
|
||||
ast_http_error(ser, 400, "Bad Request", "There was an error parsing the request.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!process_message(message, ast_str_buffer(post_dir))) {
|
||||
ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
|
||||
g_object_unref(message);
|
||||
ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request.");
|
||||
return -1;
|
||||
ast_http_error(ser, 400, "Bad Request", "There was an error parsing the request.");
|
||||
return 0;
|
||||
}
|
||||
g_object_unref(message);
|
||||
|
||||
/* XXX Passing 200 to the error response routine? */
|
||||
ast_http_error(ser, 200, "OK", "File successfully uploaded.");
|
||||
return 0;
|
||||
}
|
||||
@@ -427,7 +424,7 @@ static int __ast_http_post_load(int reload)
|
||||
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
|
||||
|
||||
cfg = ast_config_load2("http.conf", "http", config_flags);
|
||||
if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
|
||||
if (!cfg || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -435,7 +432,6 @@ static int __ast_http_post_load(int reload)
|
||||
ast_http_uri_unlink_all_with_key(__FILE__);
|
||||
}
|
||||
|
||||
if (cfg) {
|
||||
for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
|
||||
if (!strcasecmp(v->name, "prefix")) {
|
||||
ast_copy_string(prefix, v->value, sizeof(prefix));
|
||||
@@ -473,7 +469,6 @@ static int __ast_http_post_load(int reload)
|
||||
}
|
||||
|
||||
ast_config_destroy(cfg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -604,6 +604,19 @@ static char *websocket_combine_key(const char *key, char *res, int res_size)
|
||||
return res;
|
||||
}
|
||||
|
||||
static void websocket_bad_request(struct ast_tcptls_session_instance *ser)
|
||||
{
|
||||
struct ast_str *http_header = ast_str_create(64);
|
||||
|
||||
if (!http_header) {
|
||||
ast_http_request_close_on_completion(ser);
|
||||
ast_http_error(ser, 500, "Server Error", "Out of memory");
|
||||
return;
|
||||
}
|
||||
ast_str_set(&http_header, 0, "Sec-WebSocket-Version: 7, 8, 13\r\n");
|
||||
ast_http_send(ser, AST_HTTP_UNKNOWN, 400, "Bad Request", http_header, NULL, 0, 0);
|
||||
}
|
||||
|
||||
int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
|
||||
{
|
||||
struct ast_variable *v;
|
||||
@@ -618,7 +631,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
|
||||
/* Upgrade requests are only permitted on GET methods */
|
||||
if (method != AST_HTTP_GET) {
|
||||
ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
server = urih->data;
|
||||
@@ -648,7 +661,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
|
||||
ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - did not request WebSocket\n",
|
||||
ast_sockaddr_stringify(&ser->remote_address));
|
||||
ast_http_error(ser, 426, "Upgrade Required", NULL);
|
||||
return -1;
|
||||
return 0;
|
||||
} else if (ast_strlen_zero(requested_protocols)) {
|
||||
/* If there's only a single protocol registered, and the
|
||||
* client doesn't specify what protocol it's using, go ahead
|
||||
@@ -658,17 +671,15 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
|
||||
/* Multiple registered subprotocols; client must specify */
|
||||
ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - no protocols requested\n",
|
||||
ast_sockaddr_stringify(&ser->remote_address));
|
||||
fputs("HTTP/1.1 400 Bad Request\r\n"
|
||||
"Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
|
||||
return -1;
|
||||
websocket_bad_request(ser);
|
||||
return 0;
|
||||
}
|
||||
} else if (key1 && key2) {
|
||||
/* Specification defined in http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76 and
|
||||
* http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 -- not currently supported*/
|
||||
ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - unsupported version '00/76' chosen\n",
|
||||
ast_sockaddr_stringify(&ser->remote_address));
|
||||
fputs("HTTP/1.1 400 Bad Request\r\n"
|
||||
"Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
|
||||
websocket_bad_request(ser);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -681,8 +692,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
|
||||
if (!protocol_handler) {
|
||||
ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - no protocols out of '%s' supported\n",
|
||||
ast_sockaddr_stringify(&ser->remote_address), protos);
|
||||
fputs("HTTP/1.1 400 Bad Request\r\n"
|
||||
"Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
|
||||
websocket_bad_request(ser);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -691,8 +701,13 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
|
||||
char base64[64];
|
||||
|
||||
if (!key || strlen(key) + strlen(WEBSOCKET_GUID) + 1 > 8192) { /* no stack overflows please */
|
||||
fputs("HTTP/1.1 400 Bad Request\r\n"
|
||||
"Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
|
||||
websocket_bad_request(ser);
|
||||
ao2_ref(protocol_handler, -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ast_http_body_discard(ser)) {
|
||||
websocket_bad_request(ser);
|
||||
ao2_ref(protocol_handler, -1);
|
||||
return 0;
|
||||
}
|
||||
@@ -700,8 +715,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
|
||||
if (!(session = ao2_alloc(sizeof(*session), session_destroy_fn))) {
|
||||
ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted\n",
|
||||
ast_sockaddr_stringify(&ser->remote_address));
|
||||
fputs("HTTP/1.1 400 Bad Request\r\n"
|
||||
"Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
|
||||
websocket_bad_request(ser);
|
||||
ao2_ref(protocol_handler, -1);
|
||||
return 0;
|
||||
}
|
||||
@@ -735,8 +749,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
|
||||
/* Specification defined in http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 or completely unknown */
|
||||
ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - unsupported version '%d' chosen\n",
|
||||
ast_sockaddr_stringify(&ser->remote_address), version ? version : 75);
|
||||
fputs("HTTP/1.1 400 Bad Request\r\n"
|
||||
"Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
|
||||
websocket_bad_request(ser);
|
||||
ao2_ref(protocol_handler, -1);
|
||||
return 0;
|
||||
}
|
||||
@@ -745,8 +758,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
|
||||
if (setsockopt(ser->fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags))) {
|
||||
ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - failed to enable keepalive\n",
|
||||
ast_sockaddr_stringify(&ser->remote_address));
|
||||
fputs("HTTP/1.1 400 Bad Request\r\n"
|
||||
"Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
|
||||
websocket_bad_request(ser);
|
||||
ao2_ref(session, -1);
|
||||
ao2_ref(protocol_handler, -1);
|
||||
return 0;
|
||||
@@ -763,6 +775,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
|
||||
session->secure = ser->ssl ? 1 : 0;
|
||||
|
||||
/* Give up ownership of the socket and pass it to the protocol handler */
|
||||
ast_tcptls_stream_set_exclusive_input(ser->stream_cookie, 0);
|
||||
protocol_handler->callback(session, get_vars, headers);
|
||||
ao2_ref(protocol_handler, -1);
|
||||
|
||||
|
@@ -428,7 +428,7 @@ static int phoneprov_callback(struct ast_tcptls_session_instance *ser, const str
|
||||
|
||||
if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
|
||||
ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(route = ao2_find(http_routes, &search_route, OBJ_POINTER))) {
|
||||
@@ -542,12 +542,12 @@ static int phoneprov_callback(struct ast_tcptls_session_instance *ser, const str
|
||||
|
||||
out404:
|
||||
ast_http_error(ser, 404, "Not Found", "Nothing to see here. Move along.");
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
out500:
|
||||
route = unref_route(route);
|
||||
ast_http_error(ser, 500, "Internal Error", "An internal error has occured.");
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Build a route structure and add it to the list of available http routes
|
||||
|
Reference in New Issue
Block a user