forked from jeremycw/httpserver.h
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathread_socket.c
More file actions
155 lines (137 loc) · 5.19 KB
/
Copy pathread_socket.c
File metadata and controls
155 lines (137 loc) · 5.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifndef HTTPSERVER_IMPL
#include "common.h"
#include "parser.h"
#include "read_socket.h"
#endif
void _hs_token_array_push(struct hs_token_array_s *array,
struct hsh_token_s a) {
if (array->size == array->capacity) {
array->capacity *= 2;
array->buf = (struct hsh_token_s *)realloc(
array->buf, array->capacity * sizeof(struct hsh_token_s));
assert(array->buf != NULL);
}
array->buf[array->size] = a;
array->size++;
}
void _hs_buffer_init(struct hsh_buffer_s *buffer, int initial_capacity,
int64_t *memused) {
*buffer = (struct hsh_buffer_s){0};
buffer->buf = (char *)calloc(1, initial_capacity);
*memused += initial_capacity;
assert(buffer->buf != NULL);
buffer->capacity = initial_capacity;
}
int _hs_read_into_buffer(struct hsh_buffer_s *buffer, int request_socket,
int64_t *server_memused,
int64_t max_request_buf_capacity) {
int bytes;
do {
bytes = read(request_socket, buffer->buf + buffer->length,
buffer->capacity - buffer->length);
if (bytes > 0)
buffer->length += bytes;
if (buffer->length == buffer->capacity &&
buffer->capacity != max_request_buf_capacity) {
*server_memused -= buffer->capacity;
buffer->capacity *= 2;
if (buffer->capacity > max_request_buf_capacity) {
buffer->capacity = max_request_buf_capacity;
}
*server_memused += buffer->capacity;
buffer->buf = (char *)realloc(buffer->buf, buffer->capacity);
assert(buffer->buf != NULL);
}
} while (bytes > 0 && buffer->capacity < max_request_buf_capacity);
buffer->sequence_id++;
return bytes;
}
int _hs_buffer_requires_read(struct hsh_buffer_s *buffer) {
return buffer->index >= buffer->length;
}
void _hs_exec_callback(http_request_t *request,
void (*cb)(struct http_request_s *)) {
request->state = HTTP_SESSION_NOP;
cb(request);
}
enum hs_read_rc_e
_hs_parse_buffer_and_exec_user_cb(http_request_t *request,
int max_request_buf_capacity) {
enum hs_read_rc_e rc = HS_READ_RC_SUCCESS;
do {
struct hsh_token_s token = hsh_parser_exec(
&request->parser, &request->buffer, max_request_buf_capacity);
switch (token.type) {
case HSH_TOK_HEADERS_DONE:
_hs_token_array_push(&request->tokens, token);
if (HTTP_FLAG_CHECK(token.flags, HSH_TOK_FLAG_STREAMED_BODY) ||
HTTP_FLAG_CHECK(token.flags, HSH_TOK_FLAG_NO_BODY)) {
HTTP_FLAG_SET(request->flags, HTTP_FLG_STREAMED);
_hs_exec_callback(request, request->server->request_handler);
return rc;
}
break;
case HSH_TOK_BODY:
_hs_token_array_push(&request->tokens, token);
if (HTTP_FLAG_CHECK(token.flags, HSH_TOK_FLAG_SMALL_BODY)) {
_hs_exec_callback(request, request->server->request_handler);
} else {
if (HTTP_FLAG_CHECK(token.flags, HSH_TOK_FLAG_BODY_FINAL) &&
token.len > 0) {
_hs_exec_callback(request, request->chunk_cb);
// A zero length body is used to indicate to the user code that the
// body has finished streaming. This is natural when dealing with
// chunked request bodies but requires us to inject a zero length
// body for non-chunked requests.
struct hsh_token_s token = {};
memset(&token, 0, sizeof(struct hsh_token_s));
token.type = HSH_TOK_BODY;
_hs_token_array_push(&request->tokens, token);
_hs_exec_callback(request, request->chunk_cb);
} else {
_hs_exec_callback(request, request->chunk_cb);
}
}
return rc;
case HSH_TOK_ERR:
return HS_READ_RC_PARSE_ERR;
case HSH_TOK_NONE:
return rc;
default:
_hs_token_array_push(&request->tokens, token);
break;
}
} while (1);
}
// Reads the request socket if required and parses HTTP in a non-blocking
// manner.
//
// It should be called when a new connection is established and when a read
// ready event occurs for the request socket. It parses the HTTP request and
// fills the tokens array of the request struct. It will also invoke the
// request_hander callback and the chunk_cb callback in the appropriate
// scenarios.
enum hs_read_rc_e hs_read_request_and_exec_user_cb(http_request_t *request,
struct hs_read_opts_s opts) {
request->state = HTTP_SESSION_READ;
request->timeout = HTTP_REQUEST_TIMEOUT;
if (request->buffer.buf == NULL) {
_hs_buffer_init(&request->buffer, opts.initial_request_buf_capacity,
&request->server->memused);
hsh_parser_init(&request->parser);
}
if (_hs_buffer_requires_read(&request->buffer)) {
int bytes = _hs_read_into_buffer(&request->buffer, request->socket,
&request->server->memused,
opts.max_request_buf_capacity);
if (bytes == opts.eof_rc) {
return HS_READ_RC_SOCKET_ERR;
}
}
return _hs_parse_buffer_and_exec_user_cb(request,
opts.max_request_buf_capacity);
}