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 4356 : static int handle_response_line(grpc_httpcli_parser *parser) {
43 4356 : gpr_uint8 *beg = parser->cur_line;
44 4356 : gpr_uint8 *cur = beg;
45 4356 : gpr_uint8 *end = beg + parser->cur_line_length;
46 :
47 4356 : if (cur == end || *cur++ != 'H') goto error;
48 4356 : if (cur == end || *cur++ != 'T') goto error;
49 4356 : if (cur == end || *cur++ != 'T') goto error;
50 4356 : if (cur == end || *cur++ != 'P') goto error;
51 4356 : if (cur == end || *cur++ != '/') goto error;
52 4356 : if (cur == end || *cur++ != '1') goto error;
53 4356 : if (cur == end || *cur++ != '.') goto error;
54 4356 : if (cur == end || *cur < '0' || *cur++ > '1') goto error;
55 4354 : if (cur == end || *cur++ != ' ') goto error;
56 4352 : if (cur == end || *cur < '1' || *cur++ > '9') goto error;
57 4350 : if (cur == end || *cur < '0' || *cur++ > '9') goto error;
58 4350 : if (cur == end || *cur < '0' || *cur++ > '9') goto error;
59 4350 : parser->r.status =
60 4350 : (cur[-3] - '0') * 100 + (cur[-2] - '0') * 10 + (cur[-1] - '0');
61 4350 : if (cur == end || *cur++ != ' ') goto error;
62 :
63 : /* we don't really care about the status code message */
64 :
65 4350 : return 1;
66 :
67 : error:
68 6 : gpr_log(GPR_ERROR, "Failed parsing response line");
69 6 : return 0;
70 : }
71 :
72 26048 : static char *buf2str(void *buffer, size_t length) {
73 26048 : char *out = gpr_malloc(length + 1);
74 26048 : memcpy(out, buffer, length);
75 26048 : out[length] = 0;
76 26048 : return out;
77 : }
78 :
79 13026 : static int add_header(grpc_httpcli_parser *parser) {
80 13026 : gpr_uint8 *beg = parser->cur_line;
81 13026 : gpr_uint8 *cur = beg;
82 13026 : gpr_uint8 *end = beg + parser->cur_line_length;
83 13026 : grpc_httpcli_header hdr = {NULL, NULL};
84 :
85 13026 : GPR_ASSERT(cur != end);
86 :
87 13026 : if (*cur == ' ' || *cur == '\t') {
88 0 : gpr_log(GPR_ERROR, "Continued header lines not supported yet");
89 0 : goto error;
90 : }
91 :
92 121558 : while (cur != end && *cur != ':') {
93 95506 : cur++;
94 : }
95 13026 : if (cur == end) {
96 2 : gpr_log(GPR_ERROR, "Didn't find ':' in header string");
97 2 : goto error;
98 : }
99 13024 : GPR_ASSERT(cur >= beg);
100 13024 : hdr.key = buf2str(beg, (size_t)(cur - beg));
101 13024 : cur++; /* skip : */
102 :
103 39072 : while (cur != end && (*cur == ' ' || *cur == '\t')) {
104 13024 : cur++;
105 : }
106 13024 : GPR_ASSERT(end - cur >= 2);
107 13024 : hdr.value = buf2str(cur, (size_t)(end - cur) - 2);
108 :
109 13024 : if (parser->r.hdr_count == parser->hdr_capacity) {
110 13024 : parser->hdr_capacity =
111 13024 : GPR_MAX(parser->hdr_capacity + 1, parser->hdr_capacity * 3 / 2);
112 26048 : parser->r.hdrs = gpr_realloc(
113 26048 : parser->r.hdrs, parser->hdr_capacity * sizeof(*parser->r.hdrs));
114 : }
115 13024 : parser->r.hdrs[parser->r.hdr_count++] = hdr;
116 13024 : return 1;
117 :
118 : error:
119 2 : gpr_free(hdr.key);
120 2 : gpr_free(hdr.value);
121 2 : return 0;
122 : }
123 :
124 21728 : static int finish_line(grpc_httpcli_parser *parser) {
125 21728 : switch (parser->state) {
126 : case GRPC_HTTPCLI_INITIAL_RESPONSE:
127 4356 : if (!handle_response_line(parser)) {
128 6 : return 0;
129 : }
130 4350 : parser->state = GRPC_HTTPCLI_HEADERS;
131 4350 : break;
132 : case GRPC_HTTPCLI_HEADERS:
133 17372 : if (parser->cur_line_length == 2) {
134 4346 : parser->state = GRPC_HTTPCLI_BODY;
135 4346 : break;
136 : }
137 13026 : if (!add_header(parser)) {
138 2 : return 0;
139 : }
140 13024 : break;
141 : case GRPC_HTTPCLI_BODY:
142 0 : GPR_UNREACHABLE_CODE(return 0);
143 : }
144 :
145 21720 : parser->cur_line_length = 0;
146 21720 : return 1;
147 : }
148 :
149 517202 : static int addbyte(grpc_httpcli_parser *parser, gpr_uint8 byte) {
150 517202 : switch (parser->state) {
151 : case GRPC_HTTPCLI_INITIAL_RESPONSE:
152 : case GRPC_HTTPCLI_HEADERS:
153 508134 : 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 508134 : parser->cur_line[parser->cur_line_length] = byte;
159 508134 : parser->cur_line_length++;
160 994538 : if (parser->cur_line_length >= 2 &&
161 508132 : parser->cur_line[parser->cur_line_length - 2] == '\r' &&
162 21728 : parser->cur_line[parser->cur_line_length - 1] == '\n') {
163 21728 : return finish_line(parser);
164 : } else {
165 486406 : return 1;
166 : }
167 : GPR_UNREACHABLE_CODE(return 0);
168 : case GRPC_HTTPCLI_BODY:
169 9068 : if (parser->r.body_length == parser->body_capacity) {
170 2204 : parser->body_capacity = GPR_MAX(8, parser->body_capacity * 3 / 2);
171 2204 : parser->r.body =
172 2204 : gpr_realloc((void *)parser->r.body, parser->body_capacity);
173 : }
174 9068 : parser->r.body[parser->r.body_length] = (char)byte;
175 9068 : parser->r.body_length++;
176 9068 : return 1;
177 : }
178 0 : GPR_UNREACHABLE_CODE(return 0);
179 0 : }
180 :
181 4358 : void grpc_httpcli_parser_init(grpc_httpcli_parser *parser) {
182 4358 : memset(parser, 0, sizeof(*parser));
183 4358 : parser->state = GRPC_HTTPCLI_INITIAL_RESPONSE;
184 4358 : parser->r.status = 500;
185 4358 : }
186 :
187 4358 : void grpc_httpcli_parser_destroy(grpc_httpcli_parser *parser) {
188 : size_t i;
189 4358 : gpr_free(parser->r.body);
190 17382 : for (i = 0; i < parser->r.hdr_count; i++) {
191 13024 : gpr_free(parser->r.hdrs[i].key);
192 13024 : gpr_free(parser->r.hdrs[i].value);
193 : }
194 4358 : gpr_free(parser->r.hdrs);
195 4358 : }
196 :
197 11970 : int grpc_httpcli_parser_parse(grpc_httpcli_parser *parser, gpr_slice slice) {
198 : size_t i;
199 :
200 529164 : for (i = 0; i < GPR_SLICE_LENGTH(slice); i++) {
201 517202 : if (!addbyte(parser, GPR_SLICE_START_PTR(slice)[i])) {
202 8 : return 0;
203 : }
204 : }
205 :
206 11962 : return 1;
207 : }
208 :
209 4350 : int grpc_httpcli_parser_eof(grpc_httpcli_parser *parser) {
210 4350 : return parser->state == GRPC_HTTPCLI_BODY;
211 : }
|