1 /**
2  * Copyright 2010 Christian Liesch
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /**
18  * @file
19  *
20  * @Author christian liesch <liesch@gmx.ch>
21  *
22  * Implementation of the HTTP Test Tool date module
23  */
24 
25 /************************************************************************
26  * Includes
27  ***********************************************************************/
28 #include "module.h"
29 #include "worker.h"
30 
31 /************************************************************************
32  * Definitions
33  ***********************************************************************/
34 const char * date_module = "date_module";
35 apr_time_t start_time;
36 
37 typedef struct date_wconf_s {
38   apr_time_t start_time;
39 } date_wconf_t;
40 
41 /************************************************************************
42  * Commands
43  ***********************************************************************/
44 
45 /**
46  * Get lua config from worker
47  *
48  * @param worker IN worker
49  * @return lua config
50  */
date_get_worker_config(worker_t * worker)51 static date_wconf_t *date_get_worker_config(worker_t *worker) {
52   date_wconf_t *config = module_get_config(worker->config, date_module);
53   if (config == NULL) {
54     config = apr_pcalloc(worker->pbody, sizeof(*config));
55     config->start_time = start_time;
56     module_set_config(worker->config, apr_pstrdup(worker->pbody, date_module), config);
57   }
58   return config;
59 }
60 
61 /**
62  * TIME command stores time in a variable [ms]
63  *
64  * @param self IN command
65  * @param worker IN thread data object
66  * @param data IN variable name
67  *
68  * @return APR_SUCCESS
69  */
block_DATE_GET_TIME(worker_t * worker,worker_t * parent,apr_pool_t * ptmp)70 apr_status_t block_DATE_GET_TIME(worker_t *worker, worker_t *parent, apr_pool_t *ptmp) {
71   const char *var = store_get(worker->params, "1");
72 
73   if (!var) {
74     worker_log(worker, LOG_ERR, "Need a variable name to store time");
75   }
76 
77   worker_var_set(parent, var, apr_off_t_toa(worker->pbody, apr_time_as_msec(apr_time_now())));
78 
79   return APR_SUCCESS;
80 }
81 
82 /**
83  * STRFTIME command
84  *
85  * @param self IN command
86  * @param worker IN thread data object
87  * @param data IN time [ms] "format" variable
88  *
89  * @return APR_SUCCESS or APR_EGENERAL on wrong parameters
90  */
block_DATE_FORMAT(worker_t * worker,worker_t * parent,apr_pool_t * ptmp)91 apr_status_t block_DATE_FORMAT(worker_t *worker, worker_t *parent, apr_pool_t *ptmp) {
92   apr_status_t status;
93   const char *time;
94   const char *fmt;
95   const char *var;
96   const char *type;
97   char *timefmt;
98   apr_size_t len;
99   apr_time_exp_t  tm;
100   apr_time_t timems;
101 
102   time = store_get(worker->params, "1");
103   fmt = store_get(worker->params, "2");
104   var = store_get(worker->params, "3");
105   type = store_get(worker->params, "4");
106 
107   if (!time) {
108     worker_log(worker, LOG_ERR, "Time not specified");
109     return APR_EGENERAL;
110   }
111   if (!fmt) {
112     worker_log(worker, LOG_ERR, "Format not specified");
113     return APR_EGENERAL;
114   }
115   if (!var) {
116     worker_log(worker, LOG_ERR, "Variable not specified");
117     return APR_EGENERAL;
118   }
119 
120   timems = apr_atoi64(time);
121 
122   timefmt = apr_pcalloc(worker->pbody, 255);
123 
124   if (type && strncasecmp(type, "Local", 5) == 0) {
125     if ((status = apr_time_exp_lt(&tm, timems * 1000)) != APR_SUCCESS) {
126       return status;
127     }
128   }
129   else {
130     if ((status = apr_time_exp_gmt(&tm, timems * 1000)) != APR_SUCCESS) {
131       return status;
132     }
133   }
134 
135   if ((status = apr_strftime(timefmt, &len, 254, fmt, &tm)) != APR_SUCCESS) {
136     return status;
137   }
138 
139   worker_var_set_and_zero_terminate(parent, var, timefmt, len);
140 
141   return APR_SUCCESS;
142 }
143 
144 /**
145  * SYNC command
146  *
147  * @param worker IN callee
148  * @param parent IN caller
149  * @param ptmp IN temporary pool
150  *
151  * @return APR_SUCCESS
152  */
block_DATE_SYNC(worker_t * worker,worker_t * parent,apr_pool_t * ptmp)153 apr_status_t block_DATE_SYNC(worker_t *worker, worker_t *parent, apr_pool_t *ptmp) {
154   apr_time_t seconds;
155   apr_time_t next_full;
156 
157   const char *first = store_get(worker->params, "1");
158   apr_time_t now = apr_time_now();
159 
160   if (!first || strcmp(first, "second") == 0) {
161     seconds = apr_time_sec(now) + 1;
162     next_full = apr_time_from_sec(seconds);
163   }
164   else /* if (first && strcmp(first, "minute") == 0) */ {
165     seconds = apr_time_sec(now) + (60 - (apr_time_sec(now) % 60));
166     next_full = apr_time_from_sec(seconds);
167   }
168 
169   apr_sleep(next_full - now);
170 
171   return APR_SUCCESS;
172 }
173 
174 /**
175  * TIMER command
176  * @param worker IN callee
177  * @param parent IN caller
178  * @param ptmp IN temporary pool
179  * @return APR_SUCCESS
180  */
block_DATE_TIMER(worker_t * worker,worker_t * parent,apr_pool_t * ptmp)181 apr_status_t block_DATE_TIMER(worker_t *worker, worker_t *parent,
182                               apr_pool_t *ptmp) {
183 
184   const char *cmd;
185   const char *var;
186   date_wconf_t *wconf = date_get_worker_config(worker);
187 
188   apr_time_t cur = apr_time_now();
189 
190   cmd = store_get(worker->params, "1");
191   var = store_get(worker->params, "2");
192 
193   if (strcasecmp(cmd, "GET") == 0) {
194     if (var && var[0] != 0) {
195       worker_var_set(worker, var,
196 		     apr_off_t_toa(ptmp,
197 				   apr_time_as_msec(cur - wconf->start_time)));
198     }
199   }
200   else if (strcasecmp(cmd, "RESET") == 0) {
201     wconf->start_time = apr_time_now();
202   }
203   else {
204     worker_log(worker, LOG_ERR, "Timer command %s not implemented", cmd);
205   }
206   return APR_SUCCESS;
207 }
208 
209 /************************************************************************
210  * Module
211  ***********************************************************************/
date_module_init(global_t * global)212 apr_status_t date_module_init(global_t *global) {
213   apr_status_t status;
214   start_time = apr_time_now();
215   if ((status = module_command_new(global, "DATE", "_GET_TIME",
216 	                           "<var>",
217 	                           "Stores the current time [ms] into <var>",
218 	                           block_DATE_GET_TIME)) != APR_SUCCESS) {
219     return status;
220   }
221   if ((status = module_command_new(global, "DATE", "_FORMAT",
222 	                           "<time> <format> <variable> [Local|GMT]",
223 				   "Do format <time> with <format> and stores it in <variable>. "
224 				   "Local is default.",
225 	                           block_DATE_FORMAT)) != APR_SUCCESS) {
226     return status;
227   }
228   if ((status = module_command_new(global, "DATE", "_SYNC",
229 	                           "[second|minute]",
230 				   "Default wait the next full second. "
231 				   "Optional wait the next full minute.",
232 	                           block_DATE_SYNC)) != APR_SUCCESS) {
233     return status;
234   }
235 
236   if ((status = module_command_new(global, "DATE", "_TIMER",
237 	                           "GET|RESET [<variable>]",
238 				   "Stores time duration from last reset or from start of test.",
239 	                           block_DATE_TIMER)) != APR_SUCCESS) {
240     return status;
241   }
242 
243 
244   return APR_SUCCESS;
245 }
246 
247