content-length api: unsigned long to lws_filepos_t
[platform/upstream/libwebsockets.git] / lib / header.c
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010-2013 Andy Green <andy@warmcat.com>
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation:
9  *  version 2.1 of the License.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *  MA  02110-1301  USA
20  */
21
22 #include "private-libwebsockets.h"
23
24 #include "lextable-strings.h"
25
26
27 const unsigned char *lws_token_to_string(enum lws_token_indexes token)
28 {
29         if ((unsigned int)token >= ARRAY_SIZE(set))
30                 return NULL;
31
32         return (unsigned char *)set[token];
33 }
34
35 int
36 lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name,
37                             const unsigned char *value, int length,
38                             unsigned char **p, unsigned char *end)
39 {
40 #ifdef LWS_USE_HTTP2
41         if (wsi->mode == LWSCM_HTTP2_SERVING)
42                 return lws_add_http2_header_by_name(wsi, name,
43                                                     value, length, p, end);
44 #else
45         (void)wsi;
46 #endif
47         if (name) {
48                 while (*p < end && *name)
49                         *((*p)++) = *name++;
50                 if (*p == end)
51                         return 1;
52                 *((*p)++) = ' ';
53         }
54         if (*p + length + 3 >= end)
55                 return 1;
56
57         memcpy(*p, value, length);
58         *p += length;
59         *((*p)++) = '\x0d';
60         *((*p)++) = '\x0a';
61
62         return 0;
63 }
64
65 int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
66                              unsigned char *end)
67 {
68 #ifdef LWS_USE_HTTP2
69         if (wsi->mode == LWSCM_HTTP2_SERVING)
70                 return 0;
71 #else
72         (void)wsi;
73 #endif
74         if ((long)(end - *p) < 3)
75                 return 1;
76         *((*p)++) = '\x0d';
77         *((*p)++) = '\x0a';
78
79         return 0;
80 }
81
82 int
83 lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
84                              const unsigned char *value, int length,
85                              unsigned char **p, unsigned char *end)
86 {
87         const unsigned char *name;
88 #ifdef LWS_USE_HTTP2
89         if (wsi->mode == LWSCM_HTTP2_SERVING)
90                 return lws_add_http2_header_by_token(wsi, token, value, length, p, end);
91 #endif
92         name = lws_token_to_string(token);
93         if (!name)
94                 return 1;
95         return lws_add_http_header_by_name(wsi, name, value, length, p, end);
96 }
97
98 int lws_add_http_header_content_length(struct lws *wsi,
99                                        lws_filepos_t content_length,
100                                        unsigned char **p, unsigned char *end)
101 {
102         char b[24];
103         int n;
104
105         n = sprintf(b, "%llu", (unsigned long long)content_length);
106         if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
107                                          (unsigned char *)b, n, p, end))
108                 return 1;
109         wsi->u.http.content_length = content_length;
110         wsi->u.http.content_remain = content_length;
111
112         return 0;
113 }
114
115 STORE_IN_ROM static const char * const err400[] = {
116         "Bad Request",
117         "Unauthorized",
118         "Payment Required",
119         "Forbidden",
120         "Not Found",
121         "Method Not Allowed",
122         "Not Acceptable",
123         "Proxy Auth Required",
124         "Request Timeout",
125         "Conflict",
126         "Gone",
127         "Length Required",
128         "Precondition Failed",
129         "Request Entity Too Large",
130         "Request URI too Long",
131         "Unsupported Media Type",
132         "Requested Range Not Satisfiable",
133         "Expectation Failed"
134 };
135
136 STORE_IN_ROM static const char * const err500[] = {
137         "Internal Server Error",
138         "Not Implemented",
139         "Bad Gateway",
140         "Service Unavailable",
141         "Gateway Timeout",
142         "HTTP Version Not Supported"
143 };
144
145 int
146 lws_add_http_header_status(struct lws *wsi, unsigned int _code,
147                            unsigned char **p, unsigned char *end)
148 {
149         STORE_IN_ROM static const char * const hver[] = {
150                 "HTTP/1.0", "HTTP/1.1", "HTTP/2"
151         };
152         const struct lws_protocol_vhost_options *headers;
153         unsigned int code = _code & LWSAHH_CODE_MASK;
154         const char *description = "", *p1;
155         unsigned char code_and_desc[60];
156         int n;
157
158 #ifdef LWS_WITH_ACCESS_LOG
159         wsi->access_log.response = code;
160 #endif
161
162 #ifdef LWS_USE_HTTP2
163         if (wsi->mode == LWSCM_HTTP2_SERVING)
164                 return lws_add_http2_header_status(wsi, code, p, end);
165 #endif
166         if (code >= 400 && code < (400 + ARRAY_SIZE(err400)))
167                 description = err400[code - 400];
168         if (code >= 500 && code < (500 + ARRAY_SIZE(err500)))
169                 description = err500[code - 500];
170
171         if (code == 200)
172                 description = "OK";
173
174         if (code == 304)
175                 description = "Not Modified";
176         else
177                 if (code >= 300 && code < 400)
178                         description = "Redirect";
179
180         if (wsi->u.http.request_version < ARRAY_SIZE(hver))
181                 p1 = hver[wsi->u.http.request_version];
182         else
183                 p1 = hver[0];
184
185         n = sprintf((char *)code_and_desc, "%s %u %s", p1, code, description);
186
187         if (lws_add_http_header_by_name(wsi, NULL, code_and_desc, n, p, end))
188                 return 1;
189
190         headers = wsi->vhost->headers;
191         while (headers) {
192                 if (lws_add_http_header_by_name(wsi,
193                                 (const unsigned char *)headers->name,
194                                 (unsigned char *)headers->value,
195                                 strlen(headers->value), p, end))
196                         return 1;
197
198                 headers = headers->next;
199         }
200
201         if (wsi->context->server_string &&
202             !(_code & LWSAHH_FLAG_NO_SERVER_NAME))
203                 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER,
204                                 (unsigned char *)wsi->context->server_string,
205                                 wsi->context->server_string_len, p, end))
206                         return 1;
207
208         if (wsi->vhost->options & LWS_SERVER_OPTION_STS)
209                 if (lws_add_http_header_by_name(wsi, (unsigned char *)
210                                 "Strict-Transport-Security:",
211                                 (unsigned char *)"max-age=15768000 ; "
212                                 "includeSubDomains", 36, p, end))
213                         return 1;
214
215         return 0;
216 }
217
218 LWS_VISIBLE int
219 lws_return_http_status(struct lws *wsi, unsigned int code,
220                        const char *html_body)
221 {
222         struct lws_context *context = lws_get_context(wsi);
223         struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
224         unsigned char *p = pt->serv_buf + LWS_PRE;
225         unsigned char *start = p, *body = p + 512;
226         unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
227         int n, m, len;
228         char slen[20];
229
230         if (!html_body)
231                 html_body = "";
232
233         len = sprintf((char *)body, "<html><body><h1>%u</h1>%s</body></html>",
234                       code, html_body);
235
236         if (lws_add_http_header_status(wsi, code, &p, end))
237                 return 1;
238
239         if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
240                                          (unsigned char *)"text/html", 9,
241                                          &p, end))
242                 return 1;
243         n = sprintf(slen, "%d", len);
244         if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
245                                          (unsigned char *)slen, n,
246                                          &p, end))
247                 return 1;
248
249         if (lws_finalize_http_header(wsi, &p, end))
250                 return 1;
251
252         m = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
253         if (m != (int)(p - start))
254                 return 1;
255
256         m = lws_write(wsi, body, len, LWS_WRITE_HTTP);
257
258         return m != n;
259 }
260
261 LWS_VISIBLE int
262 lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len,
263                   unsigned char **p, unsigned char *end)
264 {
265         unsigned char *start = *p;
266         int n;
267
268         if (lws_add_http_header_status(wsi, code, p, end))
269                 return -1;
270
271         if (lws_add_http_header_by_token(wsi,
272                         WSI_TOKEN_HTTP_LOCATION,
273                         loc, len, p, end))
274                 return -1;
275         /*
276          * if we're going with http/1.1 and keepalive,
277          * we have to give fake content metadata so the
278          * client knows we completed the transaction and
279          * it can do the redirect...
280          */
281         if (lws_add_http_header_by_token(wsi,
282                         WSI_TOKEN_HTTP_CONTENT_TYPE,
283                         (unsigned char *)"text/html", 9,
284                         p, end))
285                 return -1;
286         if (lws_add_http_header_by_token(wsi,
287                         WSI_TOKEN_HTTP_CONTENT_LENGTH,
288                         (unsigned char *)"0", 1, p, end))
289                 return -1;
290
291         if (lws_finalize_http_header(wsi, p, end))
292                 return -1;
293
294         n = lws_write(wsi, start, *p - start,
295                         LWS_WRITE_HTTP_HEADERS);
296
297         return n;
298 }