private.h: rename to contain dir
[platform/upstream/libwebsockets.git] / lib / roles / http / compression / stream.c
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010 - 2018 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-lib-core.h"
23
24 /* compression methods listed in order of preference */
25
26 struct lws_compression_support *lcs_available[] = {
27 #if defined(LWS_WITH_HTTP_BROTLI)
28         &lcs_brotli,
29 #endif
30         &lcs_deflate,
31 };
32
33 /* compute acceptable compression encodings while we still have an ah */
34
35 int
36 lws_http_compression_validate(struct lws *wsi)
37 {
38         const char *a;
39         size_t n;
40
41         wsi->http.comp_accept_mask = 0;
42
43         if (!wsi->http.ah || !lwsi_role_server(wsi))
44                 return 0;
45
46         a = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING);
47         if (!a)
48                 return 0;
49
50         for (n = 0; n < LWS_ARRAY_SIZE(lcs_available); n++)
51                 if (strstr(a, lcs_available[n]->encoding_name))
52                         wsi->http.comp_accept_mask |= 1 << n;
53
54         return 0;
55 }
56
57 LWS_VISIBLE int
58 lws_http_compression_apply(struct lws *wsi, const char *name,
59                            unsigned char **p, unsigned char *end, char decomp)
60 {
61         size_t n;
62
63         for (n = 0; n < LWS_ARRAY_SIZE(lcs_available); n++) {
64                 /* if name is non-NULL, choose only that compression method */
65                 if (name && !strcmp(lcs_available[n]->encoding_name, name))
66                         continue;
67                 /*
68                  * If we're the server, confirm that the client told us he could
69                  * handle this kind of compression transform...
70                  */
71                 if (!decomp && !(wsi->http.comp_accept_mask & (1 << n)))
72                         continue;
73
74                 /* let's go with this one then... */
75                 break;
76         }
77
78         if (n == LWS_ARRAY_SIZE(lcs_available))
79                 return 1;
80
81         lcs_available[n]->init_compression(&wsi->http.comp_ctx, decomp);
82         if (!wsi->http.comp_ctx.u.generic_ctx_ptr) {
83                 lwsl_err("%s: init_compression %d failed\n", __func__, (int)n);
84                 return 1;
85         }
86
87         wsi->http.lcs = lcs_available[n];
88         wsi->http.comp_ctx.may_have_more = 0;
89         wsi->http.comp_ctx.final_on_input_side = 0;
90         wsi->http.comp_ctx.chunking = 0;
91         wsi->http.comp_ctx.is_decompression = decomp;
92
93         if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING,
94                         (unsigned char *)lcs_available[n]->encoding_name,
95                         strlen(lcs_available[n]->encoding_name), p, end))
96                 return -1;
97
98         lwsl_info("%s: wsi %p: applied %s content-encoding\n", __func__,
99                     wsi, lcs_available[n]->encoding_name);
100
101         return 0;
102 }
103
104 void
105 lws_http_compression_destroy(struct lws *wsi)
106 {
107         if (!wsi->http.lcs || !wsi->http.comp_ctx.u.generic_ctx_ptr)
108                 return;
109
110         wsi->http.lcs->destroy(&wsi->http.comp_ctx);
111
112         wsi->http.lcs = NULL;
113 }
114
115 /*
116  * This manages the compression transform independent of h1 or h2.
117  *
118  * wsi->buflist_comp stashes pre-transform input that was not yet compressed
119  */
120
121 int
122 lws_http_compression_transform(struct lws *wsi, unsigned char *buf,
123                                size_t len, enum lws_write_protocol *wp,
124                                unsigned char **outbuf, size_t *olen_oused)
125 {
126         size_t ilen_iused = len;
127         int n, use = 0, wp1f = (*wp) & 0x1f;
128         lws_comp_ctx_t *ctx = &wsi->http.comp_ctx;
129
130         ctx->may_have_more = 0;
131
132         if (!wsi->http.lcs ||
133             (wp1f != LWS_WRITE_HTTP && wp1f != LWS_WRITE_HTTP_FINAL)) {
134                 *outbuf = buf;
135                 *olen_oused = len;
136
137                 return 0;
138         }
139
140         if (wp1f == LWS_WRITE_HTTP_FINAL) {
141                 /*
142                  * ...we may get a large buffer that represents the final input
143                  * buffer, but it may form multiple frames after being
144                  * tranformed by compression; only the last of those is actually
145                  * the final frame on the output stream.
146                  *
147                  * Note that we have received the FINAL input, and downgrade it
148                  * to a non-final for now.
149                  */
150                 ctx->final_on_input_side = 1;
151                 *wp = LWS_WRITE_HTTP | ((*wp) & ~0x1f);
152         }
153
154         if (ctx->buflist_comp || ctx->may_have_more) {
155                 /*
156                  * we can't send this new stuff when we have old stuff
157                  * buffered and not compressed yet.  Add it to the tail
158                  * and switch to trying to process the head.
159                  */
160                 if (buf && len) {
161                         if (lws_buflist_append_segment(
162                                         &ctx->buflist_comp, buf, len) < 0)
163                                 return -1;
164                         lwsl_debug("%s: %p: adding %d to comp buflist\n",
165                                    __func__,wsi, (int)len);
166                 }
167
168                 len = lws_buflist_next_segment_len(&ctx->buflist_comp, &buf);
169                 ilen_iused = len;
170                 use = 1;
171                 lwsl_debug("%s: %p: trying comp buflist %d\n", __func__, wsi,
172                            (int)len);
173         }
174
175         if (!buf && ilen_iused)
176                 return 0;
177
178         lwsl_debug("%s: %p: pre-process: ilen_iused %d, olen_oused %d\n",
179                    __func__, wsi, (int)ilen_iused, (int)*olen_oused);
180
181         n = wsi->http.lcs->process(ctx, buf, &ilen_iused, *outbuf, olen_oused);
182
183         if (n && n != 1) {
184                 lwsl_err("%s: problem with compression\n", __func__);
185
186                 return -1;
187         }
188
189         if (!ctx->may_have_more && ctx->final_on_input_side)
190                 *wp = LWS_WRITE_HTTP_FINAL | ((*wp) & ~0x1f);
191
192         lwsl_debug("%s: %p: more %d, ilen_iused %d\n", __func__, wsi,
193                    ctx->may_have_more, (int)ilen_iused);
194
195         if (use && ilen_iused) {
196                 /*
197                  * we were flushing stuff from the buflist head... account for
198                  * however much actually got processed by the compression
199                  * transform
200                  */
201                 lws_buflist_use_segment(&ctx->buflist_comp, ilen_iused);
202                 lwsl_debug("%s: %p: marking %d of comp buflist as used "
203                            "(ctx->buflist_comp %p)\n", __func__, wsi,
204                            (int)len, ctx->buflist_comp);
205         }
206
207         if (!use && ilen_iused != len) {
208                  /*
209                   * ...we were sending stuff from the caller directly and not
210                   * all of it got processed... stash on the buflist tail
211                   */
212                 if (lws_buflist_append_segment(&ctx->buflist_comp,
213                                            buf + ilen_iused, len - ilen_iused) < 0)
214                         return -1;
215
216                 lwsl_debug("%s: buffering %d unused comp input\n", __func__,
217                            (int)(len - ilen_iused));
218         }
219         if (ctx->buflist_comp || ctx->may_have_more)
220                 lws_callback_on_writable(wsi);
221
222         return 0;
223 }