1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2014 Tatsuhiro Tsujikawa
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 #include "nghttp2_buf.h"
26 
27 #include <stdio.h>
28 
29 #include "nghttp2_helper.h"
30 #include "nghttp2_debug.h"
31 
nghttp2_buf_init(nghttp2_buf * buf)32 void nghttp2_buf_init(nghttp2_buf *buf)
33 {
34     buf->begin = NULL;
35     buf->end = NULL;
36     buf->pos = NULL;
37     buf->last = NULL;
38     buf->mark = NULL;
39 }
40 
nghttp2_buf_init2(nghttp2_buf * buf,size_t initial,nghttp2_mem * mem)41 int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem)
42 {
43     nghttp2_buf_init(buf);
44     return nghttp2_buf_reserve(buf, initial, mem);
45 }
46 
nghttp2_buf_free(nghttp2_buf * buf,nghttp2_mem * mem)47 void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem)
48 {
49     if (buf == NULL) {
50         return;
51     }
52 
53     nghttp2_mem_free(mem, buf->begin);
54     buf->begin = NULL;
55 }
56 
nghttp2_buf_reserve(nghttp2_buf * buf,size_t new_cap,nghttp2_mem * mem)57 int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem)
58 {
59     uint8_t *ptr;
60     size_t cap;
61 
62     cap = nghttp2_buf_cap(buf);
63 
64     if (cap >= new_cap) {
65         return 0;
66     }
67 
68     new_cap = nghttp2_max(new_cap, cap * 2);
69 
70     ptr = nghttp2_mem_realloc(mem, buf->begin, new_cap);
71     if (ptr == NULL) {
72         return NGHTTP2_ERR_NOMEM;
73     }
74 
75     buf->pos = ptr + (buf->pos - buf->begin);
76     buf->last = ptr + (buf->last - buf->begin);
77     buf->mark = ptr + (buf->mark - buf->begin);
78     buf->begin = ptr;
79     buf->end = ptr + new_cap;
80 
81     return 0;
82 }
83 
nghttp2_buf_reset(nghttp2_buf * buf)84 void nghttp2_buf_reset(nghttp2_buf *buf)
85 {
86     buf->pos = buf->last = buf->mark = buf->begin;
87 }
88 
nghttp2_buf_wrap_init(nghttp2_buf * buf,uint8_t * begin,size_t len)89 void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len)
90 {
91     buf->begin = buf->pos = buf->last = buf->mark = begin;
92     buf->end = begin + len;
93 }
94 
buf_chain_new(nghttp2_buf_chain ** chain,size_t chunk_length,nghttp2_mem * mem)95 static int buf_chain_new(nghttp2_buf_chain **chain, size_t chunk_length,
96                          nghttp2_mem *mem)
97 {
98     int rv;
99 
100     *chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain));
101     if (*chain == NULL) {
102         return NGHTTP2_ERR_NOMEM;
103     }
104 
105     (*chain)->next = NULL;
106 
107     rv = nghttp2_buf_init2(&(*chain)->buf, chunk_length, mem);
108     if (rv != 0) {
109         nghttp2_mem_free(mem, *chain);
110         return NGHTTP2_ERR_NOMEM;
111     }
112 
113     return 0;
114 }
115 
buf_chain_del(nghttp2_buf_chain * chain,nghttp2_mem * mem)116 static void buf_chain_del(nghttp2_buf_chain *chain, nghttp2_mem *mem)
117 {
118     nghttp2_buf_free(&chain->buf, mem);
119     nghttp2_mem_free(mem, chain);
120 }
121 
nghttp2_bufs_init(nghttp2_bufs * bufs,size_t chunk_length,size_t max_chunk,nghttp2_mem * mem)122 int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk,
123                       nghttp2_mem *mem)
124 {
125     return nghttp2_bufs_init2(bufs, chunk_length, max_chunk, 0, mem);
126 }
127 
nghttp2_bufs_init2(nghttp2_bufs * bufs,size_t chunk_length,size_t max_chunk,size_t offset,nghttp2_mem * mem)128 int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length,
129                        size_t max_chunk, size_t offset, nghttp2_mem *mem)
130 {
131     return nghttp2_bufs_init3(bufs, chunk_length, max_chunk, max_chunk, offset,
132                               mem);
133 }
134 
nghttp2_bufs_init3(nghttp2_bufs * bufs,size_t chunk_length,size_t max_chunk,size_t chunk_keep,size_t offset,nghttp2_mem * mem)135 int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length,
136                        size_t max_chunk, size_t chunk_keep, size_t offset,
137                        nghttp2_mem *mem)
138 {
139     int rv;
140     nghttp2_buf_chain *chain;
141 
142     if (chunk_keep == 0 || max_chunk < chunk_keep || chunk_length < offset) {
143         return NGHTTP2_ERR_INVALID_ARGUMENT;
144     }
145 
146     rv = buf_chain_new(&chain, chunk_length, mem);
147     if (rv != 0) {
148         return rv;
149     }
150 
151     bufs->mem = mem;
152     bufs->offset = offset;
153 
154     bufs->head = chain;
155     bufs->cur = bufs->head;
156 
157     nghttp2_buf_shift_right(&bufs->cur->buf, offset);
158 
159     bufs->chunk_length = chunk_length;
160     bufs->chunk_used = 1;
161     bufs->max_chunk = max_chunk;
162     bufs->chunk_keep = chunk_keep;
163 
164     return 0;
165 }
166 
nghttp2_bufs_realloc(nghttp2_bufs * bufs,size_t chunk_length)167 int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length)
168 {
169     int rv;
170     nghttp2_buf_chain *chain;
171 
172     if (chunk_length < bufs->offset) {
173         return NGHTTP2_ERR_INVALID_ARGUMENT;
174     }
175 
176     rv = buf_chain_new(&chain, chunk_length, bufs->mem);
177     if (rv != 0) {
178         return rv;
179     }
180 
181     nghttp2_bufs_free(bufs);
182 
183     bufs->head = chain;
184     bufs->cur = bufs->head;
185 
186     nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset);
187 
188     bufs->chunk_length = chunk_length;
189     bufs->chunk_used = 1;
190 
191     return 0;
192 }
193 
nghttp2_bufs_free(nghttp2_bufs * bufs)194 void nghttp2_bufs_free(nghttp2_bufs *bufs)
195 {
196     nghttp2_buf_chain *chain, *next_chain;
197 
198     if (bufs == NULL) {
199         return;
200     }
201 
202     for (chain = bufs->head; chain;) {
203         next_chain = chain->next;
204 
205         buf_chain_del(chain, bufs->mem);
206 
207         chain = next_chain;
208     }
209 
210     bufs->head = NULL;
211 }
212 
nghttp2_bufs_wrap_init(nghttp2_bufs * bufs,uint8_t * begin,size_t len,nghttp2_mem * mem)213 int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len,
214                            nghttp2_mem *mem)
215 {
216     nghttp2_buf_chain *chain;
217 
218     chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain));
219     if (chain == NULL) {
220         return NGHTTP2_ERR_NOMEM;
221     }
222 
223     chain->next = NULL;
224 
225     nghttp2_buf_wrap_init(&chain->buf, begin, len);
226 
227     bufs->mem = mem;
228     bufs->offset = 0;
229 
230     bufs->head = chain;
231     bufs->cur = bufs->head;
232 
233     bufs->chunk_length = len;
234     bufs->chunk_used = 1;
235     bufs->max_chunk = 1;
236     bufs->chunk_keep = 1;
237 
238     return 0;
239 }
240 
nghttp2_bufs_wrap_init2(nghttp2_bufs * bufs,const nghttp2_vec * vec,size_t veclen,nghttp2_mem * mem)241 int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, const nghttp2_vec *vec,
242                             size_t veclen, nghttp2_mem *mem)
243 {
244     size_t i = 0;
245     nghttp2_buf_chain *cur_chain;
246     nghttp2_buf_chain *head_chain;
247     nghttp2_buf_chain **dst_chain = &head_chain;
248 
249     if (veclen == 0) {
250         return nghttp2_bufs_wrap_init(bufs, NULL, 0, mem);
251     }
252 
253     head_chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain) * veclen);
254     if (head_chain == NULL) {
255         return NGHTTP2_ERR_NOMEM;
256     }
257 
258     for (i = 0; i < veclen; ++i) {
259         cur_chain = &head_chain[i];
260         cur_chain->next = NULL;
261         nghttp2_buf_wrap_init(&cur_chain->buf, vec[i].base, vec[i].len);
262 
263         *dst_chain = cur_chain;
264         dst_chain = &cur_chain->next;
265     }
266 
267     bufs->mem = mem;
268     bufs->offset = 0;
269 
270     bufs->head = head_chain;
271     bufs->cur = bufs->head;
272 
273     /* We don't use chunk_length since no allocation is expected. */
274     bufs->chunk_length = 0;
275     bufs->chunk_used = veclen;
276     bufs->max_chunk = veclen;
277     bufs->chunk_keep = veclen;
278 
279     return 0;
280 }
281 
nghttp2_bufs_wrap_free(nghttp2_bufs * bufs)282 void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs)
283 {
284     if (bufs == NULL) {
285         return;
286     }
287 
288     if (bufs->head) {
289         nghttp2_mem_free(bufs->mem, bufs->head);
290     }
291 }
292 
nghttp2_bufs_seek_last_present(nghttp2_bufs * bufs)293 void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs)
294 {
295     nghttp2_buf_chain *ci;
296 
297     for (ci = bufs->cur; ci; ci = ci->next) {
298         if (nghttp2_buf_len(&ci->buf) == 0) {
299             return;
300         } else {
301             bufs->cur = ci;
302         }
303     }
304 }
305 
nghttp2_bufs_len(nghttp2_bufs * bufs)306 size_t nghttp2_bufs_len(nghttp2_bufs *bufs)
307 {
308     nghttp2_buf_chain *ci;
309     size_t len;
310 
311     len = 0;
312     for (ci = bufs->head; ci; ci = ci->next) {
313         len += nghttp2_buf_len(&ci->buf);
314     }
315 
316     return len;
317 }
318 
bufs_alloc_chain(nghttp2_bufs * bufs)319 static int bufs_alloc_chain(nghttp2_bufs *bufs)
320 {
321     int rv;
322     nghttp2_buf_chain *chain;
323 
324     if (bufs->cur->next) {
325         bufs->cur = bufs->cur->next;
326 
327         return 0;
328     }
329 
330     if (bufs->max_chunk == bufs->chunk_used) {
331         return NGHTTP2_ERR_BUFFER_ERROR;
332     }
333 
334     rv = buf_chain_new(&chain, bufs->chunk_length, bufs->mem);
335     if (rv != 0) {
336         return rv;
337     }
338 
339     DEBUGF("new buffer %zu bytes allocated for bufs %p, used %zu\n",
340            bufs->chunk_length, bufs, bufs->chunk_used);
341 
342     ++bufs->chunk_used;
343 
344     bufs->cur->next = chain;
345     bufs->cur = chain;
346 
347     nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset);
348 
349     return 0;
350 }
351 
nghttp2_bufs_add(nghttp2_bufs * bufs,const void * data,size_t len)352 int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len)
353 {
354     int rv;
355     size_t nwrite;
356     nghttp2_buf *buf;
357     const uint8_t *p;
358 
359     p = data;
360 
361     while (len) {
362         buf = &bufs->cur->buf;
363 
364         nwrite = nghttp2_min(nghttp2_buf_avail(buf), len);
365         if (nwrite == 0) {
366             rv = bufs_alloc_chain(bufs);
367             if (rv != 0) {
368                 return rv;
369             }
370             continue;
371         }
372 
373         buf->last = nghttp2_cpymem(buf->last, p, nwrite);
374         p += nwrite;
375         len -= nwrite;
376     }
377 
378     return 0;
379 }
380 
bufs_ensure_addb(nghttp2_bufs * bufs)381 static int bufs_ensure_addb(nghttp2_bufs *bufs)
382 {
383     int rv;
384     nghttp2_buf *buf;
385 
386     buf = &bufs->cur->buf;
387 
388     if (nghttp2_buf_avail(buf) > 0) {
389         return 0;
390     }
391 
392     rv = bufs_alloc_chain(bufs);
393     if (rv != 0) {
394         return rv;
395     }
396 
397     return 0;
398 }
399 
nghttp2_bufs_addb(nghttp2_bufs * bufs,uint8_t b)400 int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b)
401 {
402     int rv;
403 
404     rv = bufs_ensure_addb(bufs);
405     if (rv != 0) {
406         return rv;
407     }
408 
409     *bufs->cur->buf.last++ = b;
410 
411     return 0;
412 }
413 
nghttp2_bufs_addb_hold(nghttp2_bufs * bufs,uint8_t b)414 int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b)
415 {
416     int rv;
417 
418     rv = bufs_ensure_addb(bufs);
419     if (rv != 0) {
420         return rv;
421     }
422 
423     *bufs->cur->buf.last = b;
424 
425     return 0;
426 }
427 
nghttp2_bufs_orb(nghttp2_bufs * bufs,uint8_t b)428 int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b)
429 {
430     int rv;
431 
432     rv = bufs_ensure_addb(bufs);
433     if (rv != 0) {
434         return rv;
435     }
436 
437     *bufs->cur->buf.last++ |= b;
438 
439     return 0;
440 }
441 
nghttp2_bufs_orb_hold(nghttp2_bufs * bufs,uint8_t b)442 int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b)
443 {
444     int rv;
445 
446     rv = bufs_ensure_addb(bufs);
447     if (rv != 0) {
448         return rv;
449     }
450 
451     *bufs->cur->buf.last |= b;
452 
453     return 0;
454 }
455 
nghttp2_bufs_remove(nghttp2_bufs * bufs,uint8_t ** out)456 ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out)
457 {
458     size_t len;
459     nghttp2_buf_chain *chain;
460     nghttp2_buf *buf;
461     uint8_t *res;
462     nghttp2_buf resbuf;
463 
464     len = 0;
465 
466     for (chain = bufs->head; chain; chain = chain->next) {
467         len += nghttp2_buf_len(&chain->buf);
468     }
469 
470     if (len == 0) {
471         res = NULL;
472         return 0;
473     }
474 
475     res = nghttp2_mem_malloc(bufs->mem, len);
476     if (res == NULL) {
477         return NGHTTP2_ERR_NOMEM;
478     }
479 
480     nghttp2_buf_wrap_init(&resbuf, res, len);
481 
482     for (chain = bufs->head; chain; chain = chain->next) {
483         buf = &chain->buf;
484         resbuf.last =
485             nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf));
486     }
487 
488     *out = res;
489 
490     return (ssize_t)len;
491 }
492 
nghttp2_bufs_remove_copy(nghttp2_bufs * bufs,uint8_t * out)493 size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out)
494 {
495     size_t len;
496     nghttp2_buf_chain *chain;
497     nghttp2_buf *buf;
498     nghttp2_buf resbuf;
499 
500     len = nghttp2_bufs_len(bufs);
501 
502     nghttp2_buf_wrap_init(&resbuf, out, len);
503 
504     for (chain = bufs->head; chain; chain = chain->next) {
505         buf = &chain->buf;
506         resbuf.last =
507             nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf));
508     }
509 
510     return len;
511 }
512 
nghttp2_bufs_reset(nghttp2_bufs * bufs)513 void nghttp2_bufs_reset(nghttp2_bufs *bufs)
514 {
515     nghttp2_buf_chain *chain, *ci;
516     size_t k;
517 
518     k = bufs->chunk_keep;
519 
520     for (ci = bufs->head; ci; ci = ci->next) {
521         nghttp2_buf_reset(&ci->buf);
522         nghttp2_buf_shift_right(&ci->buf, bufs->offset);
523 
524         if (--k == 0) {
525             break;
526         }
527     }
528 
529     if (ci) {
530         chain = ci->next;
531         ci->next = NULL;
532 
533         for (ci = chain; ci;) {
534             chain = ci->next;
535 
536             buf_chain_del(ci, bufs->mem);
537 
538             ci = chain;
539         }
540 
541         bufs->chunk_used = bufs->chunk_keep;
542     }
543 
544     bufs->cur = bufs->head;
545 }
546 
nghttp2_bufs_advance(nghttp2_bufs * bufs)547 int nghttp2_bufs_advance(nghttp2_bufs *bufs)
548 {
549     return bufs_alloc_chain(bufs);
550 }
551 
nghttp2_bufs_next_present(nghttp2_bufs * bufs)552 int nghttp2_bufs_next_present(nghttp2_bufs *bufs)
553 {
554     nghttp2_buf_chain *chain;
555 
556     chain = bufs->cur->next;
557 
558     return chain && nghttp2_buf_len(&chain->buf);
559 }
560