Line data Source code
1 : /*
2 : *
3 : * Copyright 2015, Google Inc.
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions are
8 : * met:
9 : *
10 : * * Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : * * Redistributions in binary form must reproduce the above
13 : * copyright notice, this list of conditions and the following disclaimer
14 : * in the documentation and/or other materials provided with the
15 : * distribution.
16 : * * Neither the name of Google Inc. nor the names of its
17 : * contributors may be used to endorse or promote products derived from
18 : * this software without specific prior written permission.
19 : *
20 : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 : * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 : * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 : * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 : *
32 : */
33 :
34 : #include "src/core/httpcli/parser.h"
35 :
36 : #include <string.h>
37 :
38 : #include <grpc/support/alloc.h>
39 : #include <grpc/support/log.h>
40 : #include <grpc/support/useful.h>
41 :
42 2920 : static int handle_response_line(grpc_httpcli_parser *parser) {
43 2920 : gpr_uint8 *beg = parser->cur_line;
44 2920 : gpr_uint8 *cur = beg;
45 2920 : gpr_uint8 *end = beg + parser->cur_line_length;
46 :
47 2920 : if (cur == end || *cur++ != 'H') goto error;
48 2920 : if (cur == end || *cur++ != 'T') goto error;
49 2920 : if (cur == end || *cur++ != 'T') goto error;
50 2920 : if (cur == end || *cur++ != 'P') goto error;
51 2920 : if (cur == end || *cur++ != '/') goto error;
52 2920 : if (cur == end || *cur++ != '1') goto error;
53 2920 : if (cur == end || *cur++ != '.') goto error;
54 2920 : if (cur == end || *cur < '0' || *cur++ > '1') goto error;
55 2918 : if (cur == end || *cur++ != ' ') goto error;
56 2916 : if (cur == end || *cur < '1' || *cur++ > '9') goto error;
57 2914 : if (cur == end || *cur < '0' || *cur++ > '9') goto error;
58 2914 : if (cur == end || *cur < '0' || *cur++ > '9') goto error;
59 2914 : parser->r.status =
60 2914 : (cur[-3] - '0') * 100 + (cur[-2] - '0') * 10 + (cur[-1] - '0');
61 2914 : if (cur == end || *cur++ != ' ') goto error;
62 :
63 : /* we don't really care about the status code message */
64 :
65 2914 : return 1;
66 :
67 : error:
68 6 : gpr_log(GPR_ERROR, "Failed parsing response line");
69 6 : return 0;
70 : }
71 :
72 17432 : static char *buf2str(void *buffer, size_t length) {
73 17432 : char *out = gpr_malloc(length + 1);
74 17432 : memcpy(out, buffer, length);
75 17432 : out[length] = 0;
76 17432 : return out;
77 : }
78 :
79 8718 : static int add_header(grpc_httpcli_parser *parser) {
80 8718 : gpr_uint8 *beg = parser->cur_line;
81 8718 : gpr_uint8 *cur = beg;
82 8718 : gpr_uint8 *end = beg + parser->cur_line_length;
83 8718 : grpc_httpcli_header hdr = {NULL, NULL};
84 :
85 8718 : GPR_ASSERT(cur != end);
86 :
87 8718 : if (*cur == ' ' || *cur == '\t') {
88 0 : gpr_log(GPR_ERROR, "Continued header lines not supported yet");
89 0 : goto error;
90 : }
91 :
92 81350 : while (cur != end && *cur != ':') {
93 63914 : cur++;
94 : }
95 8718 : if (cur == end) {
96 2 : gpr_log(GPR_ERROR, "Didn't find ':' in header string");
97 2 : goto error;
98 : }
99 8716 : GPR_ASSERT(cur >= beg);
100 8716 : hdr.key = buf2str(beg, (size_t)(cur - beg));
101 8716 : cur++; /* skip : */
102 :
103 26148 : while (cur != end && (*cur == ' ' || *cur == '\t')) {
104 8716 : cur++;
105 : }
106 8716 : GPR_ASSERT(end - cur >= 2);
107 8716 : hdr.value = buf2str(cur, (size_t)(end - cur) - 2);
108 :
109 8716 : if (parser->r.hdr_count == parser->hdr_capacity) {
110 8716 : parser->hdr_capacity =
111 8716 : GPR_MAX(parser->hdr_capacity + 1, parser->hdr_capacity * 3 / 2);
112 17432 : parser->r.hdrs = gpr_realloc(
113 17432 : parser->r.hdrs, parser->hdr_capacity * sizeof(*parser->r.hdrs));
114 : }
115 8716 : parser->r.hdrs[parser->r.hdr_count++] = hdr;
116 8716 : return 1;
117 :
118 : error:
119 2 : gpr_free(hdr.key);
120 2 : gpr_free(hdr.value);
121 2 : return 0;
122 : }
123 :
124 14548 : static int finish_line(grpc_httpcli_parser *parser) {
125 14548 : switch (parser->state) {
126 : case GRPC_HTTPCLI_INITIAL_RESPONSE:
127 2920 : if (!handle_response_line(parser)) {
128 6 : return 0;
129 : }
130 2914 : parser->state = GRPC_HTTPCLI_HEADERS;
131 2914 : break;
132 : case GRPC_HTTPCLI_HEADERS:
133 11628 : if (parser->cur_line_length == 2) {
134 2910 : parser->state = GRPC_HTTPCLI_BODY;
135 2910 : break;
136 : }
137 8718 : if (!add_header(parser)) {
138 2 : return 0;
139 : }
140 8716 : break;
141 : case GRPC_HTTPCLI_BODY:
142 0 : GPR_UNREACHABLE_CODE(return 0);
143 : }
144 :
145 14540 : parser->cur_line_length = 0;
146 14540 : return 1;
147 : }
148 :
149 346150 : static int addbyte(grpc_httpcli_parser *parser, gpr_uint8 byte) {
150 346150 : switch (parser->state) {
151 : case GRPC_HTTPCLI_INITIAL_RESPONSE:
152 : case GRPC_HTTPCLI_HEADERS:
153 340124 : if (parser->cur_line_length >= GRPC_HTTPCLI_MAX_HEADER_LENGTH) {
154 0 : gpr_log(GPR_ERROR, "HTTP client max line length (%d) exceeded",
155 : GRPC_HTTPCLI_MAX_HEADER_LENGTH);
156 0 : return 0;
157 : }
158 340124 : parser->cur_line[parser->cur_line_length] = byte;
159 340124 : parser->cur_line_length++;
160 665698 : if (parser->cur_line_length >= 2 &&
161 340122 : parser->cur_line[parser->cur_line_length - 2] == '\r' &&
162 14548 : parser->cur_line[parser->cur_line_length - 1] == '\n') {
163 14548 : return finish_line(parser);
164 : } else {
165 325576 : return 1;
166 : }
167 : GPR_UNREACHABLE_CODE(return 0);
168 : case GRPC_HTTPCLI_BODY:
169 6026 : if (parser->r.body_length == parser->body_capacity) {
170 1473 : parser->body_capacity = GPR_MAX(8, parser->body_capacity * 3 / 2);
171 1473 : parser->r.body =
172 1473 : gpr_realloc((void *)parser->r.body, parser->body_capacity);
173 : }
174 6026 : parser->r.body[parser->r.body_length] = (char)byte;
175 6026 : parser->r.body_length++;
176 6026 : return 1;
177 : }
178 0 : GPR_UNREACHABLE_CODE(return 0);
179 : }
180 :
181 2922 : void grpc_httpcli_parser_init(grpc_httpcli_parser *parser) {
182 2922 : memset(parser, 0, sizeof(*parser));
183 2922 : parser->state = GRPC_HTTPCLI_INITIAL_RESPONSE;
184 2922 : parser->r.status = 500;
185 2922 : }
186 :
187 2922 : void grpc_httpcli_parser_destroy(grpc_httpcli_parser *parser) {
188 : size_t i;
189 2922 : gpr_free(parser->r.body);
190 11638 : for (i = 0; i < parser->r.hdr_count; i++) {
191 8716 : gpr_free(parser->r.hdrs[i].key);
192 8716 : gpr_free(parser->r.hdrs[i].value);
193 : }
194 2922 : gpr_free(parser->r.hdrs);
195 2922 : }
196 :
197 12327 : int grpc_httpcli_parser_parse(grpc_httpcli_parser *parser, gpr_slice slice) {
198 : size_t i;
199 :
200 358469 : for (i = 0; i < GPR_SLICE_LENGTH(slice); i++) {
201 346150 : if (!addbyte(parser, GPR_SLICE_START_PTR(slice)[i])) {
202 8 : return 0;
203 : }
204 : }
205 :
206 12319 : return 1;
207 : }
208 :
209 2914 : int grpc_httpcli_parser_eof(grpc_httpcli_parser *parser) {
210 2914 : return parser->state == GRPC_HTTPCLI_BODY;
211 : }
|