1 /* ====================================================================
2  *    Licensed to the Apache Software Foundation (ASF) under one
3  *    or more contributor license agreements.  See the NOTICE file
4  *    distributed with this work for additional information
5  *    regarding copyright ownership.  The ASF licenses this file
6  *    to you under the Apache License, Version 2.0 (the
7  *    "License"); you may not use this file except in compliance
8  *    with the License.  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *    Unless required by applicable law or agreed to in writing,
13  *    software distributed under the License is distributed on an
14  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  *    KIND, either express or implied.  See the License for the
16  *    specific language governing permissions and limitations
17  *    under the License.
18  * ====================================================================
19  */
20 
21 #include "apr.h"
22 #include "apr_pools.h"
23 #include <apr_env.h>
24 #include <apr_strings.h>
25 
26 #include <stdlib.h>
27 
28 #include "serf.h"
29 
30 #include "test_serf.h"
31 #include "server/test_server.h"
32 
33 
34 /*****************************************************************************/
35 /* Server setup function(s)
36  */
37 
38 #define HTTP_SERV_URL  "http://localhost:" SERV_PORT_STR
39 #define HTTPS_SERV_URL "https://localhost:" SERV_PORT_STR
40 
get_srcdir_file(apr_pool_t * pool,const char * file)41 const char * get_srcdir_file(apr_pool_t *pool, const char * file)
42 {
43     char *srcdir = "";
44 
45     if (apr_env_get(&srcdir, "srcdir", pool) == APR_SUCCESS) {
46         return apr_pstrcat(pool, srcdir, "/", file, NULL);
47     }
48     else {
49         return file;
50     }
51 }
52 
53 /* cleanup for conn */
cleanup_conn(void * baton)54 static apr_status_t cleanup_conn(void *baton)
55 {
56     serf_connection_t *conn = baton;
57 
58     serf_connection_close(conn);
59 
60     return APR_SUCCESS;
61 }
62 
default_server_address(apr_sockaddr_t ** address,apr_pool_t * pool)63 static apr_status_t default_server_address(apr_sockaddr_t **address,
64                                            apr_pool_t *pool)
65 {
66     return apr_sockaddr_info_get(address,
67                                  "localhost", APR_UNSPEC, SERV_PORT, 0,
68                                  pool);
69 }
70 
default_proxy_address(apr_sockaddr_t ** address,apr_pool_t * pool)71 static apr_status_t default_proxy_address(apr_sockaddr_t **address,
72                                           apr_pool_t *pool)
73 {
74     return apr_sockaddr_info_get(address,
75                                  "localhost", APR_UNSPEC, PROXY_PORT, 0,
76                                  pool);
77 }
78 
79 /* Default implementation of a serf_connection_closed_t callback. */
default_closed_connection(serf_connection_t * conn,void * closed_baton,apr_status_t why,apr_pool_t * pool)80 static void default_closed_connection(serf_connection_t *conn,
81                                       void *closed_baton,
82                                       apr_status_t why,
83                                       apr_pool_t *pool)
84 {
85     if (why) {
86         abort();
87     }
88 }
89 
90 /* Default implementation of a serf_connection_setup_t callback. */
default_http_conn_setup(apr_socket_t * skt,serf_bucket_t ** input_bkt,serf_bucket_t ** output_bkt,void * setup_baton,apr_pool_t * pool)91 static apr_status_t default_http_conn_setup(apr_socket_t *skt,
92                                             serf_bucket_t **input_bkt,
93                                             serf_bucket_t **output_bkt,
94                                             void *setup_baton,
95                                             apr_pool_t *pool)
96 {
97     test_baton_t *tb = setup_baton;
98 
99     *input_bkt = serf_bucket_socket_create(skt, tb->bkt_alloc);
100     return APR_SUCCESS;
101 }
102 
103 /* This function makes serf use SSL on the connection. */
default_https_conn_setup(apr_socket_t * skt,serf_bucket_t ** input_bkt,serf_bucket_t ** output_bkt,void * setup_baton,apr_pool_t * pool)104 apr_status_t default_https_conn_setup(apr_socket_t *skt,
105                                       serf_bucket_t **input_bkt,
106                                       serf_bucket_t **output_bkt,
107                                       void *setup_baton,
108                                       apr_pool_t *pool)
109 {
110     test_baton_t *tb = setup_baton;
111 
112     *input_bkt = serf_bucket_socket_create(skt, tb->bkt_alloc);
113     *input_bkt = serf_bucket_ssl_decrypt_create(*input_bkt, NULL,
114                                                 tb->bkt_alloc);
115     tb->ssl_context = serf_bucket_ssl_encrypt_context_get(*input_bkt);
116 
117     if (output_bkt) {
118         *output_bkt = serf_bucket_ssl_encrypt_create(*output_bkt,
119                                                      tb->ssl_context,
120                                                      tb->bkt_alloc);
121     }
122 
123     if (tb->server_cert_cb)
124         serf_ssl_server_cert_callback_set(tb->ssl_context,
125                                           tb->server_cert_cb,
126                                           tb);
127 
128     serf_ssl_set_hostname(tb->ssl_context, "localhost");
129 
130     return APR_SUCCESS;
131 }
132 
use_new_connection(test_baton_t * tb,apr_pool_t * pool)133 apr_status_t use_new_connection(test_baton_t *tb,
134                                 apr_pool_t *pool)
135 {
136     apr_uri_t url;
137     apr_status_t status;
138 
139     if (tb->connection)
140         cleanup_conn(tb->connection);
141     tb->connection = NULL;
142 
143     status = apr_uri_parse(pool, tb->serv_url, &url);
144     if (status != APR_SUCCESS)
145         return status;
146 
147     status = serf_connection_create2(&tb->connection, tb->context,
148                                      url,
149                                      tb->conn_setup,
150                                      tb,
151                                      default_closed_connection,
152                                      tb,
153                                      pool);
154     apr_pool_cleanup_register(pool, tb->connection, cleanup_conn,
155                               apr_pool_cleanup_null);
156 
157     return status;
158 }
159 
160 /* Setup the client context, ready to connect and send requests to a
161    server.*/
setup(test_baton_t ** tb_p,serf_connection_setup_t conn_setup,const char * serv_url,int use_proxy,apr_size_t message_count,apr_pool_t * pool)162 static apr_status_t setup(test_baton_t **tb_p,
163                           serf_connection_setup_t conn_setup,
164                           const char *serv_url,
165                           int use_proxy,
166                           apr_size_t message_count,
167                           apr_pool_t *pool)
168 {
169     test_baton_t *tb;
170     apr_status_t status;
171 
172     tb = apr_pcalloc(pool, sizeof(*tb));
173     *tb_p = tb;
174 
175     tb->pool = pool;
176     tb->context = serf_context_create(pool);
177     tb->bkt_alloc = serf_bucket_allocator_create(pool, NULL, NULL);
178 
179     tb->accepted_requests = apr_array_make(pool, message_count, sizeof(int));
180     tb->sent_requests = apr_array_make(pool, message_count, sizeof(int));
181     tb->handled_requests = apr_array_make(pool, message_count, sizeof(int));
182 
183     tb->serv_url = serv_url;
184     tb->conn_setup = conn_setup;
185 
186     status = default_server_address(&tb->serv_addr, pool);
187     if (status != APR_SUCCESS)
188         return status;
189 
190     if (use_proxy) {
191         status = default_proxy_address(&tb->proxy_addr, pool);
192         if (status != APR_SUCCESS)
193             return status;
194 
195         /* Configure serf to use the proxy server */
196         serf_config_proxy(tb->context, tb->proxy_addr);
197     }
198 
199     status = use_new_connection(tb, pool);
200 
201     return status;
202 }
203 
204 /* Setup an https server and the client context to connect to that server */
test_https_server_setup(test_baton_t ** tb_p,test_server_message_t * message_list,apr_size_t message_count,test_server_action_t * action_list,apr_size_t action_count,apr_int32_t options,serf_connection_setup_t conn_setup,const char * keyfile,const char ** certfiles,const char * client_cn,serf_ssl_need_server_cert_t server_cert_cb,apr_pool_t * pool)205 apr_status_t test_https_server_setup(test_baton_t **tb_p,
206                                      test_server_message_t *message_list,
207                                      apr_size_t message_count,
208                                      test_server_action_t *action_list,
209                                      apr_size_t action_count,
210                                      apr_int32_t options,
211                                      serf_connection_setup_t conn_setup,
212                                      const char *keyfile,
213                                      const char **certfiles,
214                                      const char *client_cn,
215                                      serf_ssl_need_server_cert_t server_cert_cb,
216                                      apr_pool_t *pool)
217 {
218     apr_status_t status;
219     test_baton_t *tb;
220 
221     status = setup(tb_p,
222                    conn_setup ? conn_setup : default_https_conn_setup,
223                    HTTPS_SERV_URL,
224                    FALSE,
225                    message_count,
226                    pool);
227     if (status != APR_SUCCESS)
228         return status;
229 
230     tb = *tb_p;
231     tb->server_cert_cb = server_cert_cb;
232 
233     /* Prepare a server. */
234     setup_https_test_server(&tb->serv_ctx, tb->serv_addr,
235                             message_list, message_count,
236                             action_list, action_count, options,
237                             keyfile, certfiles, client_cn,
238                             pool);
239     status = start_test_server(tb->serv_ctx);
240 
241     return status;
242 }
243 
244 /* Setup an http server and the client context to connect to that server */
test_http_server_setup(test_baton_t ** tb_p,test_server_message_t * message_list,apr_size_t message_count,test_server_action_t * action_list,apr_size_t action_count,apr_int32_t options,serf_connection_setup_t conn_setup,apr_pool_t * pool)245 apr_status_t test_http_server_setup(test_baton_t **tb_p,
246                                     test_server_message_t *message_list,
247                                     apr_size_t message_count,
248                                     test_server_action_t *action_list,
249                                     apr_size_t action_count,
250                                     apr_int32_t options,
251                                     serf_connection_setup_t conn_setup,
252                                     apr_pool_t *pool)
253 {
254     apr_status_t status;
255     test_baton_t *tb;
256 
257     status = setup(tb_p,
258                    conn_setup ? conn_setup : default_http_conn_setup,
259                    HTTP_SERV_URL,
260                    FALSE,
261                    message_count,
262                    pool);
263     if (status != APR_SUCCESS)
264         return status;
265 
266     tb = *tb_p;
267 
268     /* Prepare a server. */
269     setup_test_server(&tb->serv_ctx, tb->serv_addr,
270                       message_list, message_count,
271                       action_list, action_count, options,
272                       pool);
273     status = start_test_server(tb->serv_ctx);
274 
275     return status;
276 }
277 
278 /* Setup a proxy server and an http server and the client context to connect to
279    that proxy server */
280 apr_status_t
test_server_proxy_setup(test_baton_t ** tb_p,test_server_message_t * serv_message_list,apr_size_t serv_message_count,test_server_action_t * serv_action_list,apr_size_t serv_action_count,test_server_message_t * proxy_message_list,apr_size_t proxy_message_count,test_server_action_t * proxy_action_list,apr_size_t proxy_action_count,apr_int32_t options,serf_connection_setup_t conn_setup,apr_pool_t * pool)281 test_server_proxy_setup(test_baton_t **tb_p,
282                         test_server_message_t *serv_message_list,
283                         apr_size_t serv_message_count,
284                         test_server_action_t *serv_action_list,
285                         apr_size_t serv_action_count,
286                         test_server_message_t *proxy_message_list,
287                         apr_size_t proxy_message_count,
288                         test_server_action_t *proxy_action_list,
289                         apr_size_t proxy_action_count,
290                         apr_int32_t options,
291                         serf_connection_setup_t conn_setup,
292                         apr_pool_t *pool)
293 {
294     apr_status_t status;
295     test_baton_t *tb;
296 
297     status = setup(tb_p,
298                    conn_setup ? conn_setup : default_http_conn_setup,
299                    HTTP_SERV_URL,
300                    TRUE,
301                    serv_message_count,
302                    pool);
303     if (status != APR_SUCCESS)
304         return status;
305 
306     tb = *tb_p;
307 
308     /* Prepare the server. */
309     setup_test_server(&tb->serv_ctx, tb->serv_addr,
310                       serv_message_list, serv_message_count,
311                       serv_action_list, serv_action_count,
312                       options,
313                       pool);
314     status = start_test_server(tb->serv_ctx);
315     if (status != APR_SUCCESS)
316         return status;
317 
318     /* Prepare the proxy. */
319     setup_test_server(&tb->proxy_ctx, tb->proxy_addr,
320                       proxy_message_list, proxy_message_count,
321                       proxy_action_list, proxy_action_count,
322                       options,
323                       pool);
324     status = start_test_server(tb->proxy_ctx);
325 
326     return status;
327 }
328 
329 /* Setup a proxy server and a https server and the client context to connect to
330    that proxy server */
331 apr_status_t
test_https_server_proxy_setup(test_baton_t ** tb_p,test_server_message_t * serv_message_list,apr_size_t serv_message_count,test_server_action_t * serv_action_list,apr_size_t serv_action_count,test_server_message_t * proxy_message_list,apr_size_t proxy_message_count,test_server_action_t * proxy_action_list,apr_size_t proxy_action_count,apr_int32_t options,serf_connection_setup_t conn_setup,const char * keyfile,const char ** certfiles,const char * client_cn,serf_ssl_need_server_cert_t server_cert_cb,apr_pool_t * pool)332 test_https_server_proxy_setup(test_baton_t **tb_p,
333                               test_server_message_t *serv_message_list,
334                               apr_size_t serv_message_count,
335                               test_server_action_t *serv_action_list,
336                               apr_size_t serv_action_count,
337                               test_server_message_t *proxy_message_list,
338                               apr_size_t proxy_message_count,
339                               test_server_action_t *proxy_action_list,
340                               apr_size_t proxy_action_count,
341                               apr_int32_t options,
342                               serf_connection_setup_t conn_setup,
343                               const char *keyfile,
344                               const char **certfiles,
345                               const char *client_cn,
346                               serf_ssl_need_server_cert_t server_cert_cb,
347                               apr_pool_t *pool)
348 {
349     apr_status_t status;
350     test_baton_t *tb;
351 
352     status = setup(tb_p,
353                    conn_setup ? conn_setup : default_https_conn_setup,
354                    HTTPS_SERV_URL,
355                    TRUE, /* use proxy */
356                    serv_message_count,
357                    pool);
358     if (status != APR_SUCCESS)
359         return status;
360 
361     tb = *tb_p;
362     tb->server_cert_cb = server_cert_cb;
363 
364     /* Prepare a https server. */
365     setup_https_test_server(&tb->serv_ctx, tb->serv_addr,
366                             serv_message_list, serv_message_count,
367                             serv_action_list, serv_action_count,
368                             options,
369                             keyfile, certfiles, client_cn,
370                             pool);
371     status = start_test_server(tb->serv_ctx);
372 
373     /* Prepare the proxy. */
374     setup_test_server(&tb->proxy_ctx, tb->proxy_addr,
375                       proxy_message_list, proxy_message_count,
376                       proxy_action_list, proxy_action_count,
377                       options,
378                       pool);
379     status = start_test_server(tb->proxy_ctx);
380 
381     return status;
382 }
383 
test_setup(void * dummy)384 void *test_setup(void *dummy)
385 {
386     apr_pool_t *test_pool;
387     apr_pool_create(&test_pool, NULL);
388     return test_pool;
389 }
390 
test_teardown(void * baton)391 void *test_teardown(void *baton)
392 {
393     apr_pool_t *pool = baton;
394     apr_pool_destroy(pool);
395 
396     return NULL;
397 }
398 
399 /* Helper function, runs the client and server context loops and validates
400  that no errors were encountered, and all messages were sent and received. */
401 apr_status_t
test_helper_run_requests_no_check(CuTest * tc,test_baton_t * tb,int num_requests,handler_baton_t handler_ctx[],apr_pool_t * pool)402 test_helper_run_requests_no_check(CuTest *tc, test_baton_t *tb,
403                                   int num_requests,
404                                   handler_baton_t handler_ctx[],
405                                   apr_pool_t *pool)
406 {
407     apr_pool_t *iter_pool;
408     int i, done = 0;
409     apr_status_t status;
410 
411     apr_pool_create(&iter_pool, pool);
412 
413     while (!done)
414     {
415         apr_pool_clear(iter_pool);
416 
417         /* run server event loop */
418         status = run_test_server(tb->serv_ctx, 0, iter_pool);
419         if (!APR_STATUS_IS_TIMEUP(status) &&
420             SERF_BUCKET_READ_ERROR(status))
421             return status;
422 
423         /* run proxy event loop */
424         if (tb->proxy_ctx) {
425             status = run_test_server(tb->proxy_ctx, 0, iter_pool);
426             if (!APR_STATUS_IS_TIMEUP(status) &&
427                 SERF_BUCKET_READ_ERROR(status))
428                 return status;
429         }
430 
431         /* run client event loop */
432         status = serf_context_run(tb->context, 0, iter_pool);
433         if (!APR_STATUS_IS_TIMEUP(status) &&
434             SERF_BUCKET_READ_ERROR(status))
435             return status;
436 
437         done = 1;
438         for (i = 0; i < num_requests; i++)
439             done &= handler_ctx[i].done;
440     }
441     apr_pool_destroy(iter_pool);
442 
443     return APR_SUCCESS;
444 }
445 
446 void
test_helper_run_requests_expect_ok(CuTest * tc,test_baton_t * tb,int num_requests,handler_baton_t handler_ctx[],apr_pool_t * pool)447 test_helper_run_requests_expect_ok(CuTest *tc, test_baton_t *tb,
448                                    int num_requests,
449                                    handler_baton_t handler_ctx[],
450                                    apr_pool_t *pool)
451 {
452     apr_status_t status;
453 
454     status = test_helper_run_requests_no_check(tc, tb, num_requests,
455                                                handler_ctx, pool);
456     CuAssertIntEquals(tc, APR_SUCCESS, status);
457 
458     /* Check that all requests were received */
459     CuAssertIntEquals(tc, num_requests, tb->sent_requests->nelts);
460     CuAssertIntEquals(tc, num_requests, tb->accepted_requests->nelts);
461     CuAssertIntEquals(tc, num_requests, tb->handled_requests->nelts);
462 }
463 
accept_response(serf_request_t * request,serf_bucket_t * stream,void * acceptor_baton,apr_pool_t * pool)464 serf_bucket_t* accept_response(serf_request_t *request,
465                                serf_bucket_t *stream,
466                                void *acceptor_baton,
467                                apr_pool_t *pool)
468 {
469     serf_bucket_t *c;
470     serf_bucket_alloc_t *bkt_alloc;
471     handler_baton_t *ctx = acceptor_baton;
472     serf_bucket_t *response;
473 
474     /* get the per-request bucket allocator */
475     bkt_alloc = serf_request_get_alloc(request);
476 
477     /* Create a barrier so the response doesn't eat us! */
478     c = serf_bucket_barrier_create(stream, bkt_alloc);
479 
480     APR_ARRAY_PUSH(ctx->accepted_requests, int) = ctx->req_id;
481 
482     response = serf_bucket_response_create(c, bkt_alloc);
483 
484     if (strcasecmp(ctx->method, "HEAD") == 0)
485       serf_bucket_response_set_head(response);
486 
487     return response;
488 }
489 
setup_request(serf_request_t * request,void * setup_baton,serf_bucket_t ** req_bkt,serf_response_acceptor_t * acceptor,void ** acceptor_baton,serf_response_handler_t * handler,void ** handler_baton,apr_pool_t * pool)490 apr_status_t setup_request(serf_request_t *request,
491                            void *setup_baton,
492                            serf_bucket_t **req_bkt,
493                            serf_response_acceptor_t *acceptor,
494                            void **acceptor_baton,
495                            serf_response_handler_t *handler,
496                            void **handler_baton,
497                            apr_pool_t *pool)
498 {
499     handler_baton_t *ctx = setup_baton;
500     serf_bucket_t *body_bkt;
501 
502     if (ctx->request)
503     {
504         /* Create a raw request bucket. */
505         *req_bkt = serf_bucket_simple_create(ctx->request, strlen(ctx->request),
506                                              NULL, NULL,
507                                              serf_request_get_alloc(request));
508     }
509     else
510     {
511         if (ctx->req_id >= 0) {
512             /* create a simple body text */
513             const char *str = apr_psprintf(pool, "%d", ctx->req_id);
514 
515             body_bkt = serf_bucket_simple_create(
516                                         str, strlen(str), NULL, NULL,
517                                         serf_request_get_alloc(request));
518         }
519         else
520             body_bkt = NULL;
521 
522         *req_bkt =
523         serf_request_bucket_request_create(request,
524                                            ctx->method, ctx->path,
525                                            body_bkt,
526                                            serf_request_get_alloc(request));
527     }
528 
529     APR_ARRAY_PUSH(ctx->sent_requests, int) = ctx->req_id;
530 
531     *acceptor = ctx->acceptor;
532     *acceptor_baton = ctx;
533     *handler = ctx->handler;
534     *handler_baton = ctx;
535 
536     return APR_SUCCESS;
537 }
538 
handle_response(serf_request_t * request,serf_bucket_t * response,void * handler_baton,apr_pool_t * pool)539 apr_status_t handle_response(serf_request_t *request,
540                              serf_bucket_t *response,
541                              void *handler_baton,
542                              apr_pool_t *pool)
543 {
544     handler_baton_t *ctx = handler_baton;
545 
546     if (! response) {
547         serf_connection_request_create(ctx->tb->connection,
548                                        setup_request,
549                                        ctx);
550         return APR_SUCCESS;
551     }
552 
553     while (1) {
554         apr_status_t status;
555         const char *data;
556         apr_size_t len;
557 
558         status = serf_bucket_read(response, 2048, &data, &len);
559         if (SERF_BUCKET_READ_ERROR(status))
560             return status;
561 
562         if (APR_STATUS_IS_EOF(status)) {
563             APR_ARRAY_PUSH(ctx->handled_requests, int) = ctx->req_id;
564             ctx->done = TRUE;
565             return APR_EOF;
566         }
567 
568         if (APR_STATUS_IS_EAGAIN(status)) {
569             return status;
570         }
571 
572     }
573 
574     return APR_SUCCESS;
575 }
576 
setup_handler(test_baton_t * tb,handler_baton_t * handler_ctx,const char * method,const char * path,int req_id,serf_response_handler_t handler)577 void setup_handler(test_baton_t *tb, handler_baton_t *handler_ctx,
578                    const char *method, const char *path,
579                    int req_id,
580                    serf_response_handler_t handler)
581 {
582     handler_ctx->method = method;
583     handler_ctx->path = path;
584     handler_ctx->done = FALSE;
585 
586     handler_ctx->acceptor = accept_response;
587     handler_ctx->acceptor_baton = NULL;
588     handler_ctx->handler = handler ? handler : handle_response;
589     handler_ctx->req_id = req_id;
590     handler_ctx->accepted_requests = tb->accepted_requests;
591     handler_ctx->sent_requests = tb->sent_requests;
592     handler_ctx->handled_requests = tb->handled_requests;
593     handler_ctx->tb = tb;
594     handler_ctx->request = NULL;
595 }
596 
create_new_prio_request(test_baton_t * tb,handler_baton_t * handler_ctx,const char * method,const char * path,int req_id)597 void create_new_prio_request(test_baton_t *tb,
598                              handler_baton_t *handler_ctx,
599                              const char *method, const char *path,
600                              int req_id)
601 {
602     setup_handler(tb, handler_ctx, method, path, req_id, NULL);
603     serf_connection_priority_request_create(tb->connection,
604                                             setup_request,
605                                             handler_ctx);
606 }
607 
create_new_request(test_baton_t * tb,handler_baton_t * handler_ctx,const char * method,const char * path,int req_id)608 void create_new_request(test_baton_t *tb,
609                         handler_baton_t *handler_ctx,
610                         const char *method, const char *path,
611                         int req_id)
612 {
613     setup_handler(tb, handler_ctx, method, path, req_id, NULL);
614     serf_connection_request_create(tb->connection,
615                                    setup_request,
616                                    handler_ctx);
617 }
618 
619 void
create_new_request_with_resp_hdlr(test_baton_t * tb,handler_baton_t * handler_ctx,const char * method,const char * path,int req_id,serf_response_handler_t handler)620 create_new_request_with_resp_hdlr(test_baton_t *tb,
621                                   handler_baton_t *handler_ctx,
622                                   const char *method, const char *path,
623                                   int req_id,
624                                   serf_response_handler_t handler)
625 {
626     setup_handler(tb, handler_ctx, method, path, req_id, handler);
627     serf_connection_request_create(tb->connection,
628                                    setup_request,
629                                    handler_ctx);
630 }
631