1 /*
2 * lws-minimal-raw-client
3 *
4 * Written in 2010-2022 by Andy Green <andy@warmcat.com>
5 *
6 * This file is made available under the Creative Commons CC0 1.0
7 * Universal Public Domain Dedication.
8 *
9 * This demonstrates connecting a "raw" client connection
10 */
11
12 #include <libwebsockets.h>
13 #include <string.h>
14 #include <signal.h>
15 #if !defined(WIN32)
16 #include <sys/socket.h>
17 #include <sys/types.h>
18 #include <netinet/in.h>
19 #include <netdb.h>
20 #include <arpa/inet.h>
21 #endif
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #if !defined(WIN32)
26 #include <unistd.h>
27 #endif
28 #include <errno.h>
29
30 #include <assert.h>
31
32 static struct lws *raw_wsi, *stdin_wsi;
33 static uint8_t buf[LWS_PRE + 4096];
34 static int waiting, interrupted;
35 static struct lws_context *context;
36 static int us_wait_after_input_close = LWS_USEC_PER_SEC / 10;
37
38 static const char *server = "libwebsockets.org", *port = "443";
39
40 static int
callback_raw_test(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)41 callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
42 void *user, void *in, size_t len)
43 {
44 const char *cp = (const char *)in;
45
46 switch (reason) {
47
48 /* callbacks related to file descriptor */
49
50 case LWS_CALLBACK_RAW_ADOPT_FILE:
51 lwsl_user("LWS_CALLBACK_RAW_ADOPT_FILE\n");
52 break;
53
54 case LWS_CALLBACK_RAW_CLOSE_FILE:
55 lwsl_user("LWS_CALLBACK_RAW_CLOSE_FILE\n");
56 /* stdin close, wait 1s then close the raw skt */
57 stdin_wsi = NULL; /* invalid now we close */
58 if (raw_wsi)
59 lws_set_timer_usecs(raw_wsi, us_wait_after_input_close);
60 else {
61 interrupted = 1;
62 lws_cancel_service(context);
63 }
64 break;
65
66 case LWS_CALLBACK_RAW_RX_FILE:
67 lwsl_user("LWS_CALLBACK_RAW_RX_FILE\n");
68 waiting = (int)read(0, buf, sizeof(buf));
69 lwsl_notice("raw file read %d\n", waiting);
70 if (waiting < 0)
71 return -1;
72
73 if (raw_wsi)
74 lws_callback_on_writable(raw_wsi);
75 lws_rx_flow_control(wsi, 0);
76 break;
77
78
79 /* callbacks related to raw socket descriptor */
80
81 case LWS_CALLBACK_RAW_ADOPT:
82 lwsl_user("LWS_CALLBACK_RAW_ADOPT\n");
83 lws_callback_on_writable(wsi);
84 break;
85
86 case LWS_CALLBACK_RAW_CONNECTED:
87 lwsl_user("LWS_CALLBACK_RAW_CONNECTED\n");
88 break;
89
90 case LWS_CALLBACK_RAW_CLOSE:
91 lwsl_user("LWS_CALLBACK_RAW_CLOSE\n");
92 /*
93 * If the socket to the remote server closed, we must close
94 * and drop any remaining stdin
95 */
96 interrupted = 1;
97 lws_cancel_service(context);
98 /* our pointer to this wsi is invalid now we close */
99 raw_wsi = NULL;
100 break;
101
102 case LWS_CALLBACK_RAW_RX:
103 lwsl_user("LWS_CALLBACK_RAW_RX (%d)\n", (int)len);
104 while (len--)
105 putchar(*cp++);
106 fflush(stdout);
107 break;
108
109 case LWS_CALLBACK_RAW_WRITEABLE:
110 lwsl_user("LWS_CALLBACK_RAW_WRITEABLE\n");
111 // lwsl_hexdump_info(buf, waiting);
112 if (!waiting)
113 break;
114 if (stdin_wsi)
115 lws_rx_flow_control(stdin_wsi, 1);
116 if (lws_write(wsi, buf, (unsigned int)waiting, LWS_WRITE_RAW) != waiting) {
117 lwsl_notice("%s: raw skt write failed\n", __func__);
118
119 return -1;
120 }
121 break;
122
123 case LWS_CALLBACK_TIMER:
124 lwsl_user("LWS_CALLBACK_TIMER\n");
125 interrupted = 1;
126 lws_cancel_service(context);
127 return -1;
128
129 default:
130 break;
131 }
132
133 return 0;
134 }
135
136 static struct lws_protocols protocols[] = {
137 { "raw-test", callback_raw_test, 0, 0, 0, NULL, 0 },
138 LWS_PROTOCOL_LIST_TERM
139 };
140
141 static int
system_notify_cb(lws_state_manager_t * mgr,lws_state_notify_link_t * link,int current,int target)142 system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
143 int current, int target)
144 {
145 struct lws_client_connect_info i;
146
147 if (current != LWS_SYSTATE_OPERATIONAL ||
148 target != LWS_SYSTATE_OPERATIONAL)
149 return 0;
150
151 memset(&i, 0, sizeof i);
152 i.context = context;
153 i.method = "RAW";
154 i.ssl_connection = LCCSCF_USE_SSL;
155 i.alpn = "http/1.1";
156 i.address = server;
157 i.host = server;
158 i.port = atoi(port);
159 i.local_protocol_name = "raw-test";
160
161 waiting = lws_snprintf((char *)buf, sizeof(buf), "GET / HTTP/1.1\xaHost: libwebsockets.org\xa\xa");
162
163 if (!lws_client_connect_via_info(&i)) {
164 lwsl_err("Client creation failed\n");
165 interrupted = 1;
166 }
167
168 return 0;
169 }
170
sigint_handler(int sig)171 void sigint_handler(int sig)
172 {
173 interrupted = 1;
174 }
175
main(int argc,const char ** argv)176 int main(int argc, const char **argv)
177 {
178 struct lws_context_creation_info info;
179 const char *p;
180 int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
181 lws_state_notify_link_t notifier = { { NULL, NULL, NULL },
182 system_notify_cb, "app" };
183 lws_state_notify_link_t *na[] = { ¬ifier, NULL };
184
185 signal(SIGINT, sigint_handler);
186
187 if ((p = lws_cmdline_option(argc, argv, "-d")))
188 logs = atoi(p);
189
190 lws_set_log_level(logs, NULL);
191 lwsl_user("LWS minimal raw client\n");
192
193 memset(&info, 0, sizeof info);
194
195 info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
196 info.port = CONTEXT_PORT_NO_LISTEN_SERVER;
197 info.protocols = protocols;
198 info.register_notifier_list = na;
199
200 context = lws_create_context(&info);
201 if (!context) {
202 lwsl_err("lws init failed\n");
203 return 1;
204 }
205
206 while (n >= 0 && !interrupted)
207 n = lws_service(context, 0);
208
209 lwsl_user("%s: destroying context\n", __func__);
210
211 lws_context_destroy(context);
212
213 return 0;
214 }
215