1 #include <stddef.h>
2 #include "gatling.h"
3 
4 #ifdef SUPPORT_PROXY
5 
httpstream_initstate(struct httpstreamstate * hss)6 void httpstream_initstate(struct httpstreamstate* hss) {
7   hss->state=HSS_HEADER;
8   hss->bytesleft=0;
9   hss->type=DONTKNOW;
10 }
11 
httpstream_update(struct httpstreamstate * hss,char c)12 int httpstream_update(struct httpstreamstate* hss,char c) {
13   switch (hss->state) {
14 
15   case HSS_HEADER_CRLF:
16     if (c=='\r') {
17       hss->state=HSS_HEADER_CRLFCR;
18       break;
19     }
20     goto new_header_line;
21     // else fall through
22 
23   case HSS_HEADER:
24     if (hss->type == DONTKNOW) {
25       if (c=='H') hss->type=RESPONSE_MAYBE;
26       else if (c=='P') hss->type=POSTREQUEST;
27       else hss->type=REQUEST;
28     }
29     else if (hss->type == RESPONSE_MAYBE) {
30       if (c=='T')
31 	hss->type=RESPONSE;	/* "HTTP/2.0 OK" */
32       else
33 	hss->type=REQUEST;	/* "HEAD /foo HTTP/1.1" */
34     }
35 new_header_line:
36     if ((c|0x20)=='c') hss->state=HSS_HEADER_C;
37     else if ((c|0x20)=='t') hss->state=HSS_HEADER_T;
38     else {
39 shared:
40       if (c=='\r') hss->state=HSS_HEADER_CR;
41       else hss->state=HSS_HEADER_OTHER;
42     }
43     break;
44 
45   case HSS_HEADER_C:
46     if ((c|0x20)=='o') { hss->state=HSS_HEADER_CO; break; }
47     else goto shared;
48 
49   case HSS_HEADER_CO:
50     if ((c|0x20)=='n') { hss->state=HSS_HEADER_CON; break; }
51     else goto shared;
52 
53   case HSS_HEADER_CON:
54     if ((c|0x20)=='t') { hss->state=HSS_HEADER_CONT; break; }
55     else goto shared;
56 
57   case HSS_HEADER_CONT:
58     if ((c|0x20)=='e') { hss->state=HSS_HEADER_CONTE; break; }
59     else goto shared;
60 
61   case HSS_HEADER_CONTE:
62     if ((c|0x20)=='n') { hss->state=HSS_HEADER_CONTEN; break; }
63     else goto shared;
64 
65   case HSS_HEADER_CONTEN:
66     if ((c|0x20)=='t') { hss->state=HSS_HEADER_CONTENT; break; }
67     else goto shared;
68 
69   case HSS_HEADER_CONTENT:
70     if (c=='-') { hss->state=HSS_HEADER_CONTENT_; break; }
71     else goto shared;
72 
73   case HSS_HEADER_CONTENT_:
74     if ((c|0x20)=='l') { hss->state=HSS_HEADER_CONTENT_L; break; }
75     else goto shared;
76 
77   case HSS_HEADER_CONTENT_L:
78     if ((c|0x20)=='e') { hss->state=HSS_HEADER_CONTENT_LE; break; }
79     else goto shared;
80 
81   case HSS_HEADER_CONTENT_LE:
82     if ((c|0x20)=='n') { hss->state=HSS_HEADER_CONTENT_LEN; break; }
83     else goto shared;
84 
85   case HSS_HEADER_CONTENT_LEN:
86     if ((c|0x20)=='g') { hss->state=HSS_HEADER_CONTENT_LENG; break; }
87     else goto shared;
88 
89   case HSS_HEADER_CONTENT_LENG:
90     if ((c|0x20)=='t') { hss->state=HSS_HEADER_CONTENT_LENGT; break; }
91     else goto shared;
92 
93   case HSS_HEADER_CONTENT_LENGT:
94     if ((c|0x20)=='h') { hss->state=HSS_HEADER_CONTENT_LENGTH; break; }
95     else goto shared;
96 
97   case HSS_HEADER_CONTENT_LENGTH:
98     if (c==':') { hss->state=HSS_HEADER_CONTENT_LENGTH_; break; }
99     else goto shared;
100 
101   case HSS_HEADER_CONTENT_LENGTH_:
102     if (c==' '||c=='\t') break;	// skip whitespace without changing state
103     if (c>='0' && c<='9') { hss->bytesleft=c-'0'; hss->state=HSS_HEADER_CONTENT_LENGTH_NUM; break; }
104     else goto shared;
105 
106   case HSS_HEADER_CONTENT_LENGTH_NUM:
107 //    printf("<bs=%ld, c=%c, bs=%ld>",hss->bytesleft,c,hss->bytesleft*10+c-'0');
108     if (c>='0' && c<='9') { hss->bytesleft=hss->bytesleft*10+c-'0'; break; }
109     else if (c!='\r') hss->bytesleft=UNKNOWN;
110     goto shared;
111 
112   case HSS_HEADER_T:
113     if ((c|0x20)=='r') { hss->state=HSS_HEADER_TR; break; }
114     else goto shared;
115 
116   case HSS_HEADER_TR:
117     if ((c|0x20)=='a') { hss->state=HSS_HEADER_TRA; break; }
118     else goto shared;
119 
120   case HSS_HEADER_TRA:
121     if ((c|0x20)=='n') { hss->state=HSS_HEADER_TRAN; break; }
122     else goto shared;
123 
124   case HSS_HEADER_TRAN:
125     if ((c|0x20)=='s') { hss->state=HSS_HEADER_TRANS; break; }
126     else goto shared;
127 
128   case HSS_HEADER_TRANS:
129     if ((c|0x20)=='f') { hss->state=HSS_HEADER_TRANSF; break; }
130     else goto shared;
131 
132   case HSS_HEADER_TRANSF:
133     if ((c|0x20)=='e') { hss->state=HSS_HEADER_TRANSFE; break; }
134     else goto shared;
135 
136   case HSS_HEADER_TRANSFE:
137     if ((c|0x20)=='r') { hss->state=HSS_HEADER_TRANSFER; break; }
138     else goto shared;
139 
140   case HSS_HEADER_TRANSFER:
141     if (c=='-') { hss->state=HSS_HEADER_TRANSFER_; break; }
142     else goto shared;
143 
144   case HSS_HEADER_TRANSFER_:
145     if ((c|0x20)=='e') { hss->state=HSS_HEADER_TRANSFER_E; break; }
146     else goto shared;
147 
148   case HSS_HEADER_TRANSFER_E:
149     if ((c|0x20)=='n') { hss->state=HSS_HEADER_TRANSFER_EN; break; }
150     else goto shared;
151 
152   case HSS_HEADER_TRANSFER_EN:
153     if ((c|0x20)=='c') { hss->state=HSS_HEADER_TRANSFER_ENC; break; }
154     else goto shared;
155 
156   case HSS_HEADER_TRANSFER_ENC:
157     if ((c|0x20)=='o') { hss->state=HSS_HEADER_TRANSFER_ENCO; break; }
158     else goto shared;
159 
160   case HSS_HEADER_TRANSFER_ENCO:
161     if ((c|0x20)=='d') { hss->state=HSS_HEADER_TRANSFER_ENCOD; break; }
162     else goto shared;
163 
164   case HSS_HEADER_TRANSFER_ENCOD:
165     if ((c|0x20)=='i') { hss->state=HSS_HEADER_TRANSFER_ENCODI; break; }
166     else goto shared;
167 
168   case HSS_HEADER_TRANSFER_ENCODI:
169     if ((c|0x20)=='n') { hss->state=HSS_HEADER_TRANSFER_ENCODIN; break; }
170     else goto shared;
171 
172   case HSS_HEADER_TRANSFER_ENCODIN:
173     if ((c|0x20)=='g') { hss->state=HSS_HEADER_TRANSFER_ENCODING; break; }
174     else goto shared;
175 
176   case HSS_HEADER_TRANSFER_ENCODING:
177     if (c==':') { hss->state=HSS_HEADER_TRANSFER_ENCODING_; break; }
178     else goto shared;
179 
180   case HSS_HEADER_TRANSFER_ENCODING_:
181     if (c==' ' || c=='\t') break;	/* skip whitespace */
182     if ((c|0x20)=='c') { hss->state=HSS_HEADER_TRANSFER_ENCODING_C; break; }
183     else goto shared;
184 
185   case HSS_HEADER_TRANSFER_ENCODING_C:
186     if ((c|0x20)=='h') { hss->state=HSS_HEADER_TRANSFER_ENCODING_CH; break; }
187     else goto shared;
188 
189   case HSS_HEADER_TRANSFER_ENCODING_CH:
190     if ((c|0x20)=='u') { hss->state=HSS_HEADER_TRANSFER_ENCODING_CHU; break; }
191     else goto shared;
192 
193   case HSS_HEADER_TRANSFER_ENCODING_CHU:
194     if ((c|0x20)=='n') { hss->state=HSS_HEADER_TRANSFER_ENCODING_CHUN; break; }
195     else goto shared;
196 
197   case HSS_HEADER_TRANSFER_ENCODING_CHUN:
198     if ((c|0x20)=='k') { hss->state=HSS_HEADER_TRANSFER_ENCODING_CHUNK; break; }
199     else goto shared;
200 
201   case HSS_HEADER_TRANSFER_ENCODING_CHUNK:
202     if ((c|0x20)=='e') { hss->state=HSS_HEADER_TRANSFER_ENCODING_CHUNKE; break; }
203     else goto shared;
204 
205   case HSS_HEADER_TRANSFER_ENCODING_CHUNKE:
206     if ((c|0x20)=='d') { hss->state=HSS_HEADER_TRANSFER_ENCODING_CHUNKED; break; }
207     else goto shared;
208 
209   case HSS_HEADER_TRANSFER_ENCODING_CHUNKED:
210     hss->state=HSS_HEADER_OTHER;
211     if (c==' ' || c=='\t' || c==';' || c=='\r' || c=='\n')
212       hss->bytesleft=CHUNKED;
213     goto shared;
214 
215   case HSS_HEADER_OTHER:
216     goto shared;
217 
218   case HSS_HEADER_CR:
219     if (c=='\n') { hss->state=HSS_HEADER_CRLF; break; }
220     else goto shared;
221 
222   case HSS_HEADER_CRLFCR:
223     if (c=='\n') {
224       // end of header found.
225       if (hss->type==REQUEST) {
226 	// if it's a GET request, ignore content-length and chunked
227 	// encoding.
228 	hss->bytesleft=0;
229 	hss->state=HSS_DONE;
230 	return 1;
231       }
232       // Now we either have found a content-length or the chunked
233       // encoding marker or neither.
234       if (hss->bytesleft==CHUNKED) {
235 	// chunked encoding; read chunked header next.
236 	hss->state=HSS_HEADER_CHUNKED_CRLF;
237       } else if (hss->bytesleft==UNKNOWN || hss->bytesleft==0) {
238 	// neither chunked encoding nor content length
239 	// only way to end the stream is to drop the connection
240 	hss->state=HSS_INFINITE;
241       } else {
242 	// found positive content length
243 	hss->state=HSS_KNOWLENGTH;
244       }
245       break;
246     }
247     else goto shared;
248 
249   case HSS_KNOWLENGTH:
250     if (--hss->bytesleft==0) {
251       hss->state=HSS_DONE;
252       return 1;
253     }
254     break;
255 
256   case HSS_HEADER_CHUNKED:
257     if (c=='\r')
258       hss->state=HSS_HEADER_CHUNKED_CR;
259     break;
260 
261   case HSS_HEADER_CHUNKED_CR:
262     if (c=='\n')
263       hss->state=HSS_HEADER_CHUNKED_CRLF;
264     break;
265 
266   case HSS_HEADER_CHUNKED_CRLF:
267     hss->bytesleft=0;
268     // fall through
269   case HSS_HEADER_CHUNKED_CRLF_NUM:
270     if (c>='0' && c<='9')
271       hss->bytesleft=hss->bytesleft*10+c-'0';
272     else if (c>='a' && c<='f')
273       hss->bytesleft=hss->bytesleft*10+c-'a'+10;
274     else if (c>='A' && c<='F')
275       hss->bytesleft=hss->bytesleft*10+c-'A'+10;
276     else if (c=='\r' && hss->state==HSS_HEADER_CHUNKED_CRLF_NUM) {
277       hss->state=HSS_HEADER_CHUNKED_CRLF_NUM_CR;
278       break;
279     }
280 //    printf("[bl=%d]",hss->bytesleft);
281     hss->state=HSS_HEADER_CHUNKED_CRLF_NUM;
282     break;
283 
284   case HSS_HEADER_CHUNKED_CRLF_NUM_CR:
285     if (c=='\n') {
286       if (hss->bytesleft==0) {
287 	hss->state=HSS_DONE;
288 	return 1;
289       }
290       hss->state=HSS_KNOWLENGTH_CHUNKED;
291     }
292     break;
293 
294   case HSS_KNOWLENGTH_CHUNKED:
295     if (--hss->bytesleft==0)
296       /* chunk is done; read next one */
297       hss->state=HSS_HEADER_CHUNKED;
298     break;
299 
300   case HSS_INFINITE:
301     break;
302 
303   case HSS_DONE:
304     return 1;
305   }
306   return 0;
307 }
308 
httpstream(struct httpstreamstate * hss,const char * buf,size_t len)309 size_t httpstream(struct httpstreamstate* hss,
310 		  const char* buf, size_t len) {
311   // buf is part of a http request or reply
312   // this is meant to be called from a proxy loop that keeps reading
313   // data from a socket and writing it to another socket.  For each
314   // chunk of data that is coming in, the code calls this function on
315   // each chunk.  This function then parses the chunk and says how many
316   // bytes in it belong to the http stream.  Basically, it will return
317   // len until the stream is finished (request + POST data received or
318   // reply + content data received).
319 
320   size_t i;
321   if (hss->state==HSS_DONE)
322     return 0;
323   for (i=0; i<len; ++i)
324     if (httpstream_update(hss,buf[i]))
325       return i+1;
326   return len;
327 }
328 
329 #if NEED_MAIN
330 #include <stdio.h>
331 
main()332 int main() {
333   struct httpstreamstate hss;
334 //  char buf[]="POST / HTTP/1.0\r\nHost: localhost:80\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nfnord\r\n0\r\nhalali";
335   char buf[10000];
336   size_t i,l;
337   FILE* f;
338   f=fopen("/tmp/a/blubber","r");
339   l=fread(buf,1,sizeof(buf),f);
340   httpstream_initstate(&hss);
341 
342   for (i=0; i<l; ++i) {
343     printf("%c",buf[i]);
344     if (httpstream_update(&hss,buf[i]))
345       break;
346     printf("[%d,bl=%ld]",hss.state,hss.bytesleft);
347   }
348 
349   httpstream_initstate(&hss);
350   printf("%zu\n",httpstream(&hss,buf,l));
351 
352   return 0;
353 }
354 #endif
355 
356 #endif
357