diff --git a/lib/header.c b/lib/header.c new file mode 100644 index 00000000..8d83f5cb --- /dev/null +++ b/lib/header.c @@ -0,0 +1,209 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2013 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" +#include "lextable-strings.h" + +const unsigned char *lws_token_to_string(enum lws_token_indexes token) +{ + if ((unsigned int)token >= ARRAY_SIZE(set)) + return NULL; + return (unsigned char *)set[token]; +} + +int lws_add_http_header_by_name(struct libwebsocket_context *context, + struct libwebsocket *wsi, + const unsigned char *name, + const unsigned char *value, + int length, + unsigned char **p, + unsigned char *end) +{ +#ifdef LWS_USE_HTTP2 + if (wsi->mode == LWS_CONNMODE_HTTP2_SERVING) + return lws_add_http2_header_by_name(context, wsi, name, value, length, p, end); +#endif + if (name) { + while (*p < end && *name) + *((*p)++) = *name++; + if (*p == end) + return 1; + *((*p)++) = ' '; + } + if (*p + length + 3 >= end) + return 1; + + memcpy(*p, value, length); + *p += length; + *((*p)++) = '\x0d'; + *((*p)++) = '\x0a'; + return 0; +} + +int lws_finalize_http_header(struct libwebsocket_context *context, + struct libwebsocket *wsi, + unsigned char **p, + unsigned char *end) +{ +#ifdef LWS_USE_HTTP2 + if (wsi->mode == LWS_CONNMODE_HTTP2_SERVING) + return 0; +#endif + if ((long)(end - *p) < 3) + return 1; + *((*p)++) = '\x0d'; + *((*p)++) = '\x0a'; + return 0; +} + +int lws_add_http_header_by_token(struct libwebsocket_context *context, + struct libwebsocket *wsi, + enum lws_token_indexes token, + const unsigned char *value, + int length, + unsigned char **p, + unsigned char *end) +{ + const unsigned char *name; +#ifdef LWS_USE_HTTP2 + if (wsi->mode == LWS_CONNMODE_HTTP2_SERVING) + return lws_add_http2_header_by_token(context, wsi, token, value, length, p, end); +#endif + name = lws_token_to_string(token); + if (!name) + return 1; + return lws_add_http_header_by_name(context, wsi, name, value, length, p, end); +} + +int lws_add_http_header_content_length(struct libwebsocket_context *context, + struct libwebsocket *wsi, + unsigned long content_length, + unsigned char **p, + unsigned char *end) +{ + char b[24]; + int n; + + n = sprintf(b, "%lu", content_length); + if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH, (unsigned char *)b, n, p, end)) + return 1; + wsi->u.http.content_length = content_length; + wsi->u.http.content_remain = content_length; + + return 0; +} + +static const char *err400[] = { + "Bad Request", + "Unauthorized", + "Payment Required", + "Forbidden", + "Not Found", + "Method Not Allowed", + "Not Acceptable", + "Proxy Auth Required", + "Request Timeout", + "Conflict", + "Gone", + "Length Required", + "Precondition Failed", + "Request Entity Too Large", + "Request URI too Long", + "Unsupported Media Type", + "Requested Range Not Satisfiable", + "Expectation Failed" +}; + +static const char *err500[] = { + "Internal Server Error", + "Not Implemented", + "Bad Gateway", + "Service Unavailable", + "Gateway Timeout", + "HTTP Version Not Supported" +}; + +int lws_add_http_header_status(struct libwebsocket_context *context, + struct libwebsocket *wsi, + unsigned int code, + unsigned char **p, + unsigned char *end) +{ + unsigned char code_and_desc[60]; + const char *description = ""; + int n; + +#ifdef LWS_USE_HTTP2 + if (wsi->mode == LWS_CONNMODE_HTTP2_SERVING) + return lws_add_http2_header_status(context, wsi, code, p, end); +#endif + if (code >= 400 && code < (400 + ARRAY_SIZE(err400))) + description = err400[code - 400]; + if (code >= 500 && code < (500 + ARRAY_SIZE(err500))) + description = err500[code - 500]; + + n = sprintf((char *)code_and_desc, "HTTP/1.0 %u %s", code, description); + + return lws_add_http_header_by_name(context, wsi, NULL, code_and_desc, n, p, end); +} + +/** + * libwebsockets_return_http_status() - Return simple http status + * @context: libwebsockets context + * @wsi: Websocket instance (available from user callback) + * @code: Status index, eg, 404 + * @html_body: User-readable HTML description < 1KB, or NULL + * + * Helper to report HTTP errors back to the client cleanly and + * consistently + */ +LWS_VISIBLE int libwebsockets_return_http_status( + struct libwebsocket_context *context, struct libwebsocket *wsi, + unsigned int code, const char *html_body) +{ + int n, m; + + unsigned char *p = context->service_buffer + LWS_SEND_BUFFER_PRE_PADDING; + unsigned char *start = p; + unsigned char *end = p + sizeof(context->service_buffer) - + LWS_SEND_BUFFER_PRE_PADDING; + + if (!html_body) + html_body = ""; + + if (lws_add_http_header_status(context, wsi, code, &p, end)) + return 1; + if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_SERVER, (unsigned char *)"libwebsockets", 13, &p, end)) + return 1; + if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char *)"text/html", 9, &p, end)) + return 1; + if (lws_finalize_http_header(context, wsi, &p, end)) + return 1; + + m = libwebsocket_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS); + if (m != (int)(p - start)) + return 1; + + n = sprintf((char *)start, "

%u

%s", code, html_body); + m = libwebsocket_write(wsi, start, n, LWS_WRITE_HTTP); + + return m != n; +}