| /* |
| * libwebsockets - small server side websockets and web server implementation |
| * |
| * Copyright (C) 2010-2013 Andy Green <andy@warmcat.com> |
| * |
| * 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 lws *wsi, const unsigned char *name, |
| const unsigned char *value, int length, |
| unsigned char **p, unsigned char *end) |
| { |
| #ifdef LWS_USE_HTTP2 |
| if (wsi->mode == LWSCM_HTTP2_SERVING) |
| return lws_add_http2_header_by_name(wsi, name, |
| value, length, p, end); |
| #else |
| (void)wsi; |
| #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 lws *wsi, unsigned char **p, |
| unsigned char *end) |
| { |
| #ifdef LWS_USE_HTTP2 |
| if (wsi->mode == LWSCM_HTTP2_SERVING) |
| return 0; |
| #else |
| (void)wsi; |
| #endif |
| if ((lws_intptr_t)(end - *p) < 3) |
| return 1; |
| *((*p)++) = '\x0d'; |
| *((*p)++) = '\x0a'; |
| |
| return 0; |
| } |
| |
| int |
| lws_add_http_header_by_token(struct lws *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 == LWSCM_HTTP2_SERVING) |
| return lws_add_http2_header_by_token(wsi, token, value, length, p, end); |
| #endif |
| name = lws_token_to_string(token); |
| if (!name) |
| return 1; |
| return lws_add_http_header_by_name(wsi, name, value, length, p, end); |
| } |
| |
| int lws_add_http_header_content_length(struct lws *wsi, |
| lws_filepos_t content_length, |
| unsigned char **p, unsigned char *end) |
| { |
| char b[24]; |
| int n; |
| |
| n = sprintf(b, "%llu", (unsigned long long)content_length); |
| if (lws_add_http_header_by_token(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; |
| } |
| |
| STORE_IN_ROM static const char * const 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" |
| }; |
| |
| STORE_IN_ROM static const char * const err500[] = { |
| "Internal Server Error", |
| "Not Implemented", |
| "Bad Gateway", |
| "Service Unavailable", |
| "Gateway Timeout", |
| "HTTP Version Not Supported" |
| }; |
| |
| int |
| lws_add_http_header_status(struct lws *wsi, unsigned int _code, |
| unsigned char **p, unsigned char *end) |
| { |
| STORE_IN_ROM static const char * const hver[] = { |
| "HTTP/1.0", "HTTP/1.1", "HTTP/2" |
| }; |
| const struct lws_protocol_vhost_options *headers; |
| unsigned int code = _code & LWSAHH_CODE_MASK; |
| const char *description = "", *p1; |
| unsigned char code_and_desc[60]; |
| int n; |
| |
| #ifdef LWS_WITH_ACCESS_LOG |
| wsi->access_log.response = code; |
| #endif |
| |
| #ifdef LWS_USE_HTTP2 |
| if (wsi->mode == LWSCM_HTTP2_SERVING) |
| return lws_add_http2_header_status(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]; |
| |
| if (code == 200) |
| description = "OK"; |
| |
| if (code == 304) |
| description = "Not Modified"; |
| else |
| if (code >= 300 && code < 400) |
| description = "Redirect"; |
| |
| if (wsi->u.http.request_version < ARRAY_SIZE(hver)) |
| p1 = hver[wsi->u.http.request_version]; |
| else |
| p1 = hver[0]; |
| |
| n = sprintf((char *)code_and_desc, "%s %u %s", p1, code, description); |
| |
| if (lws_add_http_header_by_name(wsi, NULL, code_and_desc, n, p, end)) |
| return 1; |
| |
| headers = wsi->vhost->headers; |
| while (headers) { |
| if (lws_add_http_header_by_name(wsi, |
| (const unsigned char *)headers->name, |
| (unsigned char *)headers->value, |
| strlen(headers->value), p, end)) |
| return 1; |
| |
| headers = headers->next; |
| } |
| |
| if (wsi->context->server_string && |
| !(_code & LWSAHH_FLAG_NO_SERVER_NAME)) |
| if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER, |
| (unsigned char *)wsi->context->server_string, |
| wsi->context->server_string_len, p, end)) |
| return 1; |
| |
| if (wsi->vhost->options & LWS_SERVER_OPTION_STS) |
| if (lws_add_http_header_by_name(wsi, (unsigned char *) |
| "Strict-Transport-Security:", |
| (unsigned char *)"max-age=15768000 ; " |
| "includeSubDomains", 36, p, end)) |
| return 1; |
| |
| return 0; |
| } |
| |
| LWS_VISIBLE int |
| lws_return_http_status(struct lws *wsi, unsigned int code, |
| const char *html_body) |
| { |
| struct lws_context *context = lws_get_context(wsi); |
| struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; |
| unsigned char *p = pt->serv_buf + LWS_PRE; |
| unsigned char *start = p; |
| unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE; |
| int n = 0, m, len; |
| char slen[20]; |
| |
| if (!html_body) |
| html_body = ""; |
| |
| if (lws_add_http_header_status(wsi, code, &p, end)) |
| return 1; |
| |
| if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, |
| (unsigned char *)"text/html", 9, |
| &p, end)) |
| return 1; |
| |
| len = 35 + strlen(html_body) + sprintf(slen, "%d", code); |
| n = sprintf(slen, "%d", len); |
| |
| if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH, |
| (unsigned char *)slen, n, |
| &p, end)) |
| return 1; |
| |
| if (lws_finalize_http_header(wsi, &p, end)) |
| return 1; |
| |
| #if defined(LWS_USE_HTTP2) |
| { |
| unsigned char *body = p + 512; |
| |
| m = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS); |
| if (m != (int)(p - start)) |
| return 1; |
| |
| len = sprintf((char *)body, "<html><body><h1>%u</h1>%s</body></html>", |
| code, html_body); |
| |
| n = len; |
| m = lws_write(wsi, body, len, LWS_WRITE_HTTP); |
| } |
| #else |
| p += lws_snprintf((char *)p, end - p - 1, |
| "<html><body><h1>%u</h1>%s</body></html>", |
| code, html_body); |
| |
| n = (int)(p - start); |
| m = lws_write(wsi, start, n, LWS_WRITE_HTTP); |
| if (m != n) |
| return 1; |
| #endif |
| |
| return m != n; |
| } |
| |
| LWS_VISIBLE int |
| lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len, |
| unsigned char **p, unsigned char *end) |
| { |
| unsigned char *start = *p; |
| int n; |
| |
| if (lws_add_http_header_status(wsi, code, p, end)) |
| return -1; |
| |
| if (lws_add_http_header_by_token(wsi, |
| WSI_TOKEN_HTTP_LOCATION, |
| loc, len, p, end)) |
| return -1; |
| /* |
| * if we're going with http/1.1 and keepalive, |
| * we have to give fake content metadata so the |
| * client knows we completed the transaction and |
| * it can do the redirect... |
| */ |
| if (lws_add_http_header_by_token(wsi, |
| WSI_TOKEN_HTTP_CONTENT_TYPE, |
| (unsigned char *)"text/html", 9, |
| p, end)) |
| return -1; |
| if (lws_add_http_header_by_token(wsi, |
| WSI_TOKEN_HTTP_CONTENT_LENGTH, |
| (unsigned char *)"0", 1, p, end)) |
| return -1; |
| |
| if (lws_finalize_http_header(wsi, p, end)) |
| return -1; |
| |
| n = lws_write(wsi, start, *p - start, |
| LWS_WRITE_HTTP_HEADERS); |
| |
| return n; |
| } |