• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

Http2ClientSession.cc

Go to the documentation of this file.
00001 /** @file
00002 
00003   Http2ClientSession.
00004 
00005   @section license License
00006 
00007   Licensed to the Apache Software Foundation (ASF) under one
00008   or more contributor license agreements.  See the NOTICE file
00009   distributed with this work for additional information
00010   regarding copyright ownership.  The ASF licenses this file
00011   to you under the Apache License, Version 2.0 (the
00012   "License"); you may not use this file except in compliance
00013   with the License.  You may obtain a copy of the License at
00014 
00015       http://www.apache.org/licenses/LICENSE-2.0
00016 
00017   Unless required by applicable law or agreed to in writing, software
00018   distributed under the License is distributed on an "AS IS" BASIS,
00019   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020   See the License for the specific language governing permissions and
00021   limitations under the License.
00022  */
00023 
00024 #include "Http2ClientSession.h"
00025 #include "HttpDebugNames.h"
00026 
00027 #define STATE_ENTER(state_name, event) do { \
00028   DebugSsn(this, "http2_cs", "[%" PRId64 "] [%s, %s]", this->connection_id(), \
00029     #state_name, HttpDebugNames::get_event_name(event)); \
00030 } while (0)
00031 
00032 #define DebugHttp2Ssn(fmt, ...) \
00033   DebugSsn(this, "http2_cs",  "[%" PRId64 "] " fmt, this->connection_id(), __VA_ARGS__)
00034 
00035 #define DebugHttp2Ssn0(msg) \
00036   DebugSsn(this, "http2_cs",  "[%" PRId64 "] " msg, this->connection_id())
00037 
00038 #define HTTP2_SET_SESSION_HANDLER(handler) do { \
00039   this->session_handler = (handler); \
00040 } while (0)
00041 
00042 ClassAllocator<Http2ClientSession> http2ClientSessionAllocator("http2ClientSessionAllocator");
00043 
00044 // memcpy the requested bytes from the IOBufferReader, returning how many were actually copied.
00045 static inline unsigned
00046 copy_from_buffer_reader(void * dst, IOBufferReader * reader, unsigned nbytes)
00047 {
00048     char * end;
00049 
00050     end = reader->memcpy(dst, nbytes, 0 /* offset */);
00051     return end - (char *)dst;
00052 }
00053 
00054 static int
00055 send_connection_event(Continuation * cont, int event, void * edata)
00056 {
00057   MUTEX_LOCK(lock, cont->mutex, this_ethread());
00058   return cont->handleEvent(event, edata);
00059 }
00060 
00061 Http2ClientSession::Http2ClientSession()
00062   : con_id(0), client_vc(NULL), read_buffer(NULL), sm_reader(NULL), write_buffer(NULL), sm_writer(NULL)
00063 {
00064 }
00065 
00066 void
00067 Http2ClientSession::destroy()
00068 {
00069   DebugHttp2Ssn0("session destroy");
00070 
00071   ink_release_assert(this->client_vc == NULL);
00072 
00073   free_MIOBuffer(this->read_buffer);
00074   ProxyClientSession::cleanup();
00075   http2ClientSessionAllocator.free(this);
00076 }
00077 
00078 void
00079 Http2ClientSession::start()
00080 {
00081   VIO * read_vio;
00082 
00083   MUTEX_LOCK(lock, this->mutex, this_ethread());
00084 
00085   SET_HANDLER(&Http2ClientSession::main_event_handler);
00086   HTTP2_SET_SESSION_HANDLER(&Http2ClientSession::state_read_connection_preface);
00087 
00088   read_vio = this->do_io_read(this, INT64_MAX, this->read_buffer);
00089   this->do_io_write(this, INT64_MAX, this->sm_writer);
00090 
00091   // 3.5 HTTP/2 Connection Preface. Upon establishment of a TCP connection and
00092   // determination that HTTP/2 will be used by both peers, each endpoint MUST
00093   // send a connection preface as a final confirmation ...
00094   //this->write_buffer->write(HTTP2_CONNECTION_PREFACE, HTTP2_CONNECTION_PREFACE_LEN);
00095 
00096   send_connection_event(&this->connection_state, HTTP2_SESSION_EVENT_INIT, this);
00097   this->handleEvent(VC_EVENT_READ_READY, read_vio);
00098 }
00099 
00100 void
00101 Http2ClientSession::new_connection(NetVConnection * new_vc, MIOBuffer * iobuf, IOBufferReader * reader, bool backdoor)
00102 {
00103   // HTTP/2 for the backdoor connections? Let's not deal woth that yet.
00104   ink_release_assert(backdoor == false);
00105 
00106   // Unique client session identifier.
00107   this->con_id = ProxyClientSession::next_connection_id();
00108   this->client_vc = new_vc;
00109   this->mutex = new_vc->mutex;
00110 
00111   this->connection_state.mutex = new_ProxyMutex();
00112 
00113   DebugHttp2Ssn("session born, netvc %p", this->client_vc);
00114 
00115   this->read_buffer = iobuf ? iobuf : new_MIOBuffer(HTTP2_HEADER_BUFFER_SIZE_INDEX);
00116   this->sm_reader = reader ? reader : this->read_buffer->alloc_reader();
00117 
00118   this->write_buffer = new_MIOBuffer(HTTP2_HEADER_BUFFER_SIZE_INDEX);
00119   this->sm_writer = this->write_buffer->alloc_reader();
00120 
00121   do_api_callout(TS_HTTP_SSN_START_HOOK);
00122 }
00123 
00124 VIO *
00125 Http2ClientSession::do_io_read(Continuation * c, int64_t nbytes, MIOBuffer * buf)
00126 {
00127   return this->client_vc->do_io_read(c, nbytes, buf);
00128 }
00129 
00130 VIO *
00131 Http2ClientSession::do_io_write(Continuation * c, int64_t nbytes, IOBufferReader * buf, bool owner)
00132 {
00133   return this->client_vc->do_io_write(c, nbytes, buf, owner);
00134 }
00135 
00136 void
00137 Http2ClientSession::do_io_shutdown(ShutdownHowTo_t howto)
00138 {
00139   this->client_vc->do_io_shutdown(howto);
00140 }
00141 
00142 // XXX Currently, we don't have a half-closed state, but we will need to implement that. After we send a GOAWAY, there
00143 // are scenarios where we would like to complete the outstanding streams.
00144 
00145 void
00146 Http2ClientSession::do_io_close(int alerrno)
00147 {
00148   DebugHttp2Ssn0("session closed");
00149 
00150   send_connection_event(&this->connection_state, HTTP2_SESSION_EVENT_FINI, this);
00151 
00152   this->client_vc->do_io_close(alerrno);
00153   this->client_vc = NULL;
00154 
00155   do_api_callout(TS_HTTP_SSN_CLOSE_HOOK);
00156 }
00157 
00158 void
00159 Http2ClientSession::reenable(VIO * vio)
00160 {
00161   this->client_vc->reenable(vio);
00162 }
00163 
00164 int
00165 Http2ClientSession::main_event_handler(int event, void * edata)
00166 {
00167   ink_assert(this->mutex->thread_holding == this_ethread());
00168 
00169   switch (event) {
00170   case VC_EVENT_READ_COMPLETE:
00171   case VC_EVENT_READ_READY:
00172     return (this->*session_handler)(event, edata);
00173 
00174   case HTTP2_SESSION_EVENT_XMIT: {
00175     Http2Frame * frame = (Http2Frame *)edata;
00176     frame->xmit(this->write_buffer);
00177     return 0;
00178   }
00179 
00180   case VC_EVENT_ACTIVE_TIMEOUT:
00181   case VC_EVENT_INACTIVITY_TIMEOUT:
00182   case VC_EVENT_ERROR:
00183   case VC_EVENT_EOS:
00184     this->do_io_close();
00185     return 0;
00186 
00187   case VC_EVENT_WRITE_COMPLETE:
00188   case VC_EVENT_WRITE_READY:
00189     return 0;
00190 
00191   default:
00192     DebugHttp2Ssn("unexpected event=%d edata=%p", event, edata);
00193     ink_release_assert(0);
00194     return 0;
00195   }
00196 
00197 }
00198 
00199 int
00200 Http2ClientSession::state_read_connection_preface(int event, void * edata)
00201 {
00202   VIO * vio = (VIO *)edata;
00203 
00204   STATE_ENTER(&Http2ClientSession::state_read_connection_preface, event);
00205   ink_assert(event == VC_EVENT_READ_COMPLETE || event == VC_EVENT_READ_READY);
00206 
00207   if (this->sm_reader->read_avail() >= (int64_t)HTTP2_CONNECTION_PREFACE_LEN) {
00208     char buf[HTTP2_CONNECTION_PREFACE_LEN];
00209     unsigned nbytes;
00210 
00211     nbytes = copy_from_buffer_reader(buf, this->sm_reader, sizeof(buf));
00212     ink_release_assert(nbytes == HTTP2_CONNECTION_PREFACE_LEN);
00213 
00214     if (memcmp(HTTP2_CONNECTION_PREFACE, buf, nbytes) != 0) {
00215       DebugHttp2Ssn0("invalid connection preface");
00216       this->do_io_close();
00217       return 0;
00218     }
00219 
00220     DebugHttp2Ssn0("received connection preface");
00221     this->sm_reader->consume(nbytes);
00222     HTTP2_SET_SESSION_HANDLER(&Http2ClientSession::state_start_frame_read);
00223 
00224     // XXX set activity timeouts ...
00225 
00226     // XXX start the write VIO ...
00227 
00228     // If we have unconsumed data, start tranferring frames now.
00229     if (this->sm_reader->is_read_avail_more_than(0)) {
00230       return this->handleEvent(VC_EVENT_READ_READY, vio);
00231     }
00232   }
00233 
00234   // XXX We don't have enough data to check the connection preface. We should reset the accept inactivity
00235   // timeout. We should have a maximum timeout to get the session started though.
00236 
00237   vio->reenable();
00238   return 0;
00239 }
00240 
00241 int
00242 Http2ClientSession::state_start_frame_read(int event, void * edata)
00243 {
00244   VIO * vio = (VIO *)edata;
00245 
00246   STATE_ENTER(&Http2ClientSession::state_start_frame_read, event);
00247   ink_assert(event == VC_EVENT_READ_COMPLETE || event == VC_EVENT_READ_READY);
00248 
00249   if (this->sm_reader->read_avail() >= (int64_t)HTTP2_FRAME_HEADER_LEN) {
00250     uint8_t buf[HTTP2_FRAME_HEADER_LEN];
00251     unsigned nbytes;
00252 
00253     DebugHttp2Ssn0("receiving frame header");
00254     nbytes = copy_from_buffer_reader(buf, this->sm_reader, sizeof(buf));
00255 
00256     if (!http2_parse_frame_header(make_iovec(buf), this->current_hdr)) {
00257       DebugHttp2Ssn0("frame header parse failure");
00258       this->do_io_close();
00259       return 0;
00260     }
00261 
00262     DebugHttp2Ssn("frame header length=%u, type=%u, flags=0x%x, streamid=%u",
00263         (unsigned)this->current_hdr.length, (unsigned)this->current_hdr.type,
00264         (unsigned)this->current_hdr.flags, this->current_hdr.streamid);
00265 
00266     this->sm_reader->consume(nbytes);
00267 
00268     if (!http2_frame_header_is_valid(this->current_hdr)) {
00269       // XXX nuke it with HTTP2_ERROR_PROTOCOL_ERROR!
00270     }
00271 
00272     // If we know up front that the payload is too long, nuke this connection.
00273     if (this->current_hdr.length > HTTP2_MAX_FRAME_PAYLOAD) {
00274       // XXX nuke it with HTTP2_ERROR_FRAME_SIZE_ERROR!
00275     }
00276 
00277     if (!http2_is_client_streamid(this->current_hdr.streamid)) {
00278       // XXX nuke it with HTTP2_ERROR_PROTOCOL_ERROR!
00279     }
00280 
00281     HTTP2_SET_SESSION_HANDLER(&Http2ClientSession::state_complete_frame_read);
00282     if (this->sm_reader->read_avail() >= this->current_hdr.length) {
00283       return this->handleEvent(VC_EVENT_READ_READY, vio);
00284     }
00285   }
00286 
00287   vio->reenable();
00288   return 0;
00289 }
00290 
00291 int
00292 Http2ClientSession::state_complete_frame_read(int event, void * edata)
00293 {
00294   VIO * vio = (VIO *)edata;
00295 
00296   STATE_ENTER(&Http2ClientSession::state_complete_frame_read, event);
00297   ink_assert(event == VC_EVENT_READ_COMPLETE || event == VC_EVENT_READ_READY);
00298 
00299   if (this->sm_reader->read_avail() < this->current_hdr.length) {
00300     vio->reenable();
00301     return 0;
00302   }
00303 
00304   DebugHttp2Ssn("completed frame read, %" PRId64 " bytes available", this->sm_reader->read_avail());
00305 
00306   // XXX parse the frame and handle it ...
00307 
00308   Http2Frame frame(this->current_hdr, this->sm_reader);
00309 
00310   send_connection_event(&this->connection_state, HTTP2_SESSION_EVENT_RECV, &frame);
00311   this->sm_reader->consume(this->current_hdr.length);
00312 
00313   HTTP2_SET_SESSION_HANDLER(&Http2ClientSession::state_start_frame_read);
00314   if (this->sm_reader->is_read_avail_more_than(0)) {
00315     return this->handleEvent(VC_EVENT_READ_READY, vio);
00316   }
00317 
00318   vio->reenable();
00319   return 0;
00320 }

Generated by  doxygen 1.7.1