-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathsocket.c
More file actions
173 lines (152 loc) · 6.29 KB
/
socket.c
File metadata and controls
173 lines (152 loc) · 6.29 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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "socket.h"
#include "block.h"
#include "string.h"
/**
* В адресе маскируется ipv4
* @param in6Addr
* @return
*/
unsigned char getIpVersion(struct in6_addr *in6Addr) {
return IN6_IS_ADDR_V4MAPPED(in6Addr) ? SOCKET_VERSION_IPV4_BIT : SOCKET_VERSION_IPV6_BIT;
}
/**
* Рендер сообщения по сокету
* @param request
* @param code
* @param message
* @param size
*/
void renderHttpMessage(struct render *render) {
struct block *body = {0};
// First line headers
switch (render->code) {
case 101:
render->stats->http_101++;
addFormatStringBlock(render->block, 1000,
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: %s\r\n",
render->websocketKey);
break;
case 400:
render->stats->http_400++;
addStringBlock(render->block, "HTTP/1.0 400 Invalid Request\r\n", 30);
break;
case 401:
render->stats->http_401++;
addStringBlock(render->block, "HTTP/1.0 401 Unauthorized\r\n", 27);
addStringBlock(render->block, "WWW-Authenticate: Basic realm=\"etracker\"\r\n", 42);
break;
case 403:
render->stats->http_403++;
addStringBlock(render->block, "HTTP/1.0 403 Forbidden\r\n", 24);
break;
case 404:
render->stats->http_404++;
addStringBlock(render->block, "HTTP/1.0 404 Not Found\r\n", 24);
break;
case 405:
render->stats->http_405++;
addStringBlock(render->block, "HTTP/1.0 405 Method Not Allowed\r\n", 33);
break;
case 408:
render->stats->http_408++;
addStringBlock(render->block, "HTTP/1.0 408 Request Timeout\r\n", 30);
break;
case 413:
render->stats->http_413++;
addStringBlock(render->block, "HTTP/1.0 413 Request Entity Too Large\r\n", 39);
break;
case 507:
render->stats->http_507++;
addStringBlock(render->block, "HTTP/1.0 507 Insufficient Storage\r\n", 35);
break;
case 200:
default:
render->stats->http_200++;
addStringBlock(render->block, "HTTP/1.1 200 OK\r\n", 17);
break;
}
if (render->code != 200 && render->code != 101) {
body = initBlock();
addFormatStringBlock(body, render->size + 1000,
"d"
"14:failure reason"
"%zu:%s"
"e",
render->size,
render->message
);
render->size = body->size;
}
// End of headers
if (render->websocketKey == NULL) {
if (render->canKeepAlive) {
addStringBlock(render->block, "Connection: Keep-Alive\r\n", 24);
addFormatStringBlock(render->block, 100, "Keep-Alive: timeout=%u, max=1000\r\n", render->socketTimeout);
} else {
addStringBlock(render->block, "Connection: Close\r\n", 19);
}
}
if (render->charset == NULL && render->contentType == NULL) {
addFormatStringBlock(render->block, 1000, "Content-Type: text/plain\r\n");
} else if (render->charset == NULL && render->contentType != NULL) {
addFormatStringBlock(render->block, 1000, "Content-Type: %s\r\n", render->contentType);
} else if (render->charset != NULL && render->contentType == NULL) {
addFormatStringBlock(render->block, 1000, "Content-Type: text/plain; charset=%s\r\n", render->charset);
} else if (render->charset != NULL && render->contentType != NULL) {
addFormatStringBlock(render->block, 1000, "Content-Type: %s; charset=%s\r\n", render->contentType,
render->charset);
}
addFormatStringBlock(render->block, 1000, "Content-Length: %zu\r\n"
"Server: github.com/truekenny/etracker\r\n"
"\r\n",
render->size
);
// Body
if (render->code == 200 || render->code == 101) {
addStringBlock(render->block, render->message, render->size);
} else {
addStringBlock(render->block, body->data, body->size);
freeBlock(body);
}
}
ssize_t send_(int socket, void *message, size_t size, struct stats *stats, _Bool sentOnce) {
// send может принять не всё сразу и за раз доставить только часть данных
ssize_t result;
// Сколько раз пытаться отправить сообщение, перед тем как проигнорировать его
int errorLimitCount = 1000000;
do {
result = send(socket, message, size, MSG_DONTWAIT | MSG_NOSIGNAL);
if (result == -1) {
incErrno(stats->send_errno);
stats->send_failed++;
// printf("send errno:%d message:%s\n", errno, strerror(errno));
// Если ошибка не errno:35 message:Resource temporarily unavailable
// то повторять не стоит
if (errno != EAGAIN) break;
// Некоторые сообщения пробую отправить лишь 1 раз, например сообщения TimeOut
if (sentOnce) break;
errorLimitCount--;
// Не удаётся отправить сообщение слишком много раз, игнорирую такой клиент
if (errorLimitCount <= 0) {
stats->send_skips++;
break;
}
} else
stats->send_pass++;
if (result >= 0) {
stats->sent_bytes += result;
// Отправить надо меньше на result байт
size -= result;
// Указатель надо сместить на result байт
message += result;
}
} while (size != 0);
return result;
}