blob: 05e8cd57865ef2b0d65b50593b47556b4082b23c [file] [log] [blame]
Tim Duesterhusdbd25c32021-04-15 21:45:55 +02001/*
2 * HTTP request URI normalization.
3 *
4 * Copyright 2021 Tim Duesterhus <tim@bastelstu.be>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
Tim Duesterhusd371e992021-04-15 21:45:58 +020013#include <import/ist.h>
14
Tim Duesterhusdbd25c32021-04-15 21:45:55 +020015#include <haproxy/api.h>
16#include <haproxy/uri_normalizer.h>
17
Tim Duesterhus9982fc22021-04-15 21:45:59 +020018/* Merges `/../` with preceding path segments. */
19enum uri_normalizer_err uri_normalizer_path_dotdot(const struct ist path, struct ist *dst)
20{
21 enum uri_normalizer_err err;
22
23 const size_t size = istclear(dst);
24 char * const tail = istptr(*dst) + size;
25 char *head = tail;
26
27 ssize_t offset = istlen(path) - 1;
28
29 int up = 0;
30
31 /* The path will either be shortened or have the same length. */
32 if (size < istlen(path)) {
33 err = URI_NORMALIZER_ERR_ALLOC;
34 goto fail;
35 }
36
37 /* Handle `/..` at the end of the path without a trailing slash. */
38 if (offset >= 2 && istmatch(istadv(path, offset - 2), ist("/.."))) {
39 up++;
40 offset -= 2;
41 }
42
43 while (offset >= 0) {
44 if (offset >= 3 && istmatch(istadv(path, offset - 3), ist("/../"))) {
45 up++;
46 offset -= 3;
47 continue;
48 }
49
50 if (up > 0) {
51 /* Skip the slash. */
52 offset--;
53
54 /* First check whether we already reached the start of the path,
55 * before popping the current `/../`.
56 */
57 if (offset >= 0) {
58 up--;
59
60 /* Skip the current path segment. */
61 while (offset >= 0 && istptr(path)[offset] != '/')
62 offset--;
63 }
64 }
65 else {
66 /* Prepend the slash. */
67 *(--head) = istptr(path)[offset];
68 offset--;
69
70 /* Prepend the current path segment. */
71 while (offset >= 0 && istptr(path)[offset] != '/') {
72 *(--head) = istptr(path)[offset];
73 offset--;
74 }
75 }
76 }
77
78 if (up > 0) {
79 /* Prepend a trailing slash. */
80 *(--head) = '/';
81
82 /* Prepend unconsumed `/..`. */
83 do {
84 *(--head) = '.';
85 *(--head) = '.';
86 *(--head) = '/';
87 up--;
88 } while (up > 0);
89 }
90
91 *dst = ist2(head, tail - head);
92
93 return URI_NORMALIZER_ERR_NONE;
94
95 fail:
96
97 return err;
98}
99
Tim Duesterhusd371e992021-04-15 21:45:58 +0200100/* Merges adjacent slashes in the given path. */
101enum uri_normalizer_err uri_normalizer_path_merge_slashes(const struct ist path, struct ist *dst)
102{
103 enum uri_normalizer_err err;
104
105 const size_t size = istclear(dst);
106 struct ist newpath = *dst;
107
108 struct ist scanner = path;
109
110 /* The path will either be shortened or have the same length. */
111 if (size < istlen(path)) {
112 err = URI_NORMALIZER_ERR_ALLOC;
113 goto fail;
114 }
115
116 while (istlen(scanner) > 0) {
117 const char current = istshift(&scanner);
118
119 if (current == '/') {
120 while (istlen(scanner) > 0 && *istptr(scanner) == '/')
121 scanner = istnext(scanner);
122 }
123
124 newpath = __istappend(newpath, current);
125 }
126
127 *dst = newpath;
128
129 return URI_NORMALIZER_ERR_NONE;
130
131 fail:
132
133 return err;
134}
135
136
Tim Duesterhusdbd25c32021-04-15 21:45:55 +0200137/*
138 * Local variables:
139 * c-indent-level: 8
140 * c-basic-offset: 8
141 * End:
142 */