| /** @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. |
| */ |
| |
| #include <sys/types.h> |
| #include <netinet/in.h> |
| #include "tscore/ink_defs.h" |
| |
| #include "TxnSM.h" |
| |
| extern TSTextLogObject protocol_plugin_log; |
| |
| /* Fix me: currently, tunnelling server_response from OS to both cache and |
| client doesn't work for client_vc. So write data first to cache and then |
| write cached data to client. */ |
| |
| /* static functions */ |
| int main_handler(TSCont contp, TSEvent event, void *data); |
| |
| /* functions for clients */ |
| int state_start(TSCont contp, TSEvent event, void *data); |
| int state_interface_with_client(TSCont contp, TSEvent event, TSVIO vio); |
| int state_read_request_from_client(TSCont contp, TSEvent event, TSVIO vio); |
| int state_send_response_to_client(TSCont contp, TSEvent event, TSVIO vio); |
| |
| /* functions for cache operation */ |
| int state_handle_cache_lookup(TSCont contp, TSEvent event, TSVConn vc); |
| int state_handle_cache_read_response(TSCont contp, TSEvent event, TSVIO vio); |
| int state_handle_cache_prepare_for_write(TSCont contp, TSEvent event, TSVConn vc); |
| int state_write_to_cache(TSCont contp, TSEvent event, TSVIO vio); |
| |
| /* functions for servers */ |
| int state_build_and_send_request(TSCont contp, TSEvent event, void *data); |
| int state_dns_lookup(TSCont contp, TSEvent event, TSHostLookupResult host_info); |
| int state_connect_to_server(TSCont contp, TSEvent event, TSVConn vc); |
| int state_interface_with_server(TSCont contp, TSEvent event, TSVIO vio); |
| int state_send_request_to_server(TSCont contp, TSEvent event, TSVIO vio); |
| int state_read_response_from_server(TSCont contp, TSEvent event, TSVIO vio); |
| |
| /* misc functions */ |
| int state_done(TSCont contp, TSEvent event, TSVIO vio); |
| |
| int send_response_to_client(TSCont contp); |
| int prepare_to_die(TSCont contp); |
| |
| char *get_info_from_buffer(TSIOBufferReader the_reader); |
| int is_request_end(char *buf); |
| int parse_request(char *request, char *server_name, char *file_name); |
| TSCacheKey CacheKeyCreate(char *file_name); |
| |
| /* Continuation handler is a function pointer, this function |
| is to assign the continuation handler to a specific function. */ |
| int |
| main_handler(TSCont contp, TSEvent event, void *data) |
| { |
| TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); |
| TxnSMHandler q_current_handler = txn_sm->q_current_handler; |
| |
| TSDebug(PLUGIN_NAME, "main_handler (contp %p event %d)", contp, event); |
| |
| /* handle common cases errors */ |
| if (event == TS_EVENT_ERROR) { |
| return prepare_to_die(contp); |
| } |
| |
| if (q_current_handler != (TxnSMHandler)&state_interface_with_server) { |
| if (event == TS_EVENT_VCONN_EOS) { |
| return prepare_to_die(contp); |
| } |
| } |
| |
| TSDebug(PLUGIN_NAME, "current_handler (%p)", q_current_handler); |
| |
| return (*q_current_handler)(contp, event, data); |
| } |
| |
| /* Create the Txn data structure and the continuation for the Txn. */ |
| TSCont |
| TxnSMCreate(TSMutex pmutex, TSVConn client_vc, int server_port) |
| { |
| TSCont contp; |
| TxnSM *txn_sm; |
| |
| txn_sm = (TxnSM *)TSmalloc(sizeof(TxnSM)); |
| |
| txn_sm->q_mutex = pmutex; |
| txn_sm->q_pending_action = NULL; |
| |
| /* Txn will use this server port to connect to the origin server. */ |
| txn_sm->q_server_port = server_port; |
| /* The client_vc is returned by TSNetAccept, refer to Protocol.c. */ |
| txn_sm->q_client_vc = client_vc; |
| /* The server_vc will be created if Txn connects to the origin server. */ |
| txn_sm->q_server_vc = NULL; |
| |
| txn_sm->q_client_read_vio = NULL; |
| txn_sm->q_client_write_vio = NULL; |
| txn_sm->q_client_request_buffer = NULL; |
| txn_sm->q_client_response_buffer = NULL; |
| txn_sm->q_client_request_buffer_reader = NULL; |
| txn_sm->q_client_response_buffer_reader = NULL; |
| |
| txn_sm->q_server_read_vio = NULL; |
| txn_sm->q_server_write_vio = NULL; |
| txn_sm->q_server_request_buffer = NULL; |
| txn_sm->q_server_response_buffer = NULL; |
| txn_sm->q_server_request_buffer_reader = NULL; |
| |
| /* Char buffers to store client request and server response. */ |
| txn_sm->q_client_request = (char *)TSmalloc(sizeof(char) * (MAX_REQUEST_LENGTH + 1)); |
| memset(txn_sm->q_client_request, '\0', (sizeof(char) * (MAX_REQUEST_LENGTH + 1))); |
| txn_sm->q_server_response = NULL; |
| txn_sm->q_server_response_length = 0; |
| txn_sm->q_block_bytes_read = 0; |
| txn_sm->q_cache_vc = NULL; |
| txn_sm->q_cache_response_length = 0; |
| txn_sm->q_cache_read_buffer = NULL; |
| txn_sm->q_cache_read_buffer_reader = NULL; |
| |
| txn_sm->q_server_name = (char *)TSmalloc(sizeof(char) * (MAX_SERVER_NAME_LENGTH + 1)); |
| txn_sm->q_file_name = (char *)TSmalloc(sizeof(char) * (MAX_FILE_NAME_LENGTH + 1)); |
| |
| txn_sm->q_key = NULL; |
| txn_sm->q_magic = TXN_SM_ALIVE; |
| |
| /* Set the current handler to be state_start. */ |
| set_handler(txn_sm->q_current_handler, &state_start); |
| |
| contp = TSContCreate(main_handler, txn_sm->q_mutex); |
| TSContDataSet(contp, txn_sm); |
| return contp; |
| } |
| |
| /* This function starts to read incoming client request data from client_vc */ |
| int |
| state_start(TSCont contp, TSEvent event ATS_UNUSED, void *data ATS_UNUSED) |
| { |
| TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); |
| |
| if (!txn_sm->q_client_vc) { |
| return prepare_to_die(contp); |
| } |
| |
| txn_sm->q_client_request_buffer = TSIOBufferCreate(); |
| if (!txn_sm->q_client_request_buffer) { |
| return prepare_to_die(contp); |
| } |
| txn_sm->q_client_request_buffer_reader = TSIOBufferReaderAlloc(txn_sm->q_client_request_buffer); |
| if (!txn_sm->q_client_request_buffer_reader) { |
| return prepare_to_die(contp); |
| } |
| |
| /* Now the IOBuffer and IOBufferReader is ready, the data from |
| client_vc can be read into the IOBuffer. Since we don't know |
| the size of the client request, set the expecting size to be |
| INT64_MAX, so that we will always get TS_EVENT_VCONN_READ_READY |
| event, but never TS_EVENT_VCONN_READ_COMPLETE event. */ |
| set_handler(txn_sm->q_current_handler, (TxnSMHandler)&state_interface_with_client); |
| txn_sm->q_client_read_vio = TSVConnRead(txn_sm->q_client_vc, (TSCont)contp, txn_sm->q_client_request_buffer, INT64_MAX); |
| |
| return TS_SUCCESS; |
| } |
| |
| /* This function is to call proper functions according to the |
| VIO argument. If it's read_vio, which means reading request from |
| client_vc, call state_read_request_from_client. If it's write_vio, |
| which means sending response to client_vc, call |
| state_send_response_to_client. If the event is TS_EVENT_VCONN_EOS, |
| which means the client closed socket and thus implies the client |
| drop all jobs between TxnSM and the client, so go to die. */ |
| int |
| state_interface_with_client(TSCont contp, TSEvent event, TSVIO vio) |
| { |
| TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); |
| |
| TSDebug(PLUGIN_NAME, "enter state_interface_with_client"); |
| |
| txn_sm->q_pending_action = NULL; |
| |
| if (vio == txn_sm->q_client_read_vio) { |
| return state_read_request_from_client(contp, event, vio); |
| } |
| |
| /* vio == txn_sm->q_client_write_vio */ |
| return state_send_response_to_client(contp, event, vio); |
| } |
| |
| /* Data is read from client_vc, if all data for the request is in, |
| parse it and do cache lookup. */ |
| int |
| state_read_request_from_client(TSCont contp, TSEvent event, TSVIO vio ATS_UNUSED) |
| { |
| int bytes_read, parse_result; |
| char *temp_buf; |
| |
| TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); |
| |
| TSDebug(PLUGIN_NAME, "enter state_read_request_from_client"); |
| |
| switch (event) { |
| case TS_EVENT_VCONN_READ_READY: |
| bytes_read = TSIOBufferReaderAvail(txn_sm->q_client_request_buffer_reader); |
| |
| if (bytes_read > 0) { |
| temp_buf = (char *)get_info_from_buffer(txn_sm->q_client_request_buffer_reader); |
| TSstrlcat(txn_sm->q_client_request, temp_buf, MAX_REQUEST_LENGTH + 1); |
| TSfree(temp_buf); |
| |
| /* Check if the request is fully read, if so, do cache lookup. */ |
| if (strstr(txn_sm->q_client_request, "\r\n\r\n") != NULL) { |
| temp_buf = (char *)TSmalloc(sizeof(char) * (strlen(txn_sm->q_client_request) + 1)); |
| memcpy(temp_buf, txn_sm->q_client_request, strlen(txn_sm->q_client_request)); |
| temp_buf[strlen(txn_sm->q_client_request)] = '\0'; |
| parse_result = parse_request(temp_buf, txn_sm->q_server_name, txn_sm->q_file_name); |
| TSfree(temp_buf); |
| |
| if (parse_result != 1) { |
| return prepare_to_die(contp); |
| } |
| |
| /* Start to do cache lookup */ |
| TSDebug(PLUGIN_NAME, "Key material: file name is %s*****", txn_sm->q_file_name); |
| txn_sm->q_key = (TSCacheKey)CacheKeyCreate(txn_sm->q_file_name); |
| |
| set_handler(txn_sm->q_current_handler, (TxnSMHandler)&state_handle_cache_lookup); |
| txn_sm->q_pending_action = TSCacheRead(contp, txn_sm->q_key); |
| |
| return TS_SUCCESS; |
| } |
| } |
| |
| /* The request is not fully read, reenable the read_vio. */ |
| TSVIOReenable(txn_sm->q_client_read_vio); |
| break; |
| |
| default: /* Shouldn't get here, prepare to die. */ |
| return prepare_to_die(contp); |
| } |
| return TS_SUCCESS; |
| } |
| |
| /* This function handle the cache lookup result. If MISS, try to |
| open cache write_vc for writing. Otherwise, use the vc returned |
| by the cache to read the data from the cache. */ |
| int |
| state_handle_cache_lookup(TSCont contp, TSEvent event, TSVConn vc) |
| { |
| TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); |
| int64_t response_size; |
| int ret_val; |
| |
| TSDebug(PLUGIN_NAME, "enter state_handle_cache_lookup"); |
| |
| switch (event) { |
| case TS_EVENT_CACHE_OPEN_READ: |
| TSDebug(PLUGIN_NAME, "cache hit!!!"); |
| /* Cache hit. */ |
| |
| /* Write log */ |
| ret_val = TSTextLogObjectWrite(protocol_plugin_log, "%s %s %d \n", txn_sm->q_file_name, txn_sm->q_server_name, 1); |
| if (ret_val != TS_SUCCESS) { |
| TSError("[%s] Fail to write into log", PLUGIN_NAME); |
| } |
| |
| txn_sm->q_cache_vc = vc; |
| txn_sm->q_pending_action = NULL; |
| |
| /* Get the size of the cached doc. */ |
| response_size = TSVConnCacheObjectSizeGet(txn_sm->q_cache_vc); |
| |
| /* Allocate IOBuffer to store data from the cache. */ |
| txn_sm->q_client_response_buffer = TSIOBufferCreate(); |
| if (!txn_sm->q_client_response_buffer) { |
| return prepare_to_die(contp); |
| } |
| txn_sm->q_client_response_buffer_reader = TSIOBufferReaderAlloc(txn_sm->q_client_response_buffer); |
| if (!txn_sm->q_client_response_buffer_reader) { |
| return prepare_to_die(contp); |
| } |
| txn_sm->q_cache_read_buffer = TSIOBufferCreate(); |
| if (!txn_sm->q_cache_read_buffer) { |
| return prepare_to_die(contp); |
| } |
| txn_sm->q_cache_read_buffer_reader = TSIOBufferReaderAlloc(txn_sm->q_cache_read_buffer); |
| if (!txn_sm->q_cache_read_buffer_reader) { |
| return prepare_to_die(contp); |
| } |
| |
| /* Read doc from the cache. */ |
| set_handler(txn_sm->q_current_handler, (TxnSMHandler)&state_handle_cache_read_response); |
| txn_sm->q_cache_read_vio = TSVConnRead(txn_sm->q_cache_vc, contp, txn_sm->q_cache_read_buffer, response_size); |
| |
| break; |
| |
| case TS_EVENT_CACHE_OPEN_READ_FAILED: |
| /* Cache miss or error, open cache write_vc. */ |
| TSDebug(PLUGIN_NAME, "cache miss or error!!!"); |
| /* Write log */ |
| ret_val = TSTextLogObjectWrite(protocol_plugin_log, "%s %s %d \n", txn_sm->q_file_name, txn_sm->q_server_name, 0); |
| |
| if (ret_val != TS_SUCCESS) { |
| TSError("[%s] Fail to write into log", PLUGIN_NAME); |
| } |
| |
| set_handler(txn_sm->q_current_handler, (TxnSMHandler)&state_handle_cache_prepare_for_write); |
| txn_sm->q_pending_action = TSCacheWrite(contp, txn_sm->q_key); |
| break; |
| |
| default: |
| /* unknown event, abort transaction */ |
| return prepare_to_die(contp); |
| } |
| |
| return TS_SUCCESS; |
| } |
| |
| static void |
| load_buffer_cache_data(TxnSM *txn_sm) |
| { |
| /* transfer the data from the cache buffer (which must |
| fully be consumed on a VCONN_READY event, to the |
| server response buffer */ |
| int rdr_avail = TSIOBufferReaderAvail(txn_sm->q_cache_read_buffer_reader); |
| |
| TSDebug(PLUGIN_NAME, "entering buffer_cache_data"); |
| TSDebug(PLUGIN_NAME, "loading %d bytes to buffer reader", rdr_avail); |
| |
| TSAssert(rdr_avail > 0); |
| |
| TSIOBufferCopy(txn_sm->q_client_response_buffer, /* (cache response buffer) */ |
| txn_sm->q_cache_read_buffer_reader, /* (transient buffer) */ |
| rdr_avail, 0); |
| |
| TSIOBufferReaderConsume(txn_sm->q_cache_read_buffer_reader, rdr_avail); |
| } |
| |
| /* If the document is fully read out of the cache, close the |
| cache read_vc, send the document to the client. Otherwise, |
| reenable the read_vio to read more data out. If some error |
| occurs, close the read_vc, open write_vc for writing the doc |
| into the cache.*/ |
| int |
| state_handle_cache_read_response(TSCont contp, TSEvent event, TSVIO vio ATS_UNUSED) |
| { |
| TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); |
| |
| TSDebug(PLUGIN_NAME, "enter state_handle_cache_read_response"); |
| |
| txn_sm->q_pending_action = NULL; |
| |
| switch (event) { |
| case TS_EVENT_VCONN_READ_COMPLETE: |
| load_buffer_cache_data(txn_sm); |
| TSVConnClose(txn_sm->q_cache_vc); |
| txn_sm->q_cache_vc = NULL; |
| txn_sm->q_cache_read_vio = NULL; |
| txn_sm->q_cache_write_vio = NULL; |
| TSIOBufferReaderFree(txn_sm->q_cache_read_buffer_reader); |
| TSIOBufferDestroy(txn_sm->q_cache_read_buffer); |
| txn_sm->q_cache_read_buffer_reader = NULL; |
| txn_sm->q_cache_read_buffer = NULL; |
| return send_response_to_client(contp); |
| |
| case TS_EVENT_VCONN_READ_READY: |
| load_buffer_cache_data(txn_sm); |
| |
| TSVIOReenable(txn_sm->q_cache_read_vio); |
| break; |
| |
| default: |
| /* Error */ |
| if (txn_sm->q_cache_vc) { |
| TSVConnClose(txn_sm->q_cache_vc); |
| txn_sm->q_cache_vc = NULL; |
| txn_sm->q_cache_read_vio = NULL; |
| txn_sm->q_cache_write_vio = NULL; |
| } |
| |
| /* Open the write_vc, after getting doc from the origin server, |
| write the doc into the cache. */ |
| set_handler(txn_sm->q_current_handler, (TxnSMHandler)&state_handle_cache_prepare_for_write); |
| TSAssert(txn_sm->q_pending_action == NULL); |
| txn_sm->q_pending_action = TSCacheWrite(contp, txn_sm->q_key); |
| break; |
| } |
| return TS_SUCCESS; |
| } |
| |
| /* The cache processor call us back with the vc to use for writing |
| data into the cache. |
| In case of error, abort txn. */ |
| int |
| state_handle_cache_prepare_for_write(TSCont contp, TSEvent event, TSVConn vc) |
| { |
| TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); |
| |
| TSDebug(PLUGIN_NAME, "enter state_handle_cache_prepare_for_write"); |
| |
| txn_sm->q_pending_action = NULL; |
| |
| switch (event) { |
| case TS_EVENT_CACHE_OPEN_WRITE: |
| txn_sm->q_cache_vc = vc; |
| break; |
| default: |
| TSError("[%s] Can't open cache write_vc, aborting txn", PLUGIN_NAME); |
| txn_sm->q_cache_vc = NULL; |
| return prepare_to_die(contp); |
| break; |
| } |
| return state_build_and_send_request(contp, 0, NULL); |
| } |
| |
| /* Cache miss or error case. Start the process to send the request |
| the origin server. */ |
| int |
| state_build_and_send_request(TSCont contp, TSEvent event ATS_UNUSED, void *data ATS_UNUSED) |
| { |
| TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); |
| |
| TSDebug(PLUGIN_NAME, "enter state_build_and_send_request"); |
| |
| txn_sm->q_pending_action = NULL; |
| |
| txn_sm->q_server_request_buffer = TSIOBufferCreate(); |
| if (!txn_sm->q_server_request_buffer) { |
| return prepare_to_die(contp); |
| } |
| txn_sm->q_server_request_buffer_reader = TSIOBufferReaderAlloc(txn_sm->q_server_request_buffer); |
| if (!txn_sm->q_server_request_buffer_reader) { |
| return prepare_to_die(contp); |
| } |
| txn_sm->q_server_response_buffer = TSIOBufferCreate(); |
| if (!txn_sm->q_server_response_buffer) { |
| return prepare_to_die(contp); |
| } |
| txn_sm->q_cache_response_buffer_reader = TSIOBufferReaderAlloc(txn_sm->q_server_response_buffer); |
| if (!txn_sm->q_cache_response_buffer_reader) { |
| return prepare_to_die(contp); |
| } |
| |
| /* Marshal request */ |
| TSIOBufferWrite(txn_sm->q_server_request_buffer, txn_sm->q_client_request, strlen(txn_sm->q_client_request)); |
| |
| /* First thing to do is to get the server IP from the server host name. */ |
| set_handler(txn_sm->q_current_handler, (TxnSMHandler)&state_dns_lookup); |
| TSAssert(txn_sm->q_pending_action == NULL); |
| txn_sm->q_pending_action = TSHostLookup(contp, txn_sm->q_server_name, strlen(txn_sm->q_server_name)); |
| |
| TSAssert(txn_sm->q_pending_action); |
| TSDebug(PLUGIN_NAME, "initiating host lookup"); |
| |
| return TS_SUCCESS; |
| } |
| |
| /* If Host lookup is successfully, connect to that IP. */ |
| int |
| state_dns_lookup(TSCont contp, TSEvent event, TSHostLookupResult host_info) |
| { |
| TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); |
| struct sockaddr const *q_server_addr; |
| struct sockaddr_in ip_addr; |
| |
| TSDebug(PLUGIN_NAME, "enter state_dns_lookup"); |
| |
| /* Can't find the server IP. */ |
| if (event != TS_EVENT_HOST_LOOKUP || !host_info) { |
| return prepare_to_die(contp); |
| } |
| txn_sm->q_pending_action = NULL; |
| |
| /* Get the server IP from data structure TSHostLookupResult. */ |
| q_server_addr = TSHostLookupResultAddrGet(host_info); |
| |
| /* Connect to the server using its IP. */ |
| set_handler(txn_sm->q_current_handler, (TxnSMHandler)&state_connect_to_server); |
| TSAssert(txn_sm->q_pending_action == NULL); |
| TSAssert(q_server_addr->sa_family == AF_INET); /* NO IPv6 in this plugin */ |
| |
| memcpy(&ip_addr, q_server_addr, sizeof(ip_addr)); |
| ip_addr.sin_port = txn_sm->q_server_port; |
| txn_sm->q_pending_action = TSNetConnect(contp, (struct sockaddr const *)&ip_addr); |
| |
| return TS_SUCCESS; |
| } |
| |
| /* Net Processor calls back, if succeeded, the net_vc is returned. |
| Note here, even if the event is TS_EVENT_NET_CONNECT, it doesn't |
| mean the net connection is set up because TSNetConnect is non-blocking. |
| Do VConnWrite to the net_vc, if fails, that means there is no net |
| connection. */ |
| int |
| state_connect_to_server(TSCont contp, TSEvent event, TSVConn vc) |
| { |
| TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); |
| |
| TSDebug(PLUGIN_NAME, "enter state_connect_to_server"); |
| |
| /* TSNetConnect failed. */ |
| if (event != TS_EVENT_NET_CONNECT) { |
| return prepare_to_die(contp); |
| } |
| txn_sm->q_pending_action = NULL; |
| |
| txn_sm->q_server_vc = vc; |
| |
| /* server_vc will be used to write request and read response. */ |
| set_handler(txn_sm->q_current_handler, (TxnSMHandler)&state_send_request_to_server); |
| |
| /* Actively write the request to the net_vc. */ |
| txn_sm->q_server_write_vio = |
| TSVConnWrite(txn_sm->q_server_vc, contp, txn_sm->q_server_request_buffer_reader, strlen(txn_sm->q_client_request)); |
| return TS_SUCCESS; |
| } |
| |
| /* Net Processor calls back, if write complete, wait for the response |
| coming in, otherwise, reenable the write_vio. */ |
| int |
| state_send_request_to_server(TSCont contp, TSEvent event, TSVIO vio) |
| { |
| TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); |
| |
| TSDebug(PLUGIN_NAME, "enter state_send_request_to_server"); |
| |
| switch (event) { |
| case TS_EVENT_VCONN_WRITE_READY: |
| TSVIOReenable(vio); |
| break; |
| case TS_EVENT_VCONN_WRITE_COMPLETE: |
| vio = NULL; |
| |
| /* Waiting for the incoming response. */ |
| set_handler(txn_sm->q_current_handler, (TxnSMHandler)&state_interface_with_server); |
| txn_sm->q_server_read_vio = TSVConnRead(txn_sm->q_server_vc, contp, txn_sm->q_server_response_buffer, INT64_MAX); |
| break; |
| |
| /* it could be failure of TSNetConnect */ |
| default: |
| return prepare_to_die(contp); |
| } |
| return TS_SUCCESS; |
| } |
| |
| /* Call correct handler according to the vio type. */ |
| int |
| state_interface_with_server(TSCont contp, TSEvent event, TSVIO vio) |
| { |
| TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); |
| |
| TSDebug(PLUGIN_NAME, "enter state_interface_with_server"); |
| |
| txn_sm->q_pending_action = NULL; |
| |
| switch (event) { |
| /* This is returned from cache_vc. */ |
| case TS_EVENT_VCONN_WRITE_READY: |
| case TS_EVENT_VCONN_WRITE_COMPLETE: |
| return state_write_to_cache(contp, event, vio); |
| /* Otherwise, handle events from server. */ |
| case TS_EVENT_VCONN_READ_READY: |
| /* Actually, we shouldn't get READ_COMPLETE because we set bytes |
| count to be INT64_MAX. */ |
| case TS_EVENT_VCONN_READ_COMPLETE: |
| return state_read_response_from_server(contp, event, vio); |
| |
| /* all data of the response come in. */ |
| case TS_EVENT_VCONN_EOS: |
| TSDebug(PLUGIN_NAME, "get server eos"); |
| /* There is no more use of server_vc, close it. */ |
| if (txn_sm->q_server_vc) { |
| TSVConnClose(txn_sm->q_server_vc); |
| txn_sm->q_server_vc = NULL; |
| } |
| txn_sm->q_server_read_vio = NULL; |
| txn_sm->q_server_write_vio = NULL; |
| |
| /* Check if the response is good */ |
| if (txn_sm->q_server_response_length == 0) { |
| /* This is the bad response. Close client_vc. */ |
| if (txn_sm->q_client_vc) { |
| TSVConnClose(txn_sm->q_client_vc); |
| txn_sm->q_client_vc = NULL; |
| } |
| txn_sm->q_client_read_vio = NULL; |
| txn_sm->q_client_write_vio = NULL; |
| |
| /* Close cache_vc as well. */ |
| if (txn_sm->q_cache_vc) { |
| TSVConnClose(txn_sm->q_cache_vc); |
| txn_sm->q_cache_vc = NULL; |
| } |
| txn_sm->q_cache_write_vio = NULL; |
| return state_done(contp, 0, NULL); |
| } |
| |
| if (txn_sm->q_cache_response_length >= txn_sm->q_server_response_length) { |
| /* Write is complete, close the cache_vc. */ |
| TSVConnClose(txn_sm->q_cache_vc); |
| txn_sm->q_cache_vc = NULL; |
| txn_sm->q_cache_write_vio = NULL; |
| TSIOBufferReaderFree(txn_sm->q_cache_response_buffer_reader); |
| |
| /* Open cache_vc to read data and send to client. */ |
| set_handler(txn_sm->q_current_handler, (TxnSMHandler)&state_handle_cache_lookup); |
| txn_sm->q_pending_action = TSCacheRead(contp, txn_sm->q_key); |
| } else { /* not done with writing into cache */ |
| |
| TSDebug(PLUGIN_NAME, "cache_response_length is %d, server response length is %d", txn_sm->q_cache_response_length, |
| txn_sm->q_server_response_length); |
| TSVIOReenable(txn_sm->q_cache_write_vio); |
| } |
| |
| default: |
| break; |
| } |
| |
| return TS_SUCCESS; |
| } |
| |
| /* The response comes in. If the origin server finishes writing, it |
| will close the socket, so the event returned from the net_vc is |
| TS_EVENT_VCONN_EOS. By this event, TxnSM knows all data of the |
| response arrives and so parse it, save a copy in the cache and |
| send the doc to the client. If reading is not done, reenable the |
| read_vio. */ |
| int |
| state_read_response_from_server(TSCont contp, TSEvent event ATS_UNUSED, TSVIO vio ATS_UNUSED) |
| { |
| TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); |
| int bytes_read = 0; |
| |
| TSDebug(PLUGIN_NAME, "enter state_read_response_from_server"); |
| |
| bytes_read = TSIOBufferReaderAvail(txn_sm->q_cache_response_buffer_reader); |
| |
| if ((bytes_read > 0) && (txn_sm->q_cache_vc)) { |
| /* If this is the first write, do TSVConnWrite, otherwise, simply |
| reenable q_cache_write_vio. */ |
| if (txn_sm->q_server_response_length == 0) { |
| txn_sm->q_cache_write_vio = TSVConnWrite(txn_sm->q_cache_vc, contp, txn_sm->q_cache_response_buffer_reader, bytes_read); |
| } else { |
| TSAssert(txn_sm->q_server_response_length > 0); |
| TSVIOReenable(txn_sm->q_cache_write_vio); |
| txn_sm->q_block_bytes_read = bytes_read; |
| /* |
| txn_sm->q_cache_write_vio = TSVConnWrite (txn_sm->q_cache_vc, |
| contp, |
| txn_sm->q_cache_response_buffer_reader, |
| bytes_read); |
| */ |
| } |
| } |
| |
| txn_sm->q_server_response_length += bytes_read; |
| TSDebug(PLUGIN_NAME, "bytes read is %d, total response length is %d", bytes_read, txn_sm->q_server_response_length); |
| |
| return TS_SUCCESS; |
| } |
| |
| /* If the whole doc has been written into the cache, send the response |
| to the client, otherwise, reenable the read_vio. */ |
| int |
| state_write_to_cache(TSCont contp, TSEvent event, TSVIO vio) |
| { |
| TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); |
| |
| TSDebug(PLUGIN_NAME, "enter state_write_to_cache"); |
| |
| switch (event) { |
| case TS_EVENT_VCONN_WRITE_READY: |
| TSVIOReenable(txn_sm->q_cache_write_vio); |
| return TS_SUCCESS; |
| |
| case TS_EVENT_VCONN_WRITE_COMPLETE: |
| TSDebug(PLUGIN_NAME, "nbytes %" PRId64 ", ndone %" PRId64, TSVIONBytesGet(vio), TSVIONDoneGet(vio)); |
| /* Since the first write is through TSVConnWrite, which already consume |
| the data in cache_buffer_reader, don't consume it again. */ |
| if (txn_sm->q_cache_response_length > 0 && txn_sm->q_block_bytes_read > 0) { |
| TSIOBufferReaderConsume(txn_sm->q_cache_response_buffer_reader, txn_sm->q_block_bytes_read); |
| } |
| |
| txn_sm->q_cache_response_length += TSVIONBytesGet(vio); |
| |
| /* If not all data have been read in, we have to reenable the read_vio */ |
| if (txn_sm->q_server_vc != NULL) { |
| TSDebug(PLUGIN_NAME, "re-enable server_read_vio"); |
| TSVIOReenable(txn_sm->q_server_read_vio); |
| return TS_SUCCESS; |
| } |
| |
| if (txn_sm->q_cache_response_length >= txn_sm->q_server_response_length) { |
| /* Write is complete, close the cache_vc. */ |
| TSDebug(PLUGIN_NAME, "close cache_vc, cache_response_length is %d, server_response_length is %d", |
| txn_sm->q_cache_response_length, txn_sm->q_server_response_length); |
| TSVConnClose(txn_sm->q_cache_vc); |
| txn_sm->q_cache_vc = NULL; |
| txn_sm->q_cache_write_vio = NULL; |
| TSIOBufferReaderFree(txn_sm->q_cache_response_buffer_reader); |
| |
| /* Open cache_vc to read data and send to client. */ |
| set_handler(txn_sm->q_current_handler, (TxnSMHandler)&state_handle_cache_lookup); |
| txn_sm->q_pending_action = TSCacheRead(contp, txn_sm->q_key); |
| } else { /* not done with writing into cache */ |
| |
| TSDebug(PLUGIN_NAME, "re-enable cache_write_vio"); |
| TSVIOReenable(txn_sm->q_cache_write_vio); |
| } |
| return TS_SUCCESS; |
| default: |
| break; |
| } |
| |
| /* Something wrong if getting here. */ |
| return prepare_to_die(contp); |
| } |
| |
| /* If the response has been fully written into the client_vc, |
| which means this txn is done, close the client_vc. Otherwise, |
| reenable the write_vio. */ |
| int |
| state_send_response_to_client(TSCont contp, TSEvent event, TSVIO vio) |
| { |
| TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); |
| |
| TSDebug(PLUGIN_NAME, "enter state_send_response_to_client"); |
| |
| switch (event) { |
| case TS_EVENT_VCONN_WRITE_READY: |
| TSDebug(PLUGIN_NAME, " . wr ready"); |
| TSDebug(PLUGIN_NAME, "write_ready: nbytes %" PRId64 ", ndone %" PRId64, TSVIONBytesGet(vio), TSVIONDoneGet(vio)); |
| TSVIOReenable(txn_sm->q_client_write_vio); |
| break; |
| |
| case TS_EVENT_VCONN_WRITE_COMPLETE: |
| TSDebug(PLUGIN_NAME, " . wr complete"); |
| TSDebug(PLUGIN_NAME, "write_complete: nbytes %" PRId64 ", ndone %" PRId64, TSVIONBytesGet(vio), TSVIONDoneGet(vio)); |
| /* Finished sending all data to client, close client_vc. */ |
| if (txn_sm->q_client_vc) { |
| TSVConnClose(txn_sm->q_client_vc); |
| txn_sm->q_client_vc = NULL; |
| } |
| txn_sm->q_client_read_vio = NULL; |
| txn_sm->q_client_write_vio = NULL; |
| |
| return state_done(contp, 0, NULL); |
| |
| default: |
| TSDebug(PLUGIN_NAME, " . default handler"); |
| return prepare_to_die(contp); |
| } |
| |
| TSDebug(PLUGIN_NAME, "leaving send_response_to_client"); |
| |
| return TS_SUCCESS; |
| } |
| |
| /* There is something wrong, abort client, server and cache vc |
| if they exist. */ |
| int |
| prepare_to_die(TSCont contp) |
| { |
| TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); |
| |
| TSDebug(PLUGIN_NAME, "enter prepare_to_die"); |
| if (txn_sm->q_client_vc) { |
| TSVConnAbort(txn_sm->q_client_vc, 1); |
| txn_sm->q_client_vc = NULL; |
| } |
| txn_sm->q_client_read_vio = NULL; |
| txn_sm->q_client_write_vio = NULL; |
| |
| if (txn_sm->q_server_vc) { |
| TSVConnAbort(txn_sm->q_server_vc, 1); |
| txn_sm->q_server_vc = NULL; |
| } |
| txn_sm->q_server_read_vio = NULL; |
| txn_sm->q_server_write_vio = NULL; |
| |
| if (txn_sm->q_cache_vc) { |
| TSVConnAbort(txn_sm->q_cache_vc, 1); |
| txn_sm->q_cache_vc = NULL; |
| } |
| txn_sm->q_cache_read_vio = NULL; |
| txn_sm->q_cache_write_vio = NULL; |
| |
| return state_done(contp, 0, NULL); |
| } |
| |
| int |
| state_done(TSCont contp, TSEvent event ATS_UNUSED, TSVIO vio ATS_UNUSED) |
| { |
| TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp); |
| |
| TSDebug(PLUGIN_NAME, "enter state_done"); |
| |
| if (txn_sm->q_pending_action && !TSActionDone(txn_sm->q_pending_action)) { |
| TSDebug(PLUGIN_NAME, "cancelling pending action %p", txn_sm->q_pending_action); |
| TSActionCancel(txn_sm->q_pending_action); |
| } else if (txn_sm->q_pending_action) { |
| TSDebug(PLUGIN_NAME, "action is done %p", txn_sm->q_pending_action); |
| } |
| |
| txn_sm->q_pending_action = NULL; |
| txn_sm->q_mutex = NULL; |
| |
| if (txn_sm->q_client_request_buffer) { |
| if (txn_sm->q_client_request_buffer_reader) { |
| TSIOBufferReaderFree(txn_sm->q_client_request_buffer_reader); |
| } |
| TSIOBufferDestroy(txn_sm->q_client_request_buffer); |
| txn_sm->q_client_request_buffer = NULL; |
| txn_sm->q_client_request_buffer_reader = NULL; |
| } |
| |
| if (txn_sm->q_client_response_buffer) { |
| if (txn_sm->q_client_response_buffer_reader) { |
| TSIOBufferReaderFree(txn_sm->q_client_response_buffer_reader); |
| } |
| |
| TSIOBufferDestroy(txn_sm->q_client_response_buffer); |
| txn_sm->q_client_response_buffer = NULL; |
| txn_sm->q_client_response_buffer_reader = NULL; |
| } |
| |
| if (txn_sm->q_cache_read_buffer) { |
| if (txn_sm->q_cache_read_buffer_reader) { |
| TSIOBufferReaderFree(txn_sm->q_cache_read_buffer_reader); |
| } |
| TSIOBufferDestroy(txn_sm->q_cache_read_buffer); |
| txn_sm->q_cache_read_buffer = NULL; |
| txn_sm->q_cache_read_buffer_reader = NULL; |
| } |
| |
| if (txn_sm->q_server_request_buffer) { |
| if (txn_sm->q_server_request_buffer_reader) { |
| TSIOBufferReaderFree(txn_sm->q_server_request_buffer_reader); |
| } |
| TSIOBufferDestroy(txn_sm->q_server_request_buffer); |
| txn_sm->q_server_request_buffer = NULL; |
| txn_sm->q_server_request_buffer_reader = NULL; |
| } |
| |
| if (txn_sm->q_server_response_buffer) { |
| TSIOBufferDestroy(txn_sm->q_server_response_buffer); |
| txn_sm->q_server_response_buffer = NULL; |
| } |
| |
| if (txn_sm->q_server_name) { |
| TSfree(txn_sm->q_server_name); |
| txn_sm->q_server_name = NULL; |
| } |
| |
| if (txn_sm->q_file_name) { |
| TSfree(txn_sm->q_file_name); |
| txn_sm->q_file_name = NULL; |
| } |
| |
| if (txn_sm->q_key) { |
| TSCacheKeyDestroy(txn_sm->q_key); |
| } |
| |
| if (txn_sm->q_client_request) { |
| TSfree(txn_sm->q_client_request); |
| txn_sm->q_client_request = NULL; |
| } |
| |
| if (txn_sm->q_server_response) { |
| TSfree(txn_sm->q_server_response); |
| txn_sm->q_server_response = NULL; |
| } |
| |
| txn_sm->q_magic = TXN_SM_DEAD; |
| TSfree(txn_sm); |
| |
| TSContDestroy(contp); |
| return TS_EVENT_NONE; |
| } |
| |
| /* Write the data into the client_vc. */ |
| int |
| send_response_to_client(TSCont contp) |
| { |
| TxnSM *txn_sm; |
| int response_len; |
| |
| TSDebug(PLUGIN_NAME, "enter send_response_to_client"); |
| |
| txn_sm = (TxnSM *)TSContDataGet(contp); |
| response_len = TSIOBufferReaderAvail(txn_sm->q_client_response_buffer_reader); |
| |
| TSDebug(PLUGIN_NAME, " . resp_len is %d", response_len); |
| |
| set_handler(txn_sm->q_current_handler, (TxnSMHandler)&state_interface_with_client); |
| txn_sm->q_client_write_vio = |
| TSVConnWrite(txn_sm->q_client_vc, (TSCont)contp, txn_sm->q_client_response_buffer_reader, response_len); |
| return TS_SUCCESS; |
| } |
| |
| /* Read data out through the_reader and save it in a char buffer. */ |
| char * |
| get_info_from_buffer(TSIOBufferReader the_reader) |
| { |
| char *info; |
| char *info_start; |
| |
| int64_t read_avail, read_done; |
| TSIOBufferBlock blk; |
| char *buf; |
| |
| if (!the_reader) { |
| return NULL; |
| } |
| |
| read_avail = TSIOBufferReaderAvail(the_reader); |
| |
| info = (char *)TSmalloc(sizeof(char) * read_avail); |
| if (info == NULL) { |
| return NULL; |
| } |
| info_start = info; |
| |
| /* Read the data out of the reader */ |
| while (read_avail > 0) { |
| blk = TSIOBufferReaderStart(the_reader); |
| buf = (char *)TSIOBufferBlockReadStart(blk, the_reader, &read_done); |
| memcpy(info, buf, read_done); |
| if (read_done > 0) { |
| TSIOBufferReaderConsume(the_reader, read_done); |
| read_avail -= read_done; |
| info += read_done; |
| } |
| } |
| |
| return info_start; |
| } |
| |
| /* Check if the end token is in the char buffer. */ |
| int |
| is_request_end(char *buf) |
| { |
| char *temp = strstr(buf, " \n\n"); |
| if (!temp) { |
| return 0; |
| } |
| return 1; |
| } |
| |
| /* Parse the server_name and file name from the request. */ |
| int |
| parse_request(char *request, char *server_name, char *file_name) |
| { |
| char *saveptr = NULL; |
| char *temp = strtok_r(request, " ", &saveptr); |
| |
| if (temp != NULL) { |
| TSstrlcpy(server_name, temp, MAX_SERVER_NAME_LENGTH + 1); |
| } else { |
| return 0; |
| } |
| |
| temp = strtok_r(NULL, " ", &saveptr); |
| if (temp != NULL) { |
| TSstrlcpy(file_name, temp, MAX_FILE_NAME_LENGTH + 1); |
| } else { |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| /* Create 128-bit cache key based on the input string, in this case, |
| the file_name of the requested doc. */ |
| TSCacheKey |
| CacheKeyCreate(char *file_name) |
| { |
| TSCacheKey key; |
| |
| /* TSCacheKeyCreate is to allocate memory space for the key */ |
| key = TSCacheKeyCreate(); |
| |
| /* TSCacheKeyDigestSet is to compute TSCackeKey from the input string */ |
| TSCacheKeyDigestSet(key, file_name, strlen(file_name)); |
| return key; |
| } |