mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
remove need for filepath buffer on http file serve
This gets rid of the stack buffer while serving files, and the PATH_MAX char array that used to hold the filepath in the wsi. It holds an extra file descriptor open while serving the file, however it attempts to stuff the socket with as much of the file as it can take. For files of a few KB, that typically completes (without blocking) in the call to libwebsockets_serve_http_file() and then closes the file descriptor before returning. Signed-off-by: Andy Green <andy.green@linaro.org>
This commit is contained in:
parent
c11b587aed
commit
b8b247d3e1
6 changed files with 81 additions and 117 deletions
|
@ -178,6 +178,11 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context,
|
|||
|
||||
wsi->u.ws.close_reason = reason;
|
||||
|
||||
if (wsi->mode == LWS_CONNMODE_HTTP_SERVING && wsi->u.http.fd) {
|
||||
close(wsi->u.http.fd);
|
||||
wsi->u.http.fd = 0;
|
||||
}
|
||||
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
/*
|
||||
* are his extensions okay with him closing? Eg he might be a mux
|
||||
|
@ -653,10 +658,8 @@ notify_action:
|
|||
else
|
||||
n = LWS_CALLBACK_SERVER_WRITEABLE;
|
||||
|
||||
user_callback_handle_rxflow(wsi->protocol->callback, context,
|
||||
return user_callback_handle_rxflow(wsi->protocol->callback, context,
|
||||
wsi, (enum libwebsocket_callback_reasons) n, wsi->user_space, NULL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1419,8 +1422,11 @@ int user_callback_handle_rxflow(callback_function callback_function,
|
|||
int n;
|
||||
|
||||
n = callback_function(context, wsi, reason, user, in, len);
|
||||
if (n < 0)
|
||||
if (n) {
|
||||
libwebsocket_close_and_free_session(context, wsi,
|
||||
LWS_CLOSE_STATUS_NOSTATUS);
|
||||
return n;
|
||||
}
|
||||
|
||||
_libwebsocket_rx_flow_control(wsi);
|
||||
|
||||
|
|
161
lib/output.c
161
lib/output.c
|
@ -480,6 +480,39 @@ send_raw:
|
|||
return lws_issue_raw_ext_access(wsi, buf - pre, len + pre + post);
|
||||
}
|
||||
|
||||
int libwebsockets_serve_http_file_fragment(struct libwebsocket_context *context,
|
||||
struct libwebsocket *wsi)
|
||||
{
|
||||
int ret = 0;
|
||||
int n;
|
||||
|
||||
while (!lws_send_pipe_choked(wsi)) {
|
||||
n = read(wsi->u.http.fd, context->service_buffer, sizeof(context->service_buffer));
|
||||
if (n > 0) {
|
||||
libwebsocket_write(wsi, context->service_buffer, n, LWS_WRITE_HTTP);
|
||||
wsi->u.http.filepos += n;
|
||||
}
|
||||
|
||||
if (n < 0) {
|
||||
libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (n < sizeof(context->service_buffer) || wsi->u.http.filepos == wsi->u.http.filelen) {
|
||||
wsi->state = WSI_STATE_HTTP;
|
||||
|
||||
if (wsi->protocol->callback)
|
||||
ret = user_callback_handle_rxflow(wsi->protocol->callback, context, wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION, wsi->user_space,
|
||||
NULL, 0);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
lwsl_notice("choked before able to send whole file (post)\n");
|
||||
libwebsocket_callback_on_writable(context, wsi);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* libwebsockets_serve_http_file() - Send a file back to the client using http
|
||||
|
@ -491,138 +524,56 @@ send_raw:
|
|||
* This function is intended to be called from the callback in response
|
||||
* to http requests from the client. It allows the callback to issue
|
||||
* local files down the http link in a single step.
|
||||
*
|
||||
* Returning <0 indicates error and the wsi should be closed. Returning
|
||||
* >0 indicates the file was completely sent and the wsi should be closed.
|
||||
* ==0 indicates the file transfer is started and needs more service later,
|
||||
* the wsi should be left alone.
|
||||
*/
|
||||
|
||||
int libwebsockets_serve_http_file(struct libwebsocket_context *context,
|
||||
struct libwebsocket *wsi, const char *file,
|
||||
const char *content_type)
|
||||
{
|
||||
int fd;
|
||||
struct stat stat_buf;
|
||||
char buf[1400];
|
||||
char *p = buf;
|
||||
int n, m;
|
||||
|
||||
strncpy(wsi->u.http.filepath, file, sizeof wsi->u.http.filepath);
|
||||
wsi->u.http.filepath[sizeof(wsi->u.http.filepath) - 1] = '\0';
|
||||
unsigned char *p = context->service_buffer;
|
||||
int ret = 0;
|
||||
|
||||
wsi->u.http.fd = open(file, O_RDONLY
|
||||
#ifdef WIN32
|
||||
fd = open(wsi->u.http.filepath, O_RDONLY | _O_BINARY);
|
||||
#else
|
||||
fd = open(wsi->u.http.filepath, O_RDONLY);
|
||||
| _O_BINARY
|
||||
#endif
|
||||
if (fd < 1) {
|
||||
p += sprintf(p, "HTTP/1.0 400 Bad\x0d\x0a"
|
||||
);
|
||||
|
||||
if (wsi->u.http.fd < 1) {
|
||||
p += sprintf((char *)p, "HTTP/1.0 400 Bad\x0d\x0a"
|
||||
"Server: libwebsockets\x0d\x0a"
|
||||
"\x0d\x0a"
|
||||
);
|
||||
libwebsocket_write(wsi, (unsigned char *)buf, p - buf,
|
||||
LWS_WRITE_HTTP);
|
||||
wsi->u.http.fd = 0;
|
||||
libwebsocket_write(wsi, context->service_buffer,
|
||||
p - context->service_buffer, LWS_WRITE_HTTP);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
fstat(fd, &stat_buf);
|
||||
fstat(wsi->u.http.fd, &stat_buf);
|
||||
wsi->u.http.filelen = stat_buf.st_size;
|
||||
p += sprintf(p, "HTTP/1.0 200 OK\x0d\x0a"
|
||||
p += sprintf((char *)p, "HTTP/1.0 200 OK\x0d\x0a"
|
||||
"Server: libwebsockets\x0d\x0a"
|
||||
"Content-Type: %s\x0d\x0a"
|
||||
"Content-Length: %u\x0d\x0a"
|
||||
"\x0d\x0a", content_type,
|
||||
(unsigned int)stat_buf.st_size);
|
||||
|
||||
n = libwebsocket_write(wsi, (unsigned char *)buf, p - buf, LWS_WRITE_HTTP);
|
||||
if (n) {
|
||||
close(fd);
|
||||
return n;
|
||||
}
|
||||
ret = libwebsocket_write(wsi, context->service_buffer,
|
||||
p - context->service_buffer, LWS_WRITE_HTTP);
|
||||
if (ret)
|
||||
return -1;
|
||||
|
||||
wsi->u.http.filepos = 0;
|
||||
wsi->state = WSI_STATE_HTTP_ISSUING_FILE;
|
||||
|
||||
while (!lws_send_pipe_choked(wsi)) {
|
||||
|
||||
n = read(fd, buf, sizeof buf);
|
||||
if (n > 0) {
|
||||
wsi->u.http.filepos += n;
|
||||
m = libwebsocket_write(wsi, (unsigned char *)buf, n, LWS_WRITE_HTTP);
|
||||
if (m) {
|
||||
close(fd);
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
if (n < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (n < sizeof(buf) || wsi->u.http.filepos == wsi->u.http.filelen) {
|
||||
/* oh, we were able to finish here! */
|
||||
wsi->state = WSI_STATE_HTTP;
|
||||
close(fd);
|
||||
|
||||
if (wsi->protocol->callback(context, wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION, wsi->user_space,
|
||||
wsi->u.http.filepath, wsi->u.http.filepos)) {
|
||||
lwsl_info("closing connecton after file_completion returned nonzero\n");
|
||||
libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* we choked, no worries schedule service for the rest of it */
|
||||
|
||||
libwebsocket_callback_on_writable(context, wsi);
|
||||
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
return libwebsockets_serve_http_file_fragment(context, wsi);
|
||||
}
|
||||
|
||||
int libwebsockets_serve_http_file_fragment(struct libwebsocket_context *context,
|
||||
struct libwebsocket *wsi)
|
||||
{
|
||||
int fd;
|
||||
int ret = 0;
|
||||
char buf[1400];
|
||||
int n;
|
||||
|
||||
#ifdef WIN32
|
||||
fd = open(wsi->u.http.filepath, O_RDONLY | _O_BINARY);
|
||||
#else
|
||||
fd = open(wsi->u.http.filepath, O_RDONLY);
|
||||
#endif
|
||||
if (fd < 1)
|
||||
return -1;
|
||||
|
||||
lseek(fd, wsi->u.http.filepos, SEEK_SET);
|
||||
|
||||
while (!lws_send_pipe_choked(wsi)) {
|
||||
n = read(fd, buf, sizeof buf);
|
||||
if (n > 0) {
|
||||
libwebsocket_write(wsi, (unsigned char *)buf, n, LWS_WRITE_HTTP);
|
||||
wsi->u.http.filepos += n;
|
||||
}
|
||||
|
||||
if (n < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (n < sizeof(buf) || wsi->u.http.filepos == wsi->u.http.filelen) {
|
||||
wsi->state = WSI_STATE_HTTP;
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
libwebsocket_callback_on_writable(context, wsi);
|
||||
|
||||
close(fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -256,6 +256,13 @@ struct libwebsocket_context {
|
|||
unsigned int options;
|
||||
unsigned long last_timeout_check_s;
|
||||
|
||||
/*
|
||||
* usable by anything in the service code, but only if the scope
|
||||
* does not last longer than the service action (since next service
|
||||
* of any socket can likewise use it and overwrite)
|
||||
*/
|
||||
unsigned char service_buffer[4096];
|
||||
|
||||
int started_with_parent;
|
||||
|
||||
int fd_random;
|
||||
|
@ -303,7 +310,7 @@ enum pending_timeout {
|
|||
*/
|
||||
|
||||
struct _lws_http_mode_related {
|
||||
char filepath[PATH_MAX];
|
||||
int fd;
|
||||
unsigned long filepos;
|
||||
unsigned long filelen;
|
||||
};
|
||||
|
|
|
@ -189,14 +189,9 @@ int lws_server_socket_service(struct libwebsocket_context *context,
|
|||
if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE)
|
||||
break;
|
||||
|
||||
if (libwebsockets_serve_http_file_fragment(context, wsi) < 0)
|
||||
if (libwebsockets_serve_http_file_fragment(context, wsi)) /* nonzero for completion or error */
|
||||
libwebsocket_close_and_free_session(context, wsi,
|
||||
LWS_CLOSE_STATUS_NOSTATUS);
|
||||
else
|
||||
if (wsi->state == WSI_STATE_HTTP && wsi->protocol->callback)
|
||||
if (user_callback_handle_rxflow(wsi->protocol->callback, context, wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION, wsi->user_space,
|
||||
wsi->u.http.filepath, wsi->u.http.filepos))
|
||||
libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS);
|
||||
break;
|
||||
|
||||
case LWS_CONNMODE_SERVER_LISTENER:
|
||||
|
|
|
@ -517,6 +517,11 @@ packet while not burdening the user code with any protocol knowledge.
|
|||
This function is intended to be called from the callback in response
|
||||
to http requests from the client. It allows the callback to issue
|
||||
local files down the http link in a single step.
|
||||
<p>
|
||||
Returning <0 indicates error and the wsi should be closed. Returning
|
||||
>0 indicates the file was completely sent and the wsi should be closed.
|
||||
==0 indicates the file transfer is started and needs more service later,
|
||||
the wsi should be left alone.
|
||||
</blockquote>
|
||||
<hr>
|
||||
<h2>lws_frame_is_binary - </h2>
|
||||
|
|
|
@ -118,7 +118,7 @@ static int callback_http(struct libwebsocket_context *context,
|
|||
sprintf(buf, LOCAL_RESOURCE_PATH"%s", whitelist[n].urlpath);
|
||||
|
||||
if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype))
|
||||
lwsl_err("Failed to send HTTP file\n");
|
||||
return 1; /* through completion or error, close the socket */
|
||||
|
||||
/*
|
||||
* notice that the sending of the file completes asynchronously,
|
||||
|
|
Loading…
Add table
Reference in a new issue