1 /** @file
2  *
3  *  A brief file description
4  *
5  *  @section license License
6  *
7  *  Licensed to the Apache Software Foundation (ASF) under one
8  *  or more contributor license agreements.  See the NOTICE file
9  *  distributed with this work for additional information
10  *  regarding copyright ownership.  The ASF licenses this file
11  *  to you under the Apache License, Version 2.0 (the
12  *  "License"); you may not use this file except in compliance
13  *  with the License.  You may obtain a copy of the License at
14  *
15  *      http://www.apache.org/licenses/LICENSE-2.0
16  *
17  *  Unless required by applicable law or agreed to in writing, software
18  *  distributed under the License is distributed on an "AS IS" BASIS,
19  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  *  See the License for the specific language governing permissions and
21  *  limitations under the License.
22  */
23 
24 #include "quic_client.h"
25 
26 #include <iostream>
27 #include <fstream>
28 #include <string_view>
29 
30 #include "Http3Transaction.h"
31 
32 // OpenSSL protocol-lists format (vector of 8-bit length-prefixed, byte strings)
33 // https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_alpn_protos.html
34 // Should be integrate with IP_PROTO_TAG_HTTP_QUIC in ts/ink_inet.h ?
35 using namespace std::literals;
36 static constexpr std::string_view HQ_ALPN_PROTO_LIST("\5hq-23"sv);
37 static constexpr std::string_view H3_ALPN_PROTO_LIST("\5h3-23"sv);
38 
QUICClient(const QUICClientConfig * config)39 QUICClient::QUICClient(const QUICClientConfig *config) : Continuation(new_ProxyMutex()), _config(config)
40 {
41   SET_HANDLER(&QUICClient::start);
42 }
43 
~QUICClient()44 QUICClient::~QUICClient()
45 {
46   freeaddrinfo(this->_remote_addr_info);
47 }
48 
49 int
start(int,void *)50 QUICClient::start(int, void *)
51 {
52   SET_HANDLER(&QUICClient::state_http_server_open);
53 
54   struct addrinfo hints;
55 
56   memset(&hints, 0, sizeof(struct addrinfo));
57   hints.ai_family   = AF_UNSPEC;
58   hints.ai_socktype = SOCK_DGRAM;
59   hints.ai_flags    = 0;
60   hints.ai_protocol = 0;
61 
62   int res = getaddrinfo(this->_config->addr, this->_config->port, &hints, &this->_remote_addr_info);
63   if (res < 0) {
64     Debug("quic_client", "Error: %s (%d)", strerror(errno), errno);
65     return EVENT_DONE;
66   }
67 
68   std::string_view alpn_protos;
69   if (this->_config->http3) {
70     alpn_protos = H3_ALPN_PROTO_LIST;
71   } else {
72     alpn_protos = HQ_ALPN_PROTO_LIST;
73   }
74 
75   for (struct addrinfo *info = this->_remote_addr_info; info != nullptr; info = info->ai_next) {
76     NetVCOptions opt;
77     opt.ip_proto            = NetVCOptions::USE_UDP;
78     opt.ip_family           = info->ai_family;
79     opt.etype               = ET_NET;
80     opt.socket_recv_bufsize = 1048576;
81     opt.socket_send_bufsize = 1048576;
82     opt.alpn_protos         = alpn_protos;
83     opt.set_sni_servername(this->_config->addr, strnlen(this->_config->addr, 1023));
84 
85     SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
86 
87     Action *action = quic_NetProcessor.connect_re(this, info->ai_addr, &opt);
88     if (action == ACTION_RESULT_DONE) {
89       break;
90     }
91   }
92   return EVENT_CONT;
93 }
94 
95 // Similar to HttpSM::state_http_server_open(int event, void *data)
96 int
state_http_server_open(int event,void * data)97 QUICClient::state_http_server_open(int event, void *data)
98 {
99   switch (event) {
100   case NET_EVENT_OPEN: {
101     // TODO: create ProxyServerSession / ProxyServerTransaction
102     Debug("quic_client", "start proxy server ssn/txn");
103 
104     QUICNetVConnection *conn = static_cast<QUICNetVConnection *>(data);
105 
106     if (this->_config->http0_9) {
107       Http09ClientApp *app = new Http09ClientApp(conn, this->_config);
108       app->start();
109     } else if (this->_config->http3) {
110       // TODO: see what server session is doing with IpAllow::ACL
111       IpAllow::ACL session_acl;
112       Http3ClientApp *app = new Http3ClientApp(conn, std::move(session_acl), options, this->_config);
113       SCOPED_MUTEX_LOCK(lock, app->mutex, this_ethread());
114       app->start();
115     } else {
116       ink_abort("invalid config");
117     }
118 
119     break;
120   }
121   case NET_EVENT_OPEN_FAILED: {
122     ink_assert(false);
123     break;
124   }
125   case NET_EVENT_ACCEPT: {
126     // do nothing
127     break;
128   }
129   default:
130     ink_assert(false);
131   }
132 
133   return 0;
134 }
135 
136 //
137 // Http09ClientApp
138 //
139 #define Http09ClientAppDebug(fmt, ...) Debug("quic_client_app", "[%s] " fmt, this->_qc->cids().data(), ##__VA_ARGS__)
140 #define Http09ClientAppVDebug(fmt, ...) Debug("v_quic_client_app", "[%s] " fmt, this->_qc->cids().data(), ##__VA_ARGS__)
141 
Http09ClientApp(QUICNetVConnection * qvc,const QUICClientConfig * config)142 Http09ClientApp::Http09ClientApp(QUICNetVConnection *qvc, const QUICClientConfig *config) : QUICApplication(qvc), _config(config)
143 {
144   this->_qc->stream_manager()->set_default_application(this);
145 
146   SET_HANDLER(&Http09ClientApp::main_event_handler);
147 }
148 
149 void
start()150 Http09ClientApp::start()
151 {
152   if (this->_config->output[0] != 0x0) {
153     this->_filename = this->_config->output;
154   }
155 
156   if (this->_filename) {
157     // Destroy contents if file already exists
158     std::ofstream f_stream(this->_filename, std::ios::binary | std::ios::trunc);
159   }
160 
161   this->_do_http_request();
162 }
163 
164 void
_do_http_request()165 Http09ClientApp::_do_http_request()
166 {
167   QUICStreamId stream_id;
168   QUICConnectionErrorUPtr error = this->_qc->stream_manager()->create_bidi_stream(stream_id);
169 
170   if (error != nullptr) {
171     Error("%s", error->msg);
172     ink_abort("Could not create bidi stream : %s", error->msg);
173   }
174 
175   // TODO: move to transaction
176   char request[1024] = {0};
177   int request_len    = snprintf(request, sizeof(request), "GET %s\r\n", this->_config->path);
178 
179   Http09ClientAppDebug("\n%s", request);
180 
181   QUICStreamIO *stream_io = this->_find_stream_io(stream_id);
182 
183   stream_io->write(reinterpret_cast<uint8_t *>(request), request_len);
184   stream_io->write_done();
185   stream_io->write_reenable();
186 }
187 
188 int
main_event_handler(int event,Event * data)189 Http09ClientApp::main_event_handler(int event, Event *data)
190 {
191   Http09ClientAppVDebug("%s (%d)", get_vc_event_name(event), event);
192 
193   VIO *vio                = reinterpret_cast<VIO *>(data);
194   QUICStreamIO *stream_io = this->_find_stream_io(vio);
195 
196   if (stream_io == nullptr) {
197     Http09ClientAppDebug("Unknown Stream");
198     return -1;
199   }
200 
201   switch (event) {
202   case VC_EVENT_READ_READY:
203   case VC_EVENT_READ_COMPLETE: {
204     std::streambuf *default_stream = nullptr;
205     std::ofstream f_stream;
206 
207     if (this->_filename) {
208       default_stream = std::cout.rdbuf();
209       f_stream       = std::ofstream(this->_filename, std::ios::binary | std::ios::app);
210       std::cout.rdbuf(f_stream.rdbuf());
211     }
212 
213     uint8_t buf[8192] = {0};
214     int64_t nread;
215     while ((nread = stream_io->read(buf, sizeof(buf))) > 0) {
216       std::cout.write(reinterpret_cast<char *>(buf), nread);
217     }
218     std::cout.flush();
219 
220     if (this->_filename) {
221       f_stream.close();
222       std::cout.rdbuf(default_stream);
223     }
224 
225     if (stream_io->is_read_done() && this->_config->close) {
226       // Connection Close Exercise
227       this->_qc->close(QUICConnectionErrorUPtr(new QUICConnectionError(QUICTransErrorCode::NO_ERROR, "Close Exercise")));
228     }
229 
230     break;
231   }
232   case VC_EVENT_WRITE_READY:
233   case VC_EVENT_WRITE_COMPLETE:
234     break;
235   case VC_EVENT_EOS:
236   case VC_EVENT_ERROR:
237   case VC_EVENT_INACTIVITY_TIMEOUT:
238   case VC_EVENT_ACTIVE_TIMEOUT:
239     ink_assert(false);
240     break;
241   default:
242     break;
243   }
244 
245   return EVENT_CONT;
246 }
247 
248 //
249 // Http3ClientApp
250 //
Http3ClientApp(QUICNetVConnection * qvc,IpAllow::ACL && session_acl,const HttpSessionAccept::Options & options,const QUICClientConfig * config)251 Http3ClientApp::Http3ClientApp(QUICNetVConnection *qvc, IpAllow::ACL &&session_acl, const HttpSessionAccept::Options &options,
252                                const QUICClientConfig *config)
253   : super(qvc, std::move(session_acl), options), _config(config)
254 {
255 }
256 
~Http3ClientApp()257 Http3ClientApp::~Http3ClientApp()
258 {
259   free_MIOBuffer(this->_req_buf);
260   this->_req_buf = nullptr;
261 
262   free_MIOBuffer(this->_resp_buf);
263   this->_resp_buf = nullptr;
264 
265   delete this->_resp_handler;
266 }
267 
268 void
start()269 Http3ClientApp::start()
270 {
271   this->_req_buf                  = new_MIOBuffer();
272   this->_resp_buf                 = new_MIOBuffer();
273   IOBufferReader *resp_buf_reader = _resp_buf->alloc_reader();
274 
275   this->_resp_handler = new RespHandler(this->_config, resp_buf_reader);
276 
277   super::start();
278   this->_do_http_request();
279 }
280 
281 void
_do_http_request()282 Http3ClientApp::_do_http_request()
283 {
284   QUICConnectionErrorUPtr error;
285   QUICStreamId stream_id;
286   error = this->_qc->stream_manager()->create_bidi_stream(stream_id);
287   if (error != nullptr) {
288     Error("%s", error->msg);
289     ink_abort("Could not create bidi stream : %s", error->msg);
290   }
291 
292   QUICStreamIO *stream_io = this->_find_stream_io(stream_id);
293 
294   // TODO: create Http3ServerTransaction
295   Http3Transaction *txn = new Http3Transaction(this->_ssn, stream_io);
296   SCOPED_MUTEX_LOCK(lock, txn->mutex, this_ethread());
297 
298   // TODO: fix below issue with H2 origin conn stuff
299   // Do not call ProxyClientTransaction::new_transaction(), but need to setup txn - e.g. do_io_write / do_io_read
300   VIO *read_vio = txn->do_io_read(this->_resp_handler, INT64_MAX, this->_resp_buf);
301   this->_resp_handler->set_read_vio(read_vio);
302 
303   // Write HTTP Request to write_vio
304   char request[1024] = {0};
305   std::string format;
306   if (this->_config->path[0] == '/') {
307     format = "GET https://%s%s HTTP/1.1\r\n\r\n";
308   } else {
309     format = "GET https://%s/%s HTTP/1.1\r\n\r\n";
310   }
311 
312   int request_len = snprintf(request, sizeof(request), format.c_str(), this->_config->addr, this->_config->path);
313 
314   Http09ClientAppDebug("\n%s", request);
315 
316   // TODO: check write avail size
317   int64_t nbytes            = this->_req_buf->write(request, request_len);
318   IOBufferReader *buf_start = this->_req_buf->alloc_reader();
319   txn->do_io_write(this, nbytes, buf_start);
320 }
321 
322 //
323 // Response Handler
324 //
RespHandler(const QUICClientConfig * config,IOBufferReader * reader)325 RespHandler::RespHandler(const QUICClientConfig *config, IOBufferReader *reader)
326   : Continuation(new_ProxyMutex()), _config(config), _reader(reader)
327 {
328   if (this->_config->output[0] != 0x0) {
329     this->_filename = this->_config->output;
330   }
331 
332   if (this->_filename) {
333     // Destroy contents if file already exists
334     std::ofstream f_stream(this->_filename, std::ios::binary | std::ios::trunc);
335   }
336 
337   SET_HANDLER(&RespHandler::main_event_handler);
338 }
339 
340 void
set_read_vio(VIO * vio)341 RespHandler::set_read_vio(VIO *vio)
342 {
343   this->_read_vio = vio;
344 }
345 
346 int
main_event_handler(int event,Event * data)347 RespHandler::main_event_handler(int event, Event *data)
348 {
349   Debug("v_http3", "%s", get_vc_event_name(event));
350   switch (event) {
351   case VC_EVENT_READ_READY:
352   case VC_EVENT_READ_COMPLETE: {
353     std::streambuf *default_stream = nullptr;
354     std::ofstream f_stream;
355 
356     if (this->_filename) {
357       default_stream = std::cout.rdbuf();
358       f_stream       = std::ofstream(this->_filename, std::ios::binary | std::ios::app);
359       std::cout.rdbuf(f_stream.rdbuf());
360     }
361 
362     uint8_t buf[8192] = {0};
363     int64_t nread;
364     while ((nread = this->_reader->read(buf, sizeof(buf))) > 0) {
365       std::cout.write(reinterpret_cast<char *>(buf), nread);
366       this->_read_vio->ndone += nread;
367     }
368     std::cout.flush();
369 
370     if (this->_filename) {
371       f_stream.close();
372       std::cout.rdbuf(default_stream);
373     }
374 
375     break;
376   }
377   case VC_EVENT_WRITE_READY:
378   case VC_EVENT_WRITE_COMPLETE:
379   default:
380     break;
381   }
382 
383   return EVENT_CONT;
384 }
385