Klaus Demo bjoern / 8a7cf64
Handle "Expect: 100-continue" with automatic continue (#162) Tom Brennan 3 months ago
6 changed file(s) with 92 addition(s) and 2 deletion(s). Raw diff Collapse all Expand all
144144 /* Ignore invalid header */
145145 if(PARSER->invalid_header) {
146146 return 0;
147 }
148
149 /* If `Expect` is encountered, set request->state to notify that the client
150 * expects an "HTTP/1.1 100 Continue" response before it will send the body */
151 if (!strncmp(field, HTTP_EXPECT, len)) {
152 if (parser->http_major > 0 && parser->http_minor > 0) {
153 REQUEST->state.expect_continue = true;
154 }
147155 }
148156
149157 char field_processed[len];
1515 unsigned keep_alive : 1;
1616 unsigned response_length_unknown : 1;
1717 unsigned chunked_response : 1;
18 unsigned expect_continue : 1;
1819 } request_state;
1920
2021 typedef struct {
4849 #define REQUEST_FROM_WATCHER(watcher) \
4950 (Request*)((size_t)watcher - (size_t)(&(((Request*)NULL)->ev_watcher)));
5051
52 #define HTTP_EXPECT "Expect"
53 #define CONTINUE_RESPONSE "HTTP/1.1 100 Continue\r\n\r\n"
54
5155 Request* Request_new(ServerInfo*, int client_fd, const char* client_addr);
5256 void Request_parse(Request*, const char*, const size_t);
5357 void Request_reset(Request*);
223223 request->current_chunk = _PEP3333_Bytes_FromString(
224224 http_error_messages[HTTP_SERVER_ERROR]);
225225 }
226 } else if (request->state.expect_continue) {
227 read_state = done;
228 request->current_chunk = _PEP3333_Bytes_FromString(
229 CONTINUE_RESPONSE);
230 request->state.keep_alive = true;
226231 } else {
227232 /* Wait for more data */
228233 read_state = not_yet_done;
284289 if(request->state.keep_alive) {
285290 DBG_REQ(request, "done, keep-alive");
286291 ev_io_stop(mainloop, &request->ev_watcher);
287 Request_clean(request);
288 Request_reset(request);
292
293 if (!request->state.expect_continue) {
294 /* There will be more to receive/send after sending 100-Continue, so
295 * don't clobber the request until after the "real" response. */
296 Request_clean(request);
297 Request_reset(request);
298 }
299
300 request->state.expect_continue = false;
301
289302 ev_io_init(&request->ev_watcher, &ev_io_on_read,
290303 request->client_fd, EV_READ);
291304 ev_io_start(mainloop, &request->ev_watcher);
5555 listen_backlog=listen_backlog)
5656 _default_instance = (sock, wsgi_app)
5757
58 return sock
59
5860
5961 def run(*args, **kwargs):
6062 """
0 requests
0 from __future__ import print_function
1 import bjoern, socket, threading, time, json, sys, requests
2
3
4 responses = 0
5
6
7 def app(e, s):
8 s('200 OK', [("Content-Length", "14")])
9 return b"hello response"
10
11
12 def make_request(host, port, reports, i):
13 global responses
14
15 content_length = 1 << i
16 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
17
18 client.connect((host, port))
19 request = ("GET /{} HTTP/1.1\r\n"
20 "Accept: */*\r\n"
21 "Expect: 100-continue\r\n"
22 "Content-Length: {}\r\n\r\n").format(i, content_length)
23
24 client.send(request.encode("utf-8"))
25 print("Request for iteration {}".format(i))
26 resp = client.recv(1 << 10)
27
28 if resp == b"HTTP/1.1 100 Continue\r\n\r\n":
29 client.send("".join("x" for x in range(0, content_length)).encode("utf-8"))
30 resp = client.recv(1 << 10)
31 reports.append({"request": request, "response": resp.decode("utf-8")})
32
33 client.close()
34
35 print("Response for iteration {}".format(i))
36 responses = responses + 1
37
38
39 if __name__ == "__main__":
40 host, port = "0.0.0.0", 8081
41 sock = bjoern.listen(app, host, port, reuse_port=True)
42
43 t = threading.Thread(target=bjoern.server_run, args=[sock, app])
44 t.setDaemon(True)
45 t.start()
46
47 reports = []
48 iterations = int(sys.argv[1]) if len(sys.argv) > 1 else 1
49
50 for i in range(0, iterations):
51 t = threading.Thread(target=make_request, args=[host, port, reports, i + 1])
52 t.setDaemon(True)
53 t.start()
54
55 while responses < iterations:
56 time.sleep(1)
57
58 if len(reports) < iterations:
59 print(json.dumps(reports), file=sys.stderr)
60 sys.exit(1)
61