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
****************************************************************************/
/****************************************************************************
* Name: httpd_init
*
* Description:
* This function initializes the web server and should be called at system
* boot-up.
*
****************************************************************************/
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);
/****************************************************************************
* Name: httpd_cgi_register
*
* Description:
* Register a CGI handler
*
****************************************************************************/
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);
/****************************************************************************
* 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
bool httpd_is_file(FAR const char *filename);
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
*
* 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
}
/****************************************************************************
* 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
static void httpd_dumpbuffer(FAR const char *msg, FAR const char *buffer,
unsigned int nbytes)
@ -440,153 +370,6 @@ static int send_chunk(struct httpd_state *pstate, const char *buf, int len)
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)
{
int ret;
@ -611,7 +394,7 @@ static int httpd_senderror(struct httpd_state *pstate, int status)
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)
{
return ERROR;
@ -682,7 +465,7 @@ static int httpd_sendfile(struct httpd_state *pstate)
#ifndef CONFIG_NETUTILS_HTTPD_KEEPALIVE_DISABLE
pstate->ht_keepalive = false;
#endif
if (send_headers(pstate, 200, -1) != OK)
if (httpd_send_headers(pstate, 200, -1) != OK)
{
goto done;
}
@ -693,13 +476,13 @@ static int httpd_sendfile(struct httpd_state *pstate)
#endif
#ifdef CONFIG_NETUTILS_HTTPD_DIRLIST
if (send_headers(pstate, 200, -1) != OK)
if (httpd_send_headers(pstate, 200, -1) != OK)
{
goto done;
}
#else
if (send_headers(pstate, pstate->ht_file.len == 0 ? 204 : 200,
pstate->ht_file.len) != OK)
if (httpd_send_headers(pstate, pstate->ht_file.len == 0 ? 204 : 200,
pstate->ht_file.len) != OK)
{
goto done;
}
@ -1039,13 +822,19 @@ static void single_server(uint16_t portno, pthread_startroutine_t handler,
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: httpd_init
****************************************************************************/
void httpd_init(void)
{
#ifdef CONFIG_NETUTILS_HTTPD_CLASSIC
httpd_fs_init();
#endif
}
/****************************************************************************
* Name: httpd_listen
*
* Description:
* This is the main processing thread for the webserver. It never returns
* unless an error occurs
*
****************************************************************************/
int httpd_listen(void)
@ -1064,17 +853,210 @@ int httpd_listen(void)
}
/****************************************************************************
* Name: httpd_init
*
* Description:
* This function initializes the web server and should be called at system
* boot-up.
*
* Name: httpd_send_datachunk
****************************************************************************/
void httpd_init(void)
int httpd_send_datachunk(int sockfd, void *data, int len, bool chunked)
{
#ifdef CONFIG_NETUTILS_HTTPD_CLASSIC
httpd_fs_init();
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;
}
/****************************************************************************
* 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
* Author: Adam Dunkels <adam@sics.se>
*
@ -58,6 +58,10 @@ struct httpd_cgi_call *cgi_calls = NULL;
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: httpd_cgi_register
****************************************************************************/
void httpd_cgi_register(struct httpd_cgi_call *cgi_call)
{
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)
{
struct httpd_cgi_call *cgi_call = cgi_calls;