| /** @file |
| |
| A brief file description |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| /**************************************************************************** |
| |
| HttpSM_update.cc |
| |
| Description: |
| An HttpSM sub class for support scheduled update functionality |
| |
| |
| |
| ****************************************************************************/ |
| |
| #include "HttpUpdateSM.h" |
| #include "HttpDebugNames.h" |
| |
| ClassAllocator<HttpUpdateSM> httpUpdateSMAllocator("httpUpdateSMAllocator"); |
| |
| #define STATE_ENTER(state_name, event, vio) \ |
| { \ |
| Debug("http", "[%" PRId64 "] [%s, %s]", sm_id, #state_name, HttpDebugNames::get_event_name(event)); \ |
| } |
| |
| HttpUpdateSM::HttpUpdateSM() : cb_occured(false), cb_cont(nullptr), cb_action(), cb_event(HTTP_SCH_UPDATE_EVENT_ERROR) {} |
| |
| void |
| HttpUpdateSM::destroy() |
| { |
| cleanup(); |
| cb_action = nullptr; |
| httpUpdateSMAllocator.free(this); |
| } |
| |
| Action * |
| HttpUpdateSM::start_scheduled_update(Continuation *cont, HTTPHdr *request) |
| { |
| // Use passed continuation's mutex for this state machine |
| this->mutex = cont->mutex; |
| SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread()); |
| |
| // Set up the Action |
| cb_cont = cont; |
| cb_action = cont; |
| |
| start_sub_sm(); |
| |
| // Make a copy of the request we being asked to do |
| t_state.hdr_info.client_request.create(HTTP_TYPE_REQUEST); |
| t_state.hdr_info.client_request.copy(request); |
| |
| // Fix ME: What should these be set to since there is not a |
| // real client |
| ats_ip4_set(&t_state.client_info.src_addr, htonl(INADDR_LOOPBACK), 0); |
| t_state.backdoor_request = false; |
| t_state.client_info.port_attribute = HttpProxyPort::TRANSPORT_DEFAULT; |
| |
| t_state.req_flavor = HttpTransact::REQ_FLAVOR_SCHEDULED_UPDATE; |
| |
| // We always deallocate this later so initialize it down |
| http_parser_init(&http_parser); |
| |
| // We need to call state to add us to the http sm list |
| // but since we can terminate the state machine on this |
| // stack, do this by calling througth the main handler |
| // so the sm will be properly terminated |
| this->default_handler = &HttpUpdateSM::state_add_to_list; |
| this->handleEvent(EVENT_NONE, nullptr); |
| |
| if (cb_occured == 0) { |
| return &cb_action; |
| } else { |
| return ACTION_RESULT_DONE; |
| } |
| } |
| |
| void |
| HttpUpdateSM::handle_api_return() |
| { |
| switch (t_state.api_next_action) { |
| case HttpTransact::SM_ACTION_API_SM_START: |
| call_transact_and_set_next_state(&HttpTransact::ModifyRequest); |
| return; |
| case HttpTransact::SM_ACTION_API_SEND_RESPONSE_HDR: |
| // we have further processing to do |
| // based on what t_state.next_action is |
| break; |
| default: |
| HttpSM::handle_api_return(); |
| return; |
| } |
| |
| switch (t_state.next_action) { |
| case HttpTransact::SM_ACTION_TRANSFORM_READ: { |
| if (t_state.cache_info.transform_action == HttpTransact::CACHE_DO_WRITE) { |
| // Transform output cachable so initiate the transfer |
| // to the cache |
| HttpTunnelProducer *p = setup_transfer_from_transform_to_cache_only(); |
| tunnel.tunnel_run(p); |
| } else { |
| // We aren't caching the transformed response abort the |
| // transform |
| |
| Debug("http", |
| "[%" PRId64 "] [HttpUpdateSM] aborting " |
| "transform since result is not cached", |
| sm_id); |
| HttpTunnelConsumer *c = tunnel.get_consumer(transform_info.vc); |
| ink_release_assert(c != nullptr); |
| |
| if (tunnel.is_tunnel_active()) { |
| default_handler = &HttpUpdateSM::tunnel_handler; |
| if (c->alive == true) { |
| // We're still streaming data to read |
| // side of the transform so abort it |
| tunnel.handleEvent(VC_EVENT_ERROR, c->write_vio); |
| } else { |
| // The read side of the transform is done but |
| // the tunnel is still going, presumably streaming |
| // to the cache. Just change the handler and |
| // wait for the tunnel to complete |
| ink_assert(transform_info.entry->in_tunnel == false); |
| } |
| } else { |
| // tunnel is not active so caching the untransformed |
| // copy is done - bail out |
| ink_assert(transform_info.entry->in_tunnel == false); |
| terminate_sm = true; |
| } |
| } |
| break; |
| } |
| case HttpTransact::SM_ACTION_INTERNAL_CACHE_WRITE: |
| case HttpTransact::SM_ACTION_SERVER_READ: |
| case HttpTransact::SM_ACTION_INTERNAL_CACHE_NOOP: |
| case HttpTransact::SM_ACTION_SEND_ERROR_CACHE_NOOP: |
| case HttpTransact::SM_ACTION_SERVE_FROM_CACHE: { |
| cb_event = HTTP_SCH_UPDATE_EVENT_NOT_CACHED; |
| t_state.squid_codes.log_code = SQUID_LOG_TCP_MISS; |
| terminate_sm = true; |
| return; |
| } |
| |
| case HttpTransact::SM_ACTION_INTERNAL_CACHE_DELETE: |
| case HttpTransact::SM_ACTION_INTERNAL_CACHE_UPDATE_HEADERS: { |
| if (t_state.next_action == HttpTransact::SM_ACTION_INTERNAL_CACHE_DELETE) { |
| cb_event = HTTP_SCH_UPDATE_EVENT_DELETED; |
| } else { |
| cb_event = HTTP_SCH_UPDATE_EVENT_UPDATED; |
| } |
| |
| perform_cache_write_action(); |
| terminate_sm = true; |
| return; |
| } |
| |
| default: { |
| ink_release_assert(!"Should not get here"); |
| } |
| } |
| } |
| |
| void |
| HttpUpdateSM::set_next_state() |
| { |
| if (t_state.cache_info.action == HttpTransact::CACHE_DO_NO_ACTION || t_state.cache_info.action == HttpTransact::CACHE_DO_SERVE) { |
| if (t_state.next_action == HttpTransact::SM_ACTION_SERVE_FROM_CACHE) { |
| cb_event = HTTP_SCH_UPDATE_EVENT_NO_ACTION; |
| t_state.squid_codes.log_code = SQUID_LOG_TCP_HIT; |
| } else { |
| t_state.squid_codes.log_code = SQUID_LOG_TCP_MISS; |
| } |
| |
| terminate_sm = true; |
| ink_assert(tunnel.is_tunnel_active() == false); |
| return; |
| } |
| |
| HttpSM::set_next_state(); |
| } |
| |
| int |
| HttpUpdateSM::kill_this_async_hook(int event, void * /* data ATS_UNUSED */) |
| { |
| STATE_ENTER(&HttpUpdateSM::user_cb_handler, event, data); |
| |
| MUTEX_TRY_LOCK(lock, cb_action.mutex, this_ethread()); |
| |
| if (!lock.is_locked()) { |
| default_handler = (HttpSMHandler)&HttpUpdateSM::kill_this_async_hook; |
| eventProcessor.schedule_in(this, HRTIME_MSECONDS(10), ET_CALL); |
| return EVENT_DONE; |
| } |
| |
| if (!cb_action.cancelled) { |
| Debug("http", "[%" PRId64 "] [HttpUpdateSM] calling back user with event %s", sm_id, HttpDebugNames::get_event_name(cb_event)); |
| cb_cont->handleEvent(cb_event, nullptr); |
| } |
| |
| cb_occured = true; |
| |
| return HttpSM::kill_this_async_hook(EVENT_NONE, nullptr); |
| } |