httpd: expose http_send_headers to be used in CGI handlers

This commit is contained in:
Matias N 2021-03-30 14:17:21 -03:00 committed by Xiang Xiao
parent 36d4bfa774
commit d548803d38
3 changed files with 300 additions and 239 deletions

View File

@ -192,12 +192,83 @@ EXTERN const int g_httpd_numfiles;
* Public Function Prototypes * Public Function Prototypes
****************************************************************************/ ****************************************************************************/
/****************************************************************************
* Name: httpd_init
*
* Description:
* This function initializes the web server and should be called at system
* boot-up.
*
****************************************************************************/
void httpd_init(void); void httpd_init(void);
/****************************************************************************
* Name: httpd_listen
*
* Description:
* This is the main processing thread for the webserver. It never returns
* unless an error occurs
*
****************************************************************************/
int httpd_listen(void); int httpd_listen(void);
/****************************************************************************
* Name: httpd_cgi_register
*
* Description:
* Register a CGI handler
*
****************************************************************************/
void httpd_cgi_register(struct httpd_cgi_call *cgi_call); void httpd_cgi_register(struct httpd_cgi_call *cgi_call);
uint16_t httpd_fs_count(char *name);
/****************************************************************************
* Name: httpd_send_datachunk
*
* Description:
* Sends a chunk of HTML data using either chunked or non-chunked encoding.
*
* Input Parameters:
* sockfd Socket to which to send the data.
* data Data to send
* len Length of data to send
* chunked If True, sends an HTTP Chunked-Encoding prolog before the data
* block, and a HTTP Chunked-Encoding epilog ("\r\n") after the
* data block. If False, just sends the data.
*
* Returned Value:
* On success, returns >=0. On failure, returns a negative number
* indicating the failure code.
*
****************************************************************************/
int httpd_send_datachunk(int sockfd, void *data, int len, bool chunked); int httpd_send_datachunk(int sockfd, void *data, int len, bool chunked);
/****************************************************************************
* Name: httpd_send_headers
*
* Description:
* Sends HTTP headers
*
* Input Parameters:
* pstate The httpd state
* status Numeric HTTP status code
* len Length of data to be sent in subsequent send
*
* Returned Value:
* On success, returns >=0. On failure, returns a negative number
* indicating the failure code.
*
****************************************************************************/
int httpd_send_headers(struct httpd_state *pstate, int status, int len);
#ifdef CONFIG_NETUTILS_HTTPDFSSTATS
uint16_t httpd_fs_count(char *name);
#endif
#ifdef CONFIG_NETUTILS_HTTPD_DIRLIST #ifdef CONFIG_NETUTILS_HTTPD_DIRLIST
bool httpd_is_file(FAR const char *filename); bool httpd_is_file(FAR const char *filename);
ssize_t httpd_dirlist(int outfd, FAR struct httpd_fs_file *file); ssize_t httpd_dirlist(int outfd, FAR struct httpd_fs_file *file);

View File

@ -1,5 +1,5 @@
/**************************************************************************** /****************************************************************************
* netutils/webserver/httpd.c * apps/netutils/webserver/httpd.c
* httpd Web server * httpd Web server
* *
* Copyright (C) 2007-2009, 2011-2012 Gregory Nutt. All rights reserved. * Copyright (C) 2007-2009, 2011-2012 Gregory Nutt. All rights reserved.
@ -192,76 +192,6 @@ static int httpd_close(struct httpd_fs_file *file)
#endif #endif
} }
/****************************************************************************
* Name: httpd_send_datachunk
*
* Description:
* Sends a chunk of HTML data using either chunked or non-chunked encoding.
*
* Input Parameters:
* sockfd Socket to which to send the data.
* data Data to send
* len Length of data to send
* chunked If True, sends an HTTP Chunked-Encoding prolog before the data
* block, and a HTTP Chunked-Encoding epilog ("\r\n") after the
* data block. If False, just sends the data.
*
* Returned Value:
* On success, returns >=0. On failure, returns a negative number
* indicating the failure code.
*
****************************************************************************/
int httpd_send_datachunk(int sockfd, void *data, int len, bool chunked)
{
int ret = 0;
#if defined(CONFIG_NETUTILS_HTTPD_ENABLE_CHUNKED_ENCODING)
char chunked_info[HTTPD_MAX_CHUNKEDLEN];
#endif
#if defined(CONFIG_NETUTILS_HTTPD_ENABLE_CHUNKED_ENCODING)
/* Chunk prolog */
if (chunked)
{
int chunked_info_len = snprintf(chunked_info, HTTPD_MAX_CHUNKEDLEN,
"%X\r\n", len);
ret = send(sockfd, chunked_info, chunked_info_len, 0);
DEBUGASSERT(ret == chunked_info_len);
}
#endif
if (ret >= 0)
{
if (len == 0)
{
/* Lower layer does not tolerate buf = NULL even if len = 0
* so just pass a dummy pointer.
*/
data = &len;
}
ret = send(sockfd, data, len, 0);
DEBUGASSERT(ret == len);
}
#if defined(CONFIG_NETUTILS_HTTPD_ENABLE_CHUNKED_ENCODING)
/* Chunk epilog */
if (ret >= 0)
{
if (chunked)
{
ret = send(sockfd, "\r\n", 2, 0);
DEBUGASSERT(ret == 2);
}
}
#endif
return ret;
}
#ifdef CONFIG_NETUTILS_HTTPD_DUMPBUFFER #ifdef CONFIG_NETUTILS_HTTPD_DUMPBUFFER
static void httpd_dumpbuffer(FAR const char *msg, FAR const char *buffer, static void httpd_dumpbuffer(FAR const char *msg, FAR const char *buffer,
unsigned int nbytes) unsigned int nbytes)
@ -440,153 +370,6 @@ static int send_chunk(struct httpd_state *pstate, const char *buf, int len)
return OK; return OK;
} }
static int send_headers(struct httpd_state *pstate, int status, int len)
{
const char *mime;
const char *ptr;
char contentlen[HTTPD_MAX_CONTENTLEN] =
{
0
};
char header[HTTPD_MAX_HEADERLEN];
int hdrlen;
int i;
static const struct
{
const char *ext;
const char *mime;
}
a[] =
{
#ifndef CONFIG_NETUTILS_HTTPD_SCRIPT_DISABLE
{
"shtml", "text/html"
},
#endif
{
"html", "text/html"
},
{
"css", "text/css"
},
{
"txt", "text/plain"
},
{
"js", "text/javascript"
},
{
"png", "image/png"
},
{
"gif", "image/gif"
},
{
"jpeg", "image/jpeg"
},
{
"jpg", "image/jpeg"
},
{
"mp3", "audio/mpeg"
}
};
ptr = strrchr(pstate->ht_filename, ISO_PERIOD);
if (ptr == NULL)
{
mime = "application/octet-stream";
}
else
{
mime = "text/plain";
for (i = 0; i < sizeof a / sizeof *a; i++)
{
if (strncmp(a[i].ext, ptr + 1, strlen(a[i].ext)) == 0)
{
mime = a[i].mime;
break;
}
}
}
#ifdef CONFIG_NETUTILS_HTTPD_DIRLIST
if (false == httpd_is_file(pstate->ht_filename))
{
/* we assume that it's a directory */
mime = "text/html";
}
#endif
if (len >= 0)
{
snprintf(contentlen, HTTPD_MAX_CONTENTLEN,
"Content-Length: %d\r\n", len);
}
else
{
#ifndef CONFIG_NETUTILS_HTTPD_KEEPALIVE_DISABLE
/* Length unknown ahead of time */
pstate->ht_keepalive = false;
#endif
#if defined(CONFIG_NETUTILS_HTTPD_ENABLE_CHUNKED_ENCODING)
/* Turn on chunked encoding */
snprintf(contentlen, HTTPD_MAX_CONTENTLEN,
"Transfer-Encoding: chunked\r\n");
pstate->ht_chunked = true;
#endif
}
if (status == 413)
{
/* TODO: here we "SHOULD" include a Retry-After header */
}
/* Construct the header.
*
* REVISIT: Wouldn't asprintf be a better option than a large stack
* array?
*/
hdrlen = snprintf(header, HTTPD_MAX_HEADERLEN,
"HTTP/1.0 %d %s\r\n"
#ifndef CONFIG_NETUTILS_HTTPD_SERVERHEADER_DISABLE
"Server: uIP/NuttX http://nuttx.org/\r\n"
#endif
"Connection: %s\r\n"
"Content-type: %s\r\n"
"%s"
"\r\n",
status,
status >= 400 ? "Error" : "OK",
#ifndef CONFIG_NETUTILS_HTTPD_KEEPALIVE_DISABLE
pstate->ht_keepalive ? "keep-alive" : "close",
#else
"close",
#endif
mime,
contentlen
);
return send_chunk(pstate, header, hdrlen);
}
static int httpd_senderror(struct httpd_state *pstate, int status) static int httpd_senderror(struct httpd_state *pstate, int status)
{ {
int ret; int ret;
@ -611,7 +394,7 @@ static int httpd_senderror(struct httpd_state *pstate, int status)
ret = httpd_openindex(pstate); ret = httpd_openindex(pstate);
if (send_headers(pstate, status, if (httpd_send_headers(pstate, status,
ret == OK ? pstate->ht_file.len : sizeof msg - 1) != OK) ret == OK ? pstate->ht_file.len : sizeof msg - 1) != OK)
{ {
return ERROR; return ERROR;
@ -682,7 +465,7 @@ static int httpd_sendfile(struct httpd_state *pstate)
#ifndef CONFIG_NETUTILS_HTTPD_KEEPALIVE_DISABLE #ifndef CONFIG_NETUTILS_HTTPD_KEEPALIVE_DISABLE
pstate->ht_keepalive = false; pstate->ht_keepalive = false;
#endif #endif
if (send_headers(pstate, 200, -1) != OK) if (httpd_send_headers(pstate, 200, -1) != OK)
{ {
goto done; goto done;
} }
@ -693,13 +476,13 @@ static int httpd_sendfile(struct httpd_state *pstate)
#endif #endif
#ifdef CONFIG_NETUTILS_HTTPD_DIRLIST #ifdef CONFIG_NETUTILS_HTTPD_DIRLIST
if (send_headers(pstate, 200, -1) != OK) if (httpd_send_headers(pstate, 200, -1) != OK)
{ {
goto done; goto done;
} }
#else #else
if (send_headers(pstate, pstate->ht_file.len == 0 ? 204 : 200, if (httpd_send_headers(pstate, pstate->ht_file.len == 0 ? 204 : 200,
pstate->ht_file.len) != OK) pstate->ht_file.len) != OK)
{ {
goto done; goto done;
} }
@ -1039,13 +822,19 @@ static void single_server(uint16_t portno, pthread_startroutine_t handler,
* Public Functions * Public Functions
****************************************************************************/ ****************************************************************************/
/****************************************************************************
* Name: httpd_init
****************************************************************************/
void httpd_init(void)
{
#ifdef CONFIG_NETUTILS_HTTPD_CLASSIC
httpd_fs_init();
#endif
}
/**************************************************************************** /****************************************************************************
* Name: httpd_listen * Name: httpd_listen
*
* Description:
* This is the main processing thread for the webserver. It never returns
* unless an error occurs
*
****************************************************************************/ ****************************************************************************/
int httpd_listen(void) int httpd_listen(void)
@ -1064,17 +853,210 @@ int httpd_listen(void)
} }
/**************************************************************************** /****************************************************************************
* Name: httpd_init * Name: httpd_send_datachunk
*
* Description:
* This function initializes the web server and should be called at system
* boot-up.
*
****************************************************************************/ ****************************************************************************/
void httpd_init(void) int httpd_send_datachunk(int sockfd, void *data, int len, bool chunked)
{ {
#ifdef CONFIG_NETUTILS_HTTPD_CLASSIC int ret = 0;
httpd_fs_init(); #if defined(CONFIG_NETUTILS_HTTPD_ENABLE_CHUNKED_ENCODING)
char chunked_info[HTTPD_MAX_CHUNKEDLEN];
#endif #endif
#if defined(CONFIG_NETUTILS_HTTPD_ENABLE_CHUNKED_ENCODING)
/* Chunk prolog */
if (chunked)
{
int chunked_info_len = snprintf(chunked_info, HTTPD_MAX_CHUNKEDLEN,
"%X\r\n", len);
ret = send(sockfd, chunked_info, chunked_info_len, 0);
DEBUGASSERT(ret == chunked_info_len);
}
#endif
if (ret >= 0)
{
if (len == 0)
{
/* Lower layer does not tolerate buf = NULL even if len = 0
* so just pass a dummy pointer.
*/
data = &len;
}
ret = send(sockfd, data, len, 0);
DEBUGASSERT(ret == len);
}
#if defined(CONFIG_NETUTILS_HTTPD_ENABLE_CHUNKED_ENCODING)
/* Chunk epilog */
if (ret >= 0)
{
if (chunked)
{
ret = send(sockfd, "\r\n", 2, 0);
DEBUGASSERT(ret == 2);
}
}
#endif
return ret;
}
/****************************************************************************
* Name: httpd_send_headers
****************************************************************************/
int httpd_send_headers(struct httpd_state *pstate, int status, int len)
{
const char *mime;
const char *ptr;
char contentlen[HTTPD_MAX_CONTENTLEN] =
{
0
};
char header[HTTPD_MAX_HEADERLEN];
int hdrlen;
int i;
static const struct
{
const char *ext;
const char *mime;
}
a[] =
{
#ifndef CONFIG_NETUTILS_HTTPD_SCRIPT_DISABLE
{
"shtml", "text/html"
},
#endif
{
"html", "text/html"
},
{
"css", "text/css"
},
{
"txt", "text/plain"
},
{
"json", "application/json"
},
{
"js", "text/javascript"
},
{
"png", "image/png"
},
{
"gif", "image/gif"
},
{
"jpeg", "image/jpeg"
},
{
"jpg", "image/jpeg"
},
{
"mp3", "audio/mpeg"
},
};
ptr = strrchr(pstate->ht_filename, ISO_PERIOD);
if (ptr == NULL)
{
mime = "application/octet-stream";
}
else
{
mime = "text/plain";
for (i = 0; i < sizeof a / sizeof *a; i++)
{
if (strncmp(a[i].ext, ptr + 1, strlen(a[i].ext)) == 0)
{
mime = a[i].mime;
break;
}
}
}
#ifdef CONFIG_NETUTILS_HTTPD_DIRLIST
if (false == httpd_is_file(pstate->ht_filename))
{
/* we assume that it's a directory */
mime = "text/html";
}
#endif
if (len >= 0)
{
snprintf(contentlen, HTTPD_MAX_CONTENTLEN,
"Content-Length: %d\r\n", len);
}
else
{
#ifndef CONFIG_NETUTILS_HTTPD_KEEPALIVE_DISABLE
/* Length unknown ahead of time */
pstate->ht_keepalive = false;
#endif
#if defined(CONFIG_NETUTILS_HTTPD_ENABLE_CHUNKED_ENCODING)
/* Turn on chunked encoding */
snprintf(contentlen, HTTPD_MAX_CONTENTLEN,
"Transfer-Encoding: chunked\r\n");
pstate->ht_chunked = true;
#endif
}
if (status == 413)
{
/* TODO: here we "SHOULD" include a Retry-After header */
}
/* Construct the header.
*
* REVISIT: Wouldn't asprintf be a better option than a large stack
* array?
*/
hdrlen = snprintf(header, HTTPD_MAX_HEADERLEN,
"HTTP/1.0 %d %s\r\n"
#ifndef CONFIG_NETUTILS_HTTPD_SERVERHEADER_DISABLE
"Server: uIP/NuttX http://nuttx.org/\r\n"
#endif
"Connection: %s\r\n"
"Content-type: %s\r\n"
"%s"
"\r\n",
status,
status >= 400 ? "Error" : "OK",
#ifndef CONFIG_NETUTILS_HTTPD_KEEPALIVE_DISABLE
pstate->ht_keepalive ? "keep-alive" : "close",
#else
"close",
#endif
mime,
contentlen
);
return send_chunk(pstate, header, hdrlen);
} }

View File

@ -1,5 +1,5 @@
/**************************************************************************** /****************************************************************************
* httpd_cgi.c * apps/netutils/webserver/httpd_cgi.c
* Web server script interface * Web server script interface
* Author: Adam Dunkels <adam@sics.se> * Author: Adam Dunkels <adam@sics.se>
* *
@ -58,6 +58,10 @@ struct httpd_cgi_call *cgi_calls = NULL;
* Public Functions * Public Functions
****************************************************************************/ ****************************************************************************/
/****************************************************************************
* Name: httpd_cgi_register
****************************************************************************/
void httpd_cgi_register(struct httpd_cgi_call *cgi_call) void httpd_cgi_register(struct httpd_cgi_call *cgi_call)
{ {
if (cgi_calls == NULL) if (cgi_calls == NULL)
@ -71,6 +75,10 @@ void httpd_cgi_register(struct httpd_cgi_call *cgi_call)
} }
} }
/****************************************************************************
* Name: httpd_cgi
****************************************************************************/
httpd_cgifunction httpd_cgi(char *name) httpd_cgifunction httpd_cgi(char *name)
{ {
struct httpd_cgi_call *cgi_call = cgi_calls; struct httpd_cgi_call *cgi_call = cgi_calls;