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 "Http3Transaction.h"
25 
26 #include "QUICDebugNames.h"
27 
28 #include "Http3Session.h"
29 #include "Http3StreamDataVIOAdaptor.h"
30 #include "Http3HeaderVIOAdaptor.h"
31 #include "Http3HeaderFramer.h"
32 #include "Http3DataFramer.h"
33 #include "HttpSM.h"
34 #include "HTTP2.h"
35 
36 #define Http3TransDebug(fmt, ...)                                                                                            \
37   Debug("http3_trans", "[%s] [%" PRIx32 "] " fmt,                                                                            \
38         static_cast<QUICConnection *>(reinterpret_cast<QUICNetVConnection *>(this->_proxy_ssn->get_netvc()))->cids().data(), \
39         this->get_transaction_id(), ##__VA_ARGS__)
40 
41 #define Http3TransVDebug(fmt, ...)                                                                                           \
42   Debug("v_http3_trans", "[%s] [%" PRIx32 "] " fmt,                                                                          \
43         static_cast<QUICConnection *>(reinterpret_cast<QUICNetVConnection *>(this->_proxy_ssn->get_netvc()))->cids().data(), \
44         this->get_transaction_id(), ##__VA_ARGS__)
45 
46 // static void
47 // dump_io_buffer(IOBufferReader *reader)
48 // {
49 //   IOBufferReader *debug_reader = reader->clone();
50 //   uint8_t msg[1024]            = {0};
51 //   int64_t msg_len              = 1024;
52 //   int64_t read_len             = debug_reader->read(msg, msg_len);
53 //   Debug("v_http3_trans", "len=%" PRId64 "\n%s\n", read_len, msg);
54 // }
55 
56 //
57 // HQTransaction
58 //
HQTransaction(HQSession * session,QUICStreamIO * stream_io)59 HQTransaction::HQTransaction(HQSession *session, QUICStreamIO *stream_io) : super(), _stream_io(stream_io)
60 {
61   this->mutex   = new_ProxyMutex();
62   this->_thread = this_ethread();
63 
64   this->set_proxy_ssn(session);
65 
66   this->_reader = this->_read_vio_buf.alloc_reader();
67 
68   HTTPType http_type = HTTP_TYPE_UNKNOWN;
69   if (this->direction() == NET_VCONNECTION_OUT) {
70     http_type = HTTP_TYPE_RESPONSE;
71   } else {
72     http_type = HTTP_TYPE_REQUEST;
73   }
74 
75   this->_header.create(http_type);
76 }
77 
~HQTransaction()78 HQTransaction::~HQTransaction()
79 {
80   this->_header.destroy();
81 }
82 
83 void
set_active_timeout(ink_hrtime timeout_in)84 HQTransaction::set_active_timeout(ink_hrtime timeout_in)
85 {
86   if (this->_proxy_ssn) {
87     this->_proxy_ssn->set_active_timeout(timeout_in);
88   }
89 }
90 
91 void
set_inactivity_timeout(ink_hrtime timeout_in)92 HQTransaction::set_inactivity_timeout(ink_hrtime timeout_in)
93 {
94   if (this->_proxy_ssn) {
95     this->_proxy_ssn->set_inactivity_timeout(timeout_in);
96   }
97 }
98 
99 void
cancel_inactivity_timeout()100 HQTransaction::cancel_inactivity_timeout()
101 {
102   if (this->_proxy_ssn) {
103     this->_proxy_ssn->cancel_inactivity_timeout();
104   }
105 }
106 
107 void
release(IOBufferReader * r)108 HQTransaction::release(IOBufferReader *r)
109 {
110   super::release(r);
111   this->do_io_close();
112   this->_sm = nullptr;
113 }
114 
115 bool
allow_half_open() const116 HQTransaction::allow_half_open() const
117 {
118   return false;
119 }
120 
121 VIO *
do_io_read(Continuation * c,int64_t nbytes,MIOBuffer * buf)122 HQTransaction::do_io_read(Continuation *c, int64_t nbytes, MIOBuffer *buf)
123 {
124   if (buf) {
125     this->_read_vio.buffer.writer_for(buf);
126   } else {
127     this->_read_vio.buffer.clear();
128   }
129 
130   this->_read_vio.mutex     = c ? c->mutex : this->mutex;
131   this->_read_vio.cont      = c;
132   this->_read_vio.nbytes    = nbytes;
133   this->_read_vio.ndone     = 0;
134   this->_read_vio.vc_server = this;
135   this->_read_vio.op        = VIO::READ;
136 
137   this->_process_read_vio();
138   this->_send_tracked_event(this->_read_event, VC_EVENT_READ_READY, &this->_read_vio);
139 
140   return &this->_read_vio;
141 }
142 
143 VIO *
do_io_write(Continuation * c,int64_t nbytes,IOBufferReader * buf,bool owner)144 HQTransaction::do_io_write(Continuation *c, int64_t nbytes, IOBufferReader *buf, bool owner)
145 {
146   if (buf) {
147     this->_write_vio.buffer.reader_for(buf);
148   } else {
149     this->_write_vio.buffer.clear();
150   }
151 
152   this->_write_vio.mutex     = c ? c->mutex : this->mutex;
153   this->_write_vio.cont      = c;
154   this->_write_vio.nbytes    = nbytes;
155   this->_write_vio.ndone     = 0;
156   this->_write_vio.vc_server = this;
157   this->_write_vio.op        = VIO::WRITE;
158 
159   this->_process_write_vio();
160   this->_send_tracked_event(this->_write_event, VC_EVENT_WRITE_READY, &this->_write_vio);
161 
162   return &this->_write_vio;
163 }
164 
165 void
do_io_close(int lerrno)166 HQTransaction::do_io_close(int lerrno)
167 {
168   if (this->_read_event) {
169     this->_read_event->cancel();
170     this->_read_event = nullptr;
171   }
172 
173   if (this->_write_event) {
174     this->_write_event->cancel();
175     this->_write_event = nullptr;
176   }
177 
178   this->_read_vio.buffer.clear();
179   this->_read_vio.nbytes = 0;
180   this->_read_vio.op     = VIO::NONE;
181   this->_read_vio.cont   = nullptr;
182 
183   this->_write_vio.buffer.clear();
184   this->_write_vio.nbytes = 0;
185   this->_write_vio.op     = VIO::NONE;
186   this->_write_vio.cont   = nullptr;
187 
188   this->_proxy_ssn->do_io_close(lerrno);
189 }
190 
191 void
do_io_shutdown(ShutdownHowTo_t howto)192 HQTransaction::do_io_shutdown(ShutdownHowTo_t howto)
193 {
194   return;
195 }
196 
197 void
reenable(VIO * vio)198 HQTransaction::reenable(VIO *vio)
199 {
200   if (vio->op == VIO::READ) {
201     int64_t len = this->_process_read_vio();
202     this->_stream_io->read_reenable();
203 
204     if (len > 0) {
205       this->_signal_read_event();
206     }
207   } else if (vio->op == VIO::WRITE) {
208     int64_t len = this->_process_write_vio();
209     this->_stream_io->write_reenable();
210 
211     if (len > 0) {
212       this->_signal_write_event();
213     }
214   }
215 }
216 
217 void
destroy()218 HQTransaction::destroy()
219 {
220   _sm = nullptr;
221 }
222 
223 void
transaction_done()224 HQTransaction::transaction_done()
225 {
226   // TODO: start closing transaction
227   return;
228 }
229 
230 int
get_transaction_id() const231 HQTransaction::get_transaction_id() const
232 {
233   return this->_stream_io->stream_id();
234 }
235 
236 void
increment_client_transactions_stat()237 HQTransaction::increment_client_transactions_stat()
238 {
239   // TODO
240 }
241 
242 void
decrement_client_transactions_stat()243 HQTransaction::decrement_client_transactions_stat()
244 {
245   // TODO
246 }
247 
248 NetVConnectionContext_t
direction() const249 HQTransaction::direction() const
250 {
251   return this->_proxy_ssn->get_netvc()->get_context();
252 }
253 
254 /**
255  * @brief Replace existing event only if the new event is different than the inprogress event
256  */
257 Event *
_send_tracked_event(Event * event,int send_event,VIO * vio)258 HQTransaction::_send_tracked_event(Event *event, int send_event, VIO *vio)
259 {
260   if (event != nullptr) {
261     if (event->callback_event != send_event) {
262       event->cancel();
263       event = nullptr;
264     }
265   }
266 
267   if (event == nullptr) {
268     event = this_ethread()->schedule_imm(this, send_event, vio);
269   }
270 
271   return event;
272 }
273 
274 /**
275  * @brief Signal event to this->_read_vio.cont
276  */
277 void
_signal_read_event()278 HQTransaction::_signal_read_event()
279 {
280   if (this->_read_vio.cont == nullptr || this->_read_vio.op == VIO::NONE) {
281     return;
282   }
283   int event = this->_read_vio.ntodo() ? VC_EVENT_READ_READY : VC_EVENT_READ_COMPLETE;
284 
285   MUTEX_TRY_LOCK(lock, this->_read_vio.mutex, this_ethread());
286   if (lock.is_locked()) {
287     this->_read_vio.cont->handleEvent(event, &this->_read_vio);
288   } else {
289     this_ethread()->schedule_imm(this->_read_vio.cont, event, &this->_read_vio);
290   }
291 
292   Http3TransVDebug("%s (%d)", get_vc_event_name(event), event);
293 }
294 
295 /**
296  * @brief Signal event to this->_write_vio.cont
297  */
298 void
_signal_write_event()299 HQTransaction::_signal_write_event()
300 {
301   if (this->_write_vio.cont == nullptr || this->_write_vio.op == VIO::NONE) {
302     return;
303   }
304   int event = this->_write_vio.ntodo() ? VC_EVENT_WRITE_READY : VC_EVENT_WRITE_COMPLETE;
305 
306   MUTEX_TRY_LOCK(lock, this->_write_vio.mutex, this_ethread());
307   if (lock.is_locked()) {
308     this->_write_vio.cont->handleEvent(event, &this->_write_vio);
309   } else {
310     this_ethread()->schedule_imm(this->_write_vio.cont, event, &this->_write_vio);
311   }
312 
313   Http3TransVDebug("%s (%d)", get_vc_event_name(event), event);
314 }
315 
316 //
317 // Http3Transaction
318 //
Http3Transaction(Http3Session * session,QUICStreamIO * stream_io)319 Http3Transaction::Http3Transaction(Http3Session *session, QUICStreamIO *stream_io) : super(session, stream_io)
320 {
321   static_cast<HQSession *>(this->_proxy_ssn)->add_transaction(static_cast<HQTransaction *>(this));
322 
323   this->_header_framer = new Http3HeaderFramer(this, &this->_write_vio, session->local_qpack(), stream_io->stream_id());
324   this->_data_framer   = new Http3DataFramer(this, &this->_write_vio);
325   this->_frame_collector.add_generator(this->_header_framer);
326   this->_frame_collector.add_generator(this->_data_framer);
327   // this->_frame_collector.add_generator(this->_push_controller);
328 
329   this->_header_handler = new Http3HeaderVIOAdaptor(&this->_header, session->remote_qpack(), this, stream_io->stream_id());
330   this->_data_handler   = new Http3StreamDataVIOAdaptor(&this->_read_vio);
331 
332   this->_frame_dispatcher.add_handler(this->_header_handler);
333   this->_frame_dispatcher.add_handler(this->_data_handler);
334 
335   SET_HANDLER(&Http3Transaction::state_stream_open);
336 }
337 
~Http3Transaction()338 Http3Transaction::~Http3Transaction()
339 {
340   delete this->_header_framer;
341   delete this->_data_framer;
342   delete this->_header_handler;
343   delete this->_data_handler;
344 }
345 
346 int
state_stream_open(int event,void * edata)347 Http3Transaction::state_stream_open(int event, void *edata)
348 {
349   // TODO: should check recursive call?
350   if (this->_thread != this_ethread()) {
351     // Send on to the owning thread
352     if (this->_cross_thread_event == nullptr) {
353       this->_cross_thread_event = this->_thread->schedule_imm(this, event, edata);
354     }
355     return 0;
356   }
357 
358   SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
359 
360   Event *e = static_cast<Event *>(edata);
361   if (e == this->_cross_thread_event) {
362     this->_cross_thread_event = nullptr;
363   }
364 
365   switch (event) {
366   case VC_EVENT_READ_READY:
367   case VC_EVENT_READ_COMPLETE: {
368     Http3TransVDebug("%s (%d)", get_vc_event_name(event), event);
369     int64_t len = this->_process_read_vio();
370     // if no progress, don't need to signal
371     if (len > 0) {
372       this->_signal_read_event();
373     }
374     this->_stream_io->read_reenable();
375 
376     break;
377   }
378   case VC_EVENT_WRITE_READY:
379   case VC_EVENT_WRITE_COMPLETE: {
380     Http3TransVDebug("%s (%d)", get_vc_event_name(event), event);
381     int64_t len = this->_process_write_vio();
382     // if no progress, don't need to signal
383     if (len > 0) {
384       this->_signal_write_event();
385     }
386     this->_stream_io->write_reenable();
387 
388     break;
389   }
390   case VC_EVENT_EOS:
391   case VC_EVENT_ERROR:
392   case VC_EVENT_INACTIVITY_TIMEOUT:
393   case VC_EVENT_ACTIVE_TIMEOUT: {
394     Http3TransVDebug("%s (%d)", get_vc_event_name(event), event);
395     break;
396   }
397   case QPACK_EVENT_DECODE_COMPLETE: {
398     Http3TransVDebug("%s (%d)", "QPACK_EVENT_DECODE_COMPLETE", event);
399     int res = this->_on_qpack_decode_complete();
400     if (res) {
401       // If READ_READY event is scheduled, should it be canceled?
402       this->_signal_read_event();
403     }
404     break;
405   }
406   case QPACK_EVENT_DECODE_FAILED: {
407     Http3TransVDebug("%s (%d)", "QPACK_EVENT_DECODE_FAILED", event);
408     // FIXME: handle error
409     break;
410   }
411   default:
412     Http3TransDebug("Unknown event %d", event);
413   }
414 
415   return EVENT_DONE;
416 }
417 
418 int
state_stream_closed(int event,void * data)419 Http3Transaction::state_stream_closed(int event, void *data)
420 {
421   Http3TransVDebug("%s (%d)", get_vc_event_name(event), event);
422 
423   switch (event) {
424   case VC_EVENT_READ_READY:
425   case VC_EVENT_READ_COMPLETE: {
426     // ignore
427     break;
428   }
429   case VC_EVENT_WRITE_READY:
430   case VC_EVENT_WRITE_COMPLETE: {
431     // ignore
432     break;
433   }
434   case VC_EVENT_EOS:
435   case VC_EVENT_ERROR:
436   case VC_EVENT_INACTIVITY_TIMEOUT:
437   case VC_EVENT_ACTIVE_TIMEOUT: {
438     // TODO
439     Http3TransVDebug("%s (%d)", get_vc_event_name(event), event);
440     break;
441   }
442   default:
443     Http3TransDebug("Unknown event %d", event);
444   }
445 
446   return EVENT_DONE;
447 }
448 
449 void
do_io_close(int lerrno)450 Http3Transaction::do_io_close(int lerrno)
451 {
452   SET_HANDLER(&Http3Transaction::state_stream_closed);
453   super::do_io_close(lerrno);
454 }
455 
456 bool
is_response_header_sent() const457 Http3Transaction::is_response_header_sent() const
458 {
459   return this->_header_framer->is_done();
460 }
461 
462 bool
is_response_body_sent() const463 Http3Transaction::is_response_body_sent() const
464 {
465   return this->_data_framer->is_done();
466 }
467 
468 int64_t
_process_read_vio()469 Http3Transaction::_process_read_vio()
470 {
471   if (this->_read_vio.cont == nullptr || this->_read_vio.op == VIO::NONE) {
472     return 0;
473   }
474 
475   if (this->_thread != this_ethread()) {
476     SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
477     if (this->_cross_thread_event == nullptr) {
478       // Send to the right thread
479       this->_cross_thread_event = this->_thread->schedule_imm(this, VC_EVENT_READ_READY, nullptr);
480     }
481     return 0;
482   }
483 
484   SCOPED_MUTEX_LOCK(lock, this->_read_vio.mutex, this_ethread());
485 
486   uint64_t nread = 0;
487   this->_frame_dispatcher.on_read_ready(*this->_stream_io, nread);
488   return nread;
489 }
490 
491 int64_t
_process_write_vio()492 Http3Transaction::_process_write_vio()
493 {
494   if (this->_write_vio.cont == nullptr || this->_write_vio.op == VIO::NONE) {
495     return 0;
496   }
497 
498   if (this->_thread != this_ethread()) {
499     SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
500     if (this->_cross_thread_event == nullptr) {
501       // Send to the right thread
502       this->_cross_thread_event = this->_thread->schedule_imm(this, VC_EVENT_WRITE_READY, nullptr);
503     }
504     return 0;
505   }
506 
507   SCOPED_MUTEX_LOCK(lock, this->_write_vio.mutex, this_ethread());
508 
509   size_t nwritten = 0;
510   this->_frame_collector.on_write_ready(this->_stream_io, nwritten);
511 
512   return nwritten;
513 }
514 
515 // Constant strings for pseudo headers
516 const char *HTTP3_VALUE_SCHEME    = ":scheme";
517 const char *HTTP3_VALUE_AUTHORITY = ":authority";
518 
519 const unsigned HTTP3_LEN_SCHEME    = countof(":scheme") - 1;
520 const unsigned HTTP3_LEN_AUTHORITY = countof(":authority") - 1;
521 
522 ParseResult
_convert_header_from_3_to_1_1(HTTPHdr * hdrs)523 Http3Transaction::_convert_header_from_3_to_1_1(HTTPHdr *hdrs)
524 {
525   // TODO: do HTTP/3 specific convert, if there
526 
527   if (http_hdr_type_get(hdrs->m_http) == HTTP_TYPE_REQUEST) {
528     // Dirty hack to bypass checks
529     MIMEField *field;
530     if ((field = hdrs->field_find(HTTP3_VALUE_SCHEME, HTTP3_LEN_SCHEME)) == nullptr) {
531       char value_s[]          = "https";
532       MIMEField *scheme_field = hdrs->field_create(HTTP3_VALUE_SCHEME, HTTP3_LEN_SCHEME);
533       scheme_field->value_set(hdrs->m_heap, hdrs->m_mime, value_s, sizeof(value_s) - 1);
534       hdrs->field_attach(scheme_field);
535     }
536 
537     if ((field = hdrs->field_find(HTTP3_VALUE_AUTHORITY, HTTP3_LEN_AUTHORITY)) == nullptr) {
538       char value_a[]             = "localhost";
539       MIMEField *authority_field = hdrs->field_create(HTTP3_VALUE_AUTHORITY, HTTP3_LEN_AUTHORITY);
540       authority_field->value_set(hdrs->m_heap, hdrs->m_mime, value_a, sizeof(value_a) - 1);
541       hdrs->field_attach(authority_field);
542     }
543   }
544 
545   return http2_convert_header_from_2_to_1_1(hdrs);
546 }
547 
548 int
_on_qpack_decode_complete()549 Http3Transaction::_on_qpack_decode_complete()
550 {
551   ParseResult res = this->_convert_header_from_3_to_1_1(&this->_header);
552   if (res == PARSE_RESULT_ERROR) {
553     Http3TransDebug("PARSE_RESULT_ERROR");
554     return -1;
555   }
556 
557   // FIXME: response header might be delayed from first response body because of callback from QPACK
558   // Workaround fix for mixed response header and body
559   if (http_hdr_type_get(this->_header.m_http) == HTTP_TYPE_RESPONSE) {
560     return 0;
561   }
562 
563   SCOPED_MUTEX_LOCK(lock, this->_read_vio.mutex, this_ethread());
564   MIOBuffer *writer = this->_read_vio.get_writer();
565 
566   // TODO: Http2Stream::send_request has same logic. It originally comes from HttpSM::write_header_into_buffer.
567   // a). Make HttpSM::write_header_into_buffer static
568   //   or
569   // b). Add interface to HTTPHdr to dump data
570   //   or
571   // c). Add interface to HttpSM to handle HTTPHdr directly
572   int bufindex;
573   int dumpoffset = 0;
574   int done, tmp;
575   IOBufferBlock *block;
576   do {
577     bufindex = 0;
578     tmp      = dumpoffset;
579     block    = writer->get_current_block();
580     if (!block) {
581       writer->add_block();
582       block = writer->get_current_block();
583     }
584     done = this->_header.print(block->end(), block->write_avail(), &bufindex, &tmp);
585     dumpoffset += bufindex;
586     writer->fill(bufindex);
587     if (!done) {
588       writer->add_block();
589     }
590   } while (!done);
591 
592   return 1;
593 }
594 
595 //
596 // Http09Transaction
597 //
Http09Transaction(Http09Session * session,QUICStreamIO * stream_io)598 Http09Transaction::Http09Transaction(Http09Session *session, QUICStreamIO *stream_io) : super(session, stream_io)
599 {
600   static_cast<HQSession *>(this->_proxy_ssn)->add_transaction(static_cast<HQTransaction *>(this));
601 
602   SET_HANDLER(&Http09Transaction::state_stream_open);
603 }
604 
~Http09Transaction()605 Http09Transaction::~Http09Transaction() {}
606 
607 int
state_stream_open(int event,void * edata)608 Http09Transaction::state_stream_open(int event, void *edata)
609 {
610   // TODO: should check recursive call?
611   Http3TransVDebug("%s (%d)", get_vc_event_name(event), event);
612 
613   if (this->_thread != this_ethread()) {
614     // Send on to the owning thread
615     if (this->_cross_thread_event == nullptr) {
616       this->_cross_thread_event = this->_thread->schedule_imm(this, event, edata);
617     }
618     return 0;
619   }
620 
621   SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
622 
623   Event *e = static_cast<Event *>(edata);
624   if (e == this->_cross_thread_event) {
625     this->_cross_thread_event = nullptr;
626   }
627 
628   switch (event) {
629   case VC_EVENT_READ_READY:
630   case VC_EVENT_READ_COMPLETE: {
631     int64_t len = this->_process_read_vio();
632     // if no progress, don't need to signal
633     if (len > 0) {
634       this->_signal_read_event();
635     }
636     this->_stream_io->read_reenable();
637 
638     break;
639   }
640   case VC_EVENT_WRITE_READY:
641   case VC_EVENT_WRITE_COMPLETE: {
642     int64_t len = this->_process_write_vio();
643     if (len > 0) {
644       this->_signal_write_event();
645     }
646     this->_stream_io->write_reenable();
647 
648     break;
649   }
650   case VC_EVENT_EOS:
651   case VC_EVENT_ERROR:
652   case VC_EVENT_INACTIVITY_TIMEOUT:
653   case VC_EVENT_ACTIVE_TIMEOUT: {
654     Http3TransDebug("%d", event);
655     break;
656   }
657   default:
658     Http3TransDebug("Unknown event %d", event);
659   }
660 
661   return EVENT_DONE;
662 }
663 
664 void
do_io_close(int lerrno)665 Http09Transaction::do_io_close(int lerrno)
666 {
667   SET_HANDLER(&Http09Transaction::state_stream_closed);
668   super::do_io_close(lerrno);
669 }
670 
671 int
state_stream_closed(int event,void * data)672 Http09Transaction::state_stream_closed(int event, void *data)
673 {
674   Http3TransVDebug("%s (%d)", get_vc_event_name(event), event);
675 
676   switch (event) {
677   case VC_EVENT_READ_READY:
678   case VC_EVENT_READ_COMPLETE: {
679     // ignore
680     break;
681   }
682   case VC_EVENT_WRITE_READY:
683   case VC_EVENT_WRITE_COMPLETE: {
684     // ignore
685     break;
686   }
687   case VC_EVENT_EOS:
688   case VC_EVENT_ERROR:
689   case VC_EVENT_INACTIVITY_TIMEOUT:
690   case VC_EVENT_ACTIVE_TIMEOUT: {
691     // TODO
692     break;
693   }
694   default:
695     Http3TransDebug("Unknown event %d", event);
696   }
697 
698   return EVENT_DONE;
699 }
700 
701 // Convert HTTP/0.9 to HTTP/1.1
702 int64_t
_process_read_vio()703 Http09Transaction::_process_read_vio()
704 {
705   if (this->_read_vio.cont == nullptr || this->_read_vio.op == VIO::NONE) {
706     return 0;
707   }
708 
709   if (this->_thread != this_ethread()) {
710     SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
711     if (this->_cross_thread_event == nullptr) {
712       // Send to the right thread
713       this->_cross_thread_event = this->_thread->schedule_imm(this, VC_EVENT_READ_READY, nullptr);
714     }
715     return 0;
716   }
717 
718   SCOPED_MUTEX_LOCK(lock, this->_read_vio.mutex, this_ethread());
719 
720   // Nuke this block when we drop 0.9 support
721   if (!this->_protocol_detected) {
722     uint8_t start[3];
723     if (this->_stream_io->peek(start, 3) < 3) {
724       return 0;
725     }
726     // If the first two bit are 0 and 1, the 3rd byte is type field.
727     // Because there is no type value larger than 0x20, we can assume that the
728     // request is HTTP/0.9 if the value is larger than 0x20.
729     if (0x40 <= start[0] && start[0] < 0x80 && start[2] > 0x20) {
730       this->_legacy_request = true;
731     }
732     this->_protocol_detected = true;
733   }
734 
735   if (this->_legacy_request) {
736     uint64_t nread    = 0;
737     MIOBuffer *writer = this->_read_vio.get_writer();
738 
739     // Nuke this branch when we drop 0.9 support
740     if (!this->_client_req_header_complete) {
741       uint8_t buf[4096];
742       int len = this->_stream_io->peek(buf, 4096);
743       // Check client request is complete or not
744       if (len < 2 || buf[len - 1] != '\n') {
745         return 0;
746       }
747       this->_stream_io->consume(len);
748       nread += len;
749       this->_client_req_header_complete = true;
750 
751       // Check "CRLF" or "LF"
752       int n = 2;
753       if (buf[len - 2] != '\r') {
754         n = 1;
755       }
756 
757       writer->write(buf, len - n);
758       // FIXME: Get hostname from SNI?
759       const char version[] = " HTTP/1.1\r\nHost: localhost\r\n\r\n";
760       writer->write(version, sizeof(version));
761     } else {
762       uint8_t buf[4096];
763       int len;
764       while ((len = this->_stream_io->read(buf, 4096)) > 0) {
765         nread += len;
766         writer->write(buf, len);
767       }
768     }
769 
770     return nread;
771     // End of code for HTTP/0.9
772   } else {
773     // Ignore malformed data
774     uint8_t buf[4096];
775     int len;
776     uint64_t nread = 0;
777 
778     while ((len = this->_stream_io->read(buf, 4096)) > 0) {
779       nread += len;
780     }
781 
782     return nread;
783   }
784 }
785 
786 // FIXME: already defined somewhere?
787 static constexpr char http_1_1_version[] = "HTTP/1.1";
788 
789 // Convert HTTP/1.1 to HTTP/0.9
790 int64_t
_process_write_vio()791 Http09Transaction::_process_write_vio()
792 {
793   if (this->_write_vio.cont == nullptr || this->_write_vio.op == VIO::NONE) {
794     return 0;
795   }
796 
797   if (this->_thread != this_ethread()) {
798     SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
799     if (this->_cross_thread_event == nullptr) {
800       // Send to the right thread
801       this->_cross_thread_event = this->_thread->schedule_imm(this, VC_EVENT_WRITE_READY, nullptr);
802     }
803     return 0;
804   }
805 
806   SCOPED_MUTEX_LOCK(lock, this->_write_vio.mutex, this_ethread());
807 
808   IOBufferReader *reader = this->_write_vio.get_reader();
809 
810   if (this->_legacy_request) {
811     // This branch is for HTTP/0.9
812     int64_t http_1_1_version_len = sizeof(http_1_1_version) - 1;
813 
814     if (reader->is_read_avail_more_than(http_1_1_version_len) &&
815         memcmp(reader->start(), http_1_1_version, http_1_1_version_len) == 0) {
816       // Skip HTTP/1.1 response headers
817       IOBufferBlock *headers = reader->get_current_block();
818       int64_t headers_size   = headers->read_avail();
819       reader->consume(headers_size);
820       this->_write_vio.ndone += headers_size;
821     }
822 
823     // Write HTTP/1.1 response body
824     int64_t bytes_avail   = reader->read_avail();
825     int64_t total_written = 0;
826 
827     while (total_written < bytes_avail) {
828       int64_t data_len      = reader->block_read_avail();
829       int64_t bytes_written = this->_stream_io->write(reader, data_len);
830       if (bytes_written <= 0) {
831         break;
832       }
833 
834       reader->consume(bytes_written);
835       this->_write_vio.ndone += bytes_written;
836       total_written += bytes_written;
837     }
838 
839     // NOTE: When Chunked Transfer Coding is supported, check ChunkedState of ChunkedHandler
840     // is CHUNK_READ_DONE and set FIN flag
841     if (this->_write_vio.ntodo() == 0) {
842       // The size of respons to client
843       this->_stream_io->write_done();
844     }
845 
846     return total_written;
847   } else {
848     // nothing to do
849     return 0;
850   }
851 }
852