Skip to content

Commit a838840

Browse files
author
Dane Springmeyer
committed
actually add web client code
1 parent 58e48b4 commit a838840

File tree

1 file changed

+227
-0
lines changed

1 file changed

+227
-0
lines changed

webclient.cc

+227
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
#include <cstdio>
2+
#include <cstdlib>
3+
#include <cassert>
4+
#include "uv.h"
5+
#include "http_parser.h"
6+
// posix only
7+
#include <sys/resource.h> // setrlimit, getrlimit
8+
#include <string>
9+
#include <iostream>
10+
#include <sstream>
11+
12+
13+
static uv_loop_t* uv_loop;
14+
static http_parser_settings req_parser_settings;
15+
static int request_num = 1;
16+
// http://serverfault.com/questions/145907/does-mac-os-x-throttle-the-rate-of-socket-creation
17+
//static int req_num = 16000;
18+
static int req_num = 100;
19+
20+
#define CHECK(status, msg) \
21+
if (status != 0) { \
22+
fprintf(stderr, "%s: %s\n", msg, uv_err_name(status)); \
23+
exit(1); \
24+
}
25+
#define UVERR(err, msg) fprintf(stderr, "%s: %s\n", msg, uv_strerror(err))
26+
#define LOG_ERROR(msg) puts(msg);
27+
#define LOG(msg) puts(msg);
28+
#define LOGF(fmt, params...) printf(fmt "\n", params);
29+
30+
struct client_t {
31+
client_t() :
32+
body() {}
33+
http_parser parser;
34+
int request_num;
35+
uv_tcp_t tcp;
36+
uv_connect_t connect_req;
37+
uv_shutdown_t shutdown_req;
38+
uv_write_t write_req;
39+
std::stringstream body;
40+
};
41+
42+
uv_buf_t alloc_buffer(uv_handle_t *handle, size_t suggested_size) {
43+
return uv_buf_init((char*) malloc(suggested_size), suggested_size);
44+
}
45+
46+
void on_close(uv_handle_t* handle) {
47+
client_t* client = (client_t*) handle->data;
48+
LOG("on close")
49+
//client->tcp.data = NULL;
50+
delete client;
51+
}
52+
53+
void on_read(uv_stream_t *tcp, ssize_t nread, uv_buf_t buf) {
54+
size_t parsed;
55+
client_t* client = (client_t*) tcp->data;
56+
LOGF("on read: %ld",nread);
57+
LOGF("on read buf.size: %ld",buf.len);
58+
if (nread > 0) {
59+
http_parser * parser = &client->parser;
60+
/*if (parser->http_errno == HPE_PAUSED) {
61+
LOG("PAUSED");
62+
return ;
63+
}*/
64+
parsed = http_parser_execute(parser, &req_parser_settings, buf.base, nread);
65+
if (parser->upgrade) {
66+
LOG("We do not support upgrades yet")
67+
}
68+
else if (parsed != nread) {
69+
LOGF("parsed incomplete data::%ld/%ld bytes parsed\n", parsed, nread);
70+
LOGF("\n*** %s ***\n\n",
71+
http_errno_description(HTTP_PARSER_ERRNO(parser)));
72+
}
73+
} else {
74+
if (nread != UV_EOF) {
75+
UVERR(nread, "read");
76+
}
77+
}
78+
// if not keepalive
79+
//if (!http_should_keep_alive(parser)) {
80+
//if (!uv_is_closing((uv_handle_t*)tcp))
81+
//uv_close((uv_handle_t*)tcp, on_close);
82+
//}
83+
free(buf.base);
84+
}
85+
86+
void after_write(uv_write_t* req, int status) {
87+
LOG("after write")
88+
CHECK(status, "write");
89+
}
90+
91+
void on_connect(uv_connect_t *req, int status) {
92+
client_t *client = (client_t*)req->handle->data;
93+
94+
if (status == -1) {
95+
fprintf(stderr, "connect failed error %s\n", uv_err_name(status));
96+
uv_close((uv_handle_t*)req->handle, on_close);
97+
return;
98+
}
99+
100+
client->request_num++;
101+
102+
LOGF("[ %5d ] new connection", request_num);
103+
104+
uv_buf_t resbuf;
105+
std::string res =
106+
"GET /hello HTTP/1.1\r\n"
107+
"Host: 0.0.0.0=8000\r\n"
108+
"User-Agent: webclient.c\r\n"
109+
"Keep-Alive: 100\r\n"
110+
"Connection: keep-alive\r\n"
111+
"\r\n";
112+
resbuf.base = (char *)res.c_str();
113+
resbuf.len = res.size();
114+
115+
int rr = uv_read_start(req->handle, alloc_buffer, on_read);
116+
CHECK(rr, "bind");
117+
118+
int r = uv_write(&client->write_req,
119+
req->handle,
120+
&resbuf,
121+
1,
122+
after_write);
123+
CHECK(r, "bind");
124+
}
125+
126+
int on_message_begin(http_parser* /*parser*/) {
127+
printf("\n***MESSAGE BEGIN***\n");
128+
return 0;
129+
}
130+
131+
int on_headers_complete(http_parser* /*parser*/) {
132+
printf("\n***HEADERS COMPLETE***\n");
133+
return 0;
134+
}
135+
136+
int on_url(http_parser* /*parser*/, const char* at, size_t length) {
137+
printf("Url: %.*s\n", (int)length, at);
138+
return 0;
139+
}
140+
141+
int on_header_field(http_parser* /*parser*/, const char* at, size_t length) {
142+
printf("Header field: %.*s\n", (int)length, at);
143+
return 0;
144+
}
145+
146+
int on_header_value(http_parser* /*parser*/, const char* at, size_t length) {
147+
printf("Header value: %.*s\n", (int)length, at);
148+
return 0;
149+
}
150+
151+
int on_body(http_parser* parser, const char* at, size_t length) {
152+
printf("Body: %d\n", (int)length);
153+
client_t *client = (client_t*)parser->data;
154+
if (at && client)
155+
{
156+
client->body << std::string(at,length);
157+
}
158+
159+
//fprintf("Body: %.*s\n", (int)length, at);
160+
/*if (http_body_is_final(parser)) {
161+
LOG("body is final!")
162+
std::cerr << "on-body len: " << length << "\n";
163+
} else {
164+
LOG("body not final\n")
165+
std::cerr << "non final on-body len: " << length << "\n";
166+
}*/
167+
168+
return 0;
169+
}
170+
171+
int on_message_complete(http_parser* parser) {
172+
printf("\n***MESSAGE COMPLETE***\n\n");
173+
client_t *client = (client_t*)parser->data;
174+
ssize_t total_len = client->body.str().size();
175+
LOGF("total length parsed: %ld",total_len)
176+
if (http_should_keep_alive(parser)) {
177+
printf("\n***SHOULD CLOSE keepalive HERE \n\n");
178+
uv_stream_t* tcp = (uv_stream_t*)&client->tcp;
179+
uv_close((uv_handle_t*)tcp, on_close);
180+
}
181+
return 0;
182+
}
183+
184+
int main() {
185+
// mainly for osx, bump up ulimit
186+
struct rlimit limit;
187+
getrlimit(RLIMIT_NOFILE,&limit);
188+
LOGF("current ulimit: %lld",limit.rlim_cur);
189+
req_parser_settings.on_message_begin = on_message_begin;
190+
req_parser_settings.on_url = on_url;
191+
req_parser_settings.on_header_field = on_header_field;
192+
req_parser_settings.on_header_value = on_header_value;
193+
req_parser_settings.on_headers_complete = on_headers_complete;
194+
req_parser_settings.on_body = on_body;
195+
req_parser_settings.on_message_complete = on_message_complete;
196+
197+
struct sockaddr_in dest = uv_ip4_addr("127.0.0.1", 8000);
198+
199+
uv_loop = uv_default_loop();
200+
for (int i=0;i<req_num;++i) {
201+
client_t* client = new client_t();
202+
client->request_num = request_num;
203+
client->tcp.data = client;
204+
http_parser_init(&client->parser, HTTP_RESPONSE);
205+
client->parser.data = client;
206+
int r = uv_tcp_init(uv_loop, &client->tcp);
207+
CHECK(r, "bind");
208+
//r = uv_tcp_keepalive(&client->tcp,1,60);
209+
//CHECK(r, "bind");
210+
r = uv_tcp_connect(&client->connect_req, &client->tcp, dest, on_connect);
211+
CHECK(r, "bind");
212+
/*
213+
* This function runs the event loop. It will act differently depending on the
214+
* specified mode:
215+
* - UV_RUN_DEFAULT: Runs the event loop until the reference count drops to
216+
* zero. Always returns zero.
217+
* - UV_RUN_ONCE: Poll for new events once. Note that this function blocks if
218+
* there are no pending events. Returns zero when done (no active handles
219+
* or requests left), or non-zero if more events are expected (meaning you
220+
* should run the event loop again sometime in the future).
221+
* - UV_RUN_NOWAIT: Poll for new events once but don't block if there are no
222+
* pending events.
223+
*/
224+
uv_run(uv_loop,UV_RUN_DEFAULT);
225+
}
226+
LOG("listening on port 8000");
227+
}

0 commit comments

Comments
 (0)