test-http-payload.c revision 38af46387e565053adf6c47f7f6871676d685de8
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include "lib.h"
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include "str.h"
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include "llist.h"
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi#include "path-util.h"
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include "hostpid.h"
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi#include "ioloop.h"
c5f932968281763df360b9c97cef60f5f80d5e3dTimo Sirainen#include "istream.h"
bdd36cfdba3ff66d25570a9ff568d69e1eb543cfTimo Sirainen#include "ostream.h"
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include "iostream-temp.h"
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include "connection.h"
d951320d498ae0800b677b754dde71574102123bTimo Sirainen#include "test-common.h"
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include "http-url.h"
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include "http-request.h"
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include "http-server.h"
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include "http-client.h"
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include <sys/types.h>
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include <sys/stat.h>
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include <sys/wait.h>
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include <signal.h>
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include <fcntl.h>
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include <unistd.h>
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen#include <dirent.h>
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenenum payload_handling {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen PAYLOAD_HANDLING_LOW_LEVEL,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen PAYLOAD_HANDLING_FORWARD,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen PAYLOAD_HANDLING_HANDLER,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen};
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic bool debug = FALSE;
091a2dea9d89734a7c1225eed511b3851693a757Timo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic bool blocking = FALSE;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic enum payload_handling server_payload_handling =
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen PAYLOAD_HANDLING_LOW_LEVEL;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic bool request_100_continue = FALSE;
a21f618de284dc22a480af1371d5f5cea50a39dfTimo Sirainenstatic size_t read_server_partial = 0;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic size_t read_client_partial = 0;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic unsigned int test_max_pending = 200;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic unsigned int client_ioloop_nesting = 0;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic struct ip_addr bind_ip;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic in_port_t bind_port = 0;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic int fd_listen = -1;
091a2dea9d89734a7c1225eed511b3851693a757Timo Sirainenstatic pid_t server_pid = (pid_t)-1;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic struct ioloop *ioloop_nested = NULL;
091a2dea9d89734a7c1225eed511b3851693a757Timo Sirainenstatic unsigned ioloop_nested_first = 0;
091a2dea9d89734a7c1225eed511b3851693a757Timo Sirainenstatic unsigned ioloop_nested_last = 0;
091a2dea9d89734a7c1225eed511b3851693a757Timo Sirainenstatic unsigned ioloop_nested_depth = 0;
091a2dea9d89734a7c1225eed511b3851693a757Timo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen/*
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen * Test files
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic ARRAY_TYPE(const_string) files;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic pool_t files_pool;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
091a2dea9d89734a7c1225eed511b3851693a757Timo Sirainenstatic void test_files_read_dir(const char *path)
091a2dea9d89734a7c1225eed511b3851693a757Timo Sirainen{
091a2dea9d89734a7c1225eed511b3851693a757Timo Sirainen DIR *dirp;
091a2dea9d89734a7c1225eed511b3851693a757Timo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* open the directory */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if ((dirp = opendir(path)) == NULL) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (errno == ENOENT || errno == EACCES)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_fatal("test files: "
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen "failed to open directory %s: %m", path);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* read entries */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen for (;;) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen const char *file;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct dirent *dp;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct stat st;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen errno = 0;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if ((dp=readdir(dirp)) == NULL)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen break;
15dd18994a8a4933e39e2f87233255d0ca82ba3eTimo Sirainen if (*dp->d_name == '.')
15dd18994a8a4933e39e2f87233255d0ca82ba3eTimo Sirainen continue;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen file = t_abspath_to(dp->d_name, path);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (stat(file, &st) == 0) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (S_ISREG(st.st_mode)) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen file += 2; /* skip "./" */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen file = p_strdup(files_pool, file);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen array_append(&files, &file, 1);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen } else {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen test_files_read_dir(file);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (errno != 0)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_fatal("test files: "
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen "failed to read directory %s: %m", path);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* Close the directory */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (closedir(dirp) < 0)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_error("test files: "
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen "failed to close directory %s: %m", path);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen}
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic void test_files_init(void)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen{
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* initialize file array */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen files_pool = pool_alloconly_create
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen (MEMPOOL_GROWING"http_server_request", 4096);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen p_array_init(&files, files_pool, 512);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* obtain all filenames */
a5b64f1abb1cb0a9718d5bf7f0ae808072237259Timo Sirainen test_files_read_dir(".");
a5b64f1abb1cb0a9718d5bf7f0ae808072237259Timo Sirainen}
a5b64f1abb1cb0a9718d5bf7f0ae808072237259Timo Sirainen
a5b64f1abb1cb0a9718d5bf7f0ae808072237259Timo Sirainenstatic void test_files_deinit(void)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen{
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen pool_unref(&files_pool);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen}
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Boschstatic struct istream *
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Boschtest_file_open(const char *path,
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch unsigned int *status_r, const char **reason_r)
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch ATTR_NULL(2, 3)
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch{
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch int fd;
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch if (status_r != NULL)
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch *status_r = 200;
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch if (reason_r != NULL)
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch *reason_r = "OK";
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch fd = open(path, O_RDONLY);
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch if (fd < 0) {
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch if (debug) {
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch i_debug("test files: "
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch "open(%s) failed: %m", path);
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch }
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch switch (errno) {
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch case EFAULT:
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen case ENOENT:
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (status_r != NULL)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen *status_r = 404;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (reason_r != NULL)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen *reason_r = "Not Found";
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen break;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen case EISDIR:
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen case EACCES:
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (status_r != NULL)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen *status_r = 403;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (reason_r != NULL)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen *reason_r = "Forbidden";
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi break;
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi default:
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi if (status_r != NULL)
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi *status_r = 500;
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi if (reason_r != NULL)
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi *reason_r = "Internal Server Error";
d48e40d6c77d673ad402d96571198d1cce4da225Timo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return NULL;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi return i_stream_create_fd_autoclose(&fd, 40960);
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi}
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi/*
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi * Test server
a327d9301f593433c228c4cc8cca05c95b37f6fbTimo Sirainen */
8a4a0ef8264d95ffb2ba8f6f109f94ea7f3454e8Timo Sirainen
d48e40d6c77d673ad402d96571198d1cce4da225Timo Sirainenstruct client {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen pool_t pool;
086b73efd1a5812a64acc951366a499d325509a6Stephan Bosch struct client *prev, *next;
086b73efd1a5812a64acc951366a499d325509a6Stephan Bosch
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch struct http_server_connection *http_conn;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen};
086b73efd1a5812a64acc951366a499d325509a6Stephan Bosch
086b73efd1a5812a64acc951366a499d325509a6Stephan Boschstruct client_request {
086b73efd1a5812a64acc951366a499d325509a6Stephan Bosch struct client *client;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct http_server_request *server_req;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen const char *path;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct istream *payload_input;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct ostream *payload_output;
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen struct io *io;
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen};
ed567dac7e55ab3e8dd53d9c86c31ebb2032d4dfTimo Sirainen
ed567dac7e55ab3e8dd53d9c86c31ebb2032d4dfTimo Sirainenstatic const struct http_server_callbacks http_callbacks;
ed567dac7e55ab3e8dd53d9c86c31ebb2032d4dfTimo Sirainenstatic struct http_server *http_server;
ed567dac7e55ab3e8dd53d9c86c31ebb2032d4dfTimo Sirainen
ed567dac7e55ab3e8dd53d9c86c31ebb2032d4dfTimo Sirainenstatic struct io *io_listen;
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Boschstatic struct client *clients;
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch
ed567dac7e55ab3e8dd53d9c86c31ebb2032d4dfTimo Sirainen/* location: /succes */
ed567dac7e55ab3e8dd53d9c86c31ebb2032d4dfTimo Sirainen
ed567dac7e55ab3e8dd53d9c86c31ebb2032d4dfTimo Sirainenstatic void
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainenclient_handle_success_request(struct client_request *creq)
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen{
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen struct http_server_request *req = creq->server_req;
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen const struct http_request *hreq =
2c5c70e12365d7910848259f88eb237ce3a15947Timo Sirainen http_server_request_get(req);
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen struct http_server_response *resp;
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen if (strcmp(hreq->method, "GET") != 0) {
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen http_server_request_fail(req,
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen 405, "Method Not Allowed");
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen return;
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen }
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen resp = http_server_response_create(req, 200, "OK");
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen http_server_response_submit(resp);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen}
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch/* location: /download/... */
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch
086b73efd1a5812a64acc951366a499d325509a6Stephan Boschstatic void
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Boschclient_handle_download_request(
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch struct client_request *creq,
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch const char *path)
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch{
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch struct http_server_request *req = creq->server_req;
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch const struct http_request *hreq =
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch http_server_request_get(req);
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch struct http_server_response *resp;
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch const char *fpath, *reason;
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch struct istream *fstream;
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch struct ostream *output;
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch unsigned int status;
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (strcmp(hreq->method, "GET") != 0) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen http_server_request_fail(req,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen 405, "Method Not Allowed");
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen fpath = t_strconcat(".", path, NULL);
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch if (debug) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_debug("test server: download: "
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen "sending payload for %s", fpath);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen fstream = test_file_open(fpath, &status, &reason);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (fstream == NULL) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen http_server_request_fail(req, status, reason);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return;
009217abb57a24a4076092e8e4e165545747839eStephan Bosch }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen resp = http_server_response_create(req, 200, "OK");
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen http_server_response_add_header(resp, "Content-Type", "text/plain");
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch if (blocking) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen output = http_server_response_get_payload_output(resp, TRUE);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (o_stream_send_istream(output, fstream) != OSTREAM_SEND_ISTREAM_RESULT_FINISHED) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_fatal("test server: download: "
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen "failed to send blocking file payload");
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (debug) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_debug("test server: download: "
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen "finished sending blocking payload for %s"
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen "(%"PRIuUOFF_T":%"PRIuUOFF_T")",
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen fpath, fstream->v_offset, output->offset);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch o_stream_close(output);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen o_stream_unref(&output);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen } else {
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi http_server_response_set_payload(resp, fstream);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen http_server_response_submit(resp);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch i_stream_unref(&fstream);
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch}
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch/* location: /echo */
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Boschstatic void
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Boschclient_request_finish_payload_in(struct client_request *creq)
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch{
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch struct http_server_response *resp;
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch struct istream *payload_input;
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch payload_input = iostream_temp_finish(&creq->payload_output, 4096);
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch resp = http_server_response_create
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch (creq->server_req, 200, "OK");
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch http_server_response_add_header(resp, "Content-Type", "text/plain");
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch http_server_response_set_payload(resp, payload_input);
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch http_server_response_submit(resp);
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch i_stream_unref(&payload_input);
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch}
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Boschstatic void
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Boschclient_request_read_echo(struct client_request *creq)
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch{
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch enum ostream_send_istream_result res;
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch o_stream_set_max_buffer_size(creq->payload_output, IO_BLOCK_SIZE);
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch res = o_stream_send_istream(creq->payload_output, creq->payload_input);
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch o_stream_set_max_buffer_size(creq->payload_output, (size_t)-1);
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch switch (res) {
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch break;
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
6793538c389d3e725456e3eabb697e2743233646Stephan Bosch case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
0d1b8b6bec79746c5d89d57dd8c1688946bd9237Josef 'Jeff' Sipek i_fatal("test server: echo: "
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen "Failed to read all echo payload [%s]", creq->path);
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen i_fatal("test server: echo: "
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen "Failed to write all echo payload [%s]", creq->path);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen client_request_finish_payload_in(creq);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_stream_unref(&creq->payload_input);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen}
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic void
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenclient_request_read_echo_more(struct client_request *creq)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen{
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen client_request_read_echo(creq);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
4487c66123ca4830f8afbf4efcd7a260848d0e05Timo Sirainen if (creq->payload_input != NULL)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return;
4487c66123ca4830f8afbf4efcd7a260848d0e05Timo Sirainen
4487c66123ca4830f8afbf4efcd7a260848d0e05Timo Sirainen io_remove(&creq->io);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (debug) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_debug("test server: echo: "
2c5c70e12365d7910848259f88eb237ce3a15947Timo Sirainen "finished receiving payload for %s", creq->path);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
cb3ab2fd5668700a89b274a43595cfbfa1616e4bTimo Sirainen}
cb3ab2fd5668700a89b274a43595cfbfa1616e4bTimo Sirainen
086b73efd1a5812a64acc951366a499d325509a6Stephan Boschstatic void
086b73efd1a5812a64acc951366a499d325509a6Stephan Boschclient_handle_echo_request(struct client_request *creq,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen const char *path)
086b73efd1a5812a64acc951366a499d325509a6Stephan Bosch{
086b73efd1a5812a64acc951366a499d325509a6Stephan Bosch struct http_server_request *req = creq->server_req;
086b73efd1a5812a64acc951366a499d325509a6Stephan Bosch const struct http_request *hreq =
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen http_server_request_get(req);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct http_server_response *resp;
7db7fbea5d8a07463b625f93d69166d56018dadfTimo Sirainen struct ostream *payload_output;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen uoff_t size;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen creq->path = p_strdup
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen (http_server_request_get_pool(req), path);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (strcmp(hreq->method, "PUT") != 0) {
a2d962e729a1c162145b86b3b6a666ef0a139e52Timo Sirainen http_server_request_fail(req,
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch 405, "Method Not Allowed");
0d1b8b6bec79746c5d89d57dd8c1688946bd9237Josef 'Jeff' Sipek return;
5f1d689131a75c39f064cbd4202373e7edf78f18Josef 'Jeff' Sipek }
9a84b90d894a741ae6e090de104d31382a41d0aaJosef 'Jeff' Sipek
9a84b90d894a741ae6e090de104d31382a41d0aaJosef 'Jeff' Sipek size = 0;
9a84b90d894a741ae6e090de104d31382a41d0aaJosef 'Jeff' Sipek (void)http_request_get_payload_size(hreq, &size);
9a84b90d894a741ae6e090de104d31382a41d0aaJosef 'Jeff' Sipek if (size == 0) {
33a80622828063f5be6f743855d5273fabe8ae58Timo Sirainen resp = http_server_response_create
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen (creq->server_req, 200, "OK");
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen http_server_response_add_header(resp, "Content-Type", "text/plain");
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen http_server_response_submit(resp);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return;
a618726eb3eb09a3866fe93208baf923d593f4d3Timo Sirainen }
a618726eb3eb09a3866fe93208baf923d593f4d3Timo Sirainen
a618726eb3eb09a3866fe93208baf923d593f4d3Timo Sirainen payload_output = iostream_temp_create
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen ("/tmp/test-http-server", 0);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (blocking) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct istream *payload_input;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen payload_input =
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen http_server_request_get_payload_input(req, TRUE);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (read_server_partial > 0) {
a238c6fede2022e5a4af707107ffb8f047b7753fTimo Sirainen struct istream *partial =
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_stream_create_limit(payload_input, read_server_partial);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_stream_unref(&payload_input);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen payload_input = partial;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (o_stream_send_istream(payload_output, payload_input) != OSTREAM_SEND_ISTREAM_RESULT_FINISHED) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_fatal("test server: echo: "
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen "failed to receive blocking echo payload");
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_stream_unref(&payload_input);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen payload_input = iostream_temp_finish(&payload_output, 4096);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (debug) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_debug("test server: echo: "
48325adac125d7ff275ec69b05b7a92be9637630Timo Sirainen "finished receiving blocking payload for %s", path);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen resp = http_server_response_create(req, 200, "OK");
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen http_server_response_add_header(resp, "Content-Type", "text/plain");
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen payload_output = http_server_response_get_payload_output(resp, TRUE);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (o_stream_send_istream(payload_output, payload_input) != OSTREAM_SEND_ISTREAM_RESULT_FINISHED) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_fatal("test server: echo: "
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen "failed to send blocking echo payload");
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (debug) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_debug("test server: echo: "
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen "finished sending blocking payload for %s", path);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_stream_unref(&payload_input);
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen o_stream_close(payload_output);
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen o_stream_unref(&payload_output);
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen } else {
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen creq->payload_output = payload_output;
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen switch (server_payload_handling) {
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen case PAYLOAD_HANDLING_LOW_LEVEL:
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen creq->payload_input =
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen http_server_request_get_payload_input(req, FALSE);
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen if (read_server_partial > 0) {
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen struct istream *partial =
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen i_stream_create_limit(creq->payload_input, read_server_partial);
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen i_stream_unref(&creq->payload_input);
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen creq->payload_input = partial;
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen }
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen creq->io = io_add_istream(creq->payload_input,
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen client_request_read_echo_more, creq);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen client_request_read_echo_more(creq);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen break;
d951320d498ae0800b677b754dde71574102123bTimo Sirainen case PAYLOAD_HANDLING_FORWARD:
d951320d498ae0800b677b754dde71574102123bTimo Sirainen http_server_request_forward_payload(req,
d951320d498ae0800b677b754dde71574102123bTimo Sirainen payload_output, (size_t)-1,
d951320d498ae0800b677b754dde71574102123bTimo Sirainen client_request_finish_payload_in, creq);
d951320d498ae0800b677b754dde71574102123bTimo Sirainen break;
d951320d498ae0800b677b754dde71574102123bTimo Sirainen case PAYLOAD_HANDLING_HANDLER:
d951320d498ae0800b677b754dde71574102123bTimo Sirainen creq->payload_input =
d951320d498ae0800b677b754dde71574102123bTimo Sirainen http_server_request_get_payload_input(req, FALSE);
d951320d498ae0800b677b754dde71574102123bTimo Sirainen http_server_request_handle_payload(req,
d951320d498ae0800b677b754dde71574102123bTimo Sirainen client_request_read_echo, creq);
d951320d498ae0800b677b754dde71574102123bTimo Sirainen break;
d951320d498ae0800b677b754dde71574102123bTimo Sirainen }
d951320d498ae0800b677b754dde71574102123bTimo Sirainen }
d951320d498ae0800b677b754dde71574102123bTimo Sirainen}
d951320d498ae0800b677b754dde71574102123bTimo Sirainen
d951320d498ae0800b677b754dde71574102123bTimo Sirainen/* request */
d951320d498ae0800b677b754dde71574102123bTimo Sirainen
d951320d498ae0800b677b754dde71574102123bTimo Sirainenstatic void
ddaf416216a83e71bc1bfc1b6faf2ead9d774613Stephan Boschhttp_server_request_destroyed(struct client_request *creq);
ddaf416216a83e71bc1bfc1b6faf2ead9d774613Stephan Bosch
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic struct client_request *
086b73efd1a5812a64acc951366a499d325509a6Stephan Boschclient_request_init(struct client *client,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct http_server_request *req)
ddaf416216a83e71bc1bfc1b6faf2ead9d774613Stephan Bosch{
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct client_request *creq;
ddaf416216a83e71bc1bfc1b6faf2ead9d774613Stephan Bosch pool_t pool = http_server_request_get_pool(req);
959eca738ab09d2760fd96ba329263fe589f84b0Timo Sirainen
ddaf416216a83e71bc1bfc1b6faf2ead9d774613Stephan Bosch http_server_request_ref(req);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
ddaf416216a83e71bc1bfc1b6faf2ead9d774613Stephan Bosch creq = p_new(pool, struct client_request, 1);
ddaf416216a83e71bc1bfc1b6faf2ead9d774613Stephan Bosch creq->client = client;
ddaf416216a83e71bc1bfc1b6faf2ead9d774613Stephan Bosch creq->server_req = req;
ddaf416216a83e71bc1bfc1b6faf2ead9d774613Stephan Bosch
ddaf416216a83e71bc1bfc1b6faf2ead9d774613Stephan Bosch http_server_request_set_destroy_callback(req,
ddaf416216a83e71bc1bfc1b6faf2ead9d774613Stephan Bosch http_server_request_destroyed, creq);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return creq;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen}
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic void client_request_deinit(struct client_request **_creq)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen{
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct client_request *creq = *_creq;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct http_server_request *req = creq->server_req;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen *_creq = NULL;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (creq->io != NULL) {
3e675de58a30e18fc973d4f0203ed3a4175c8ac5Aki Tuomi i_stream_unref(&creq->payload_input);
3e675de58a30e18fc973d4f0203ed3a4175c8ac5Aki Tuomi io_remove(&creq->io);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen http_server_request_unref(&req);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen}
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic void
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenhttp_server_request_destroyed(struct client_request *creq)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen{
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen client_request_deinit(&creq);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen}
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic void
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenclient_handle_request(void *context,
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct http_server_request *req)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen{
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen const struct http_request *hreq =
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen http_server_request_get(req);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen const char *path = hreq->target.url->path, *p;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct client *client = (struct client *)context;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen struct client_request *creq;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (debug) {
00b706a9ea136a5945f4ebafaa4ba958b641635dTimo Sirainen i_debug("test server: "
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen "request method=`%s' path=`%s'", hreq->method, path);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen }
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen creq = client_request_init(client, req);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen
if (strcmp(path, "/success") == 0) {
client_handle_success_request(creq);
return;
}
if ((p=strchr(path+1, '/')) == NULL) {
http_server_request_fail(req, 404, "Not found");
return;
}
if (strncmp(path, "/download", p-path) == 0) {
client_handle_download_request(creq, p);
return;
}
if (strncmp(path, "/echo", p-path) == 0) {
client_handle_echo_request(creq, p);
return;
}
http_server_request_fail(req, 404, "Not found");
return;
}
/* client connection */
static void
client_connection_destroy(void *context, const char *reason);
static const struct http_server_callbacks http_callbacks = {
.connection_destroy = client_connection_destroy,
.handle_request = client_handle_request
};
static void client_init(int fd)
{
struct client *client;
pool_t pool;
net_set_nonblock(fd, TRUE);
pool = pool_alloconly_create("client", 256);
client = p_new(pool, struct client, 1);
client->pool = pool;
client->http_conn = http_server_connection_create(http_server,
fd, fd, FALSE, &http_callbacks, client);
DLLIST_PREPEND(&clients, client);
}
static void client_deinit(struct client **_client)
{
struct client *client = *_client;
*_client = NULL;
DLLIST_REMOVE(&clients, client);
if (client->http_conn != NULL)
http_server_connection_close(&client->http_conn, "deinit");
pool_unref(&client->pool);
}
static void
client_connection_destroy(void *context, const char *reason ATTR_UNUSED)
{
struct client *client = context;
client->http_conn = NULL;
client_deinit(&client);
}
static void client_accept(void *context ATTR_UNUSED)
{
int fd;
/* accept new client */
fd = net_accept(fd_listen, NULL, NULL);
if (fd == -1)
return;
if (fd == -2) {
i_fatal("test server: accept() failed: %m");
}
client_init(fd);
}
/* */
static void
test_server_init(const struct http_server_settings *server_set)
{
/* open server socket */
io_listen = io_add(fd_listen,
IO_READ, client_accept, (void *)NULL);
http_server = http_server_init(server_set);
}
static void test_server_deinit(void)
{
/* close server socket */
io_remove(&io_listen);
/* deinitialize */
http_server_deinit(&http_server);
}
/*
* Test client
*/
struct test_client_request {
struct test_client_request *prev, *next;
struct http_client_request *hreq;
struct io *io;
struct istream *payload;
struct istream *file;
unsigned int files_idx;
};
static struct http_client *http_client;
static struct test_client_request *client_requests;
static unsigned int client_files_first, client_files_last;
static struct test_client_request *
test_client_request_new(void)
{
struct test_client_request *tcreq;
tcreq = i_new(struct test_client_request, 1);
DLLIST_PREPEND(&client_requests, tcreq);
return tcreq;
}
static void
test_client_request_destroy(struct test_client_request *tcreq)
{
if (tcreq->io != NULL)
io_remove(&tcreq->io);
if (tcreq->payload != NULL)
i_stream_unref(&tcreq->payload);
if (tcreq->file != NULL)
i_stream_unref(&tcreq->file);
DLLIST_REMOVE(&client_requests, tcreq);
i_free(tcreq);
}
static void
test_client_requests_switch_ioloop(void)
{
struct test_client_request *tcreq;
for (tcreq = client_requests; tcreq != NULL;
tcreq = tcreq->next) {
if (tcreq->io != NULL)
tcreq->io = io_loop_move_io(&tcreq->io);
if (tcreq->payload != NULL)
i_stream_switch_ioloop(tcreq->payload);
}
}
/* download */
static void test_client_download_continue(void);
static void
test_client_download_finished(unsigned int files_idx)
{
const char **paths;
unsigned int count;
paths = array_get_modifiable(&files, &count);
i_assert(files_idx < count);
i_assert(client_files_first < count);
i_assert(paths[files_idx] != NULL);
paths[files_idx] = NULL;
test_client_download_continue();
}
static void
test_client_download_payload_input(struct test_client_request *tcreq)
{
struct istream *payload = tcreq->payload;
const unsigned char *pdata, *fdata;
size_t psize, fsize, pleft;
unsigned int files_idx = tcreq->files_idx;
off_t ret;
/* read payload */
while ((ret=i_stream_read_more(payload, &pdata, &psize)) > 0) {
if (debug) {
i_debug("test client: download: "
"got data for [%u] (size=%d)",
tcreq->files_idx, (int)psize);
}
/* compare with file on disk */
pleft = psize;
while ((ret=i_stream_read_more
(tcreq->file, &fdata, &fsize)) > 0 && pleft > 0) {
fsize = (fsize > pleft ? pleft : fsize);
if (memcmp(pdata, fdata, fsize) != 0) {
i_fatal("test client: download: "
"received data does not match file "
"(%"PRIuUOFF_T":%"PRIuUOFF_T")",
payload->v_offset, tcreq->file->v_offset);
}
i_stream_skip(tcreq->file, fsize);
pleft -= fsize;
pdata += fsize;
}
if (ret < 0 && tcreq->file->stream_errno != 0) {
i_fatal("test client: download: "
"failed to read file: %s", i_stream_get_error(tcreq->file));
}
i_stream_skip(payload, psize);
}
if (ret == 0) {
if (debug) {
i_debug("test client: download: "
"need more data for [%u]",
tcreq->files_idx);
}
/* we will be called again for this request */
} else {
(void)i_stream_read(tcreq->file);
if (payload->stream_errno != 0) {
i_fatal("test client: download: "
"failed to read request payload: %s",
i_stream_get_error(payload));
} if (i_stream_have_bytes_left(tcreq->file)) {
if (i_stream_read_more(tcreq->file, &fdata, &fsize) <= 0)
fsize = 0;
i_fatal("test client: download: "
"payload ended prematurely "
"(at least %"PRIuSIZE_T" bytes left)", fsize);
} else if (debug) {
i_debug("test client: download: "
"finished request for [%u]",
tcreq->files_idx);
}
/* dereference payload stream; finishes the request */
tcreq->payload = NULL;
io_remove(&tcreq->io); /* holds a reference too */
i_stream_unref(&payload);
/* finished */
test_client_download_finished(files_idx);
}
}
static void
test_client_download_response(const struct http_response *resp,
struct test_client_request *tcreq)
{
const char **paths;
const char *path;
unsigned int count, status;
struct istream *fstream;
const char *reason;
if (debug) {
i_debug("test client: download: "
"got response for [%u]",
tcreq->files_idx);
}
paths = array_get_modifiable(&files, &count);
i_assert(tcreq->files_idx < count);
i_assert(client_files_first < count);
path = paths[tcreq->files_idx];
i_assert(path != NULL);
if (debug) {
i_debug("test client: download: "
"path for [%u]: %s",
tcreq->files_idx, path);
}
fstream = test_file_open(path, &status, &reason);
i_assert(fstream != NULL);
if (status != resp->status) {
i_fatal("test client: download: "
"got wrong response for %s: %u %s (expected: %u %s)",
path, resp->status, resp->reason, status, reason);
}
if (resp->status / 100 != 2) {
if (debug) {
i_debug("test client: download: "
"HTTP request for %s failed: %u %s",
path, resp->status, resp->reason);
}
i_stream_unref(&fstream);
test_client_download_finished(tcreq->files_idx);
return;
}
if (resp->payload == NULL) {
if (debug) {
i_debug("test client: download: "
"no payload for %s [%u]",
path, tcreq->files_idx);
}
i_stream_unref(&fstream);
test_client_download_finished(tcreq->files_idx);
return;
}
i_assert(fstream != NULL);
if (read_client_partial == 0) {
i_stream_ref(resp->payload);
tcreq->payload = resp->payload;
tcreq->file = fstream;
} else {
struct istream *payload = resp->payload;
tcreq->payload = i_stream_create_limit
(payload, read_client_partial);
tcreq->file = i_stream_create_limit
(fstream, read_client_partial);
i_stream_unref(&fstream);
}
tcreq->io = io_add_istream(tcreq->payload,
test_client_download_payload_input, tcreq);
test_client_download_payload_input(tcreq);
}
static void test_client_download_continue(void)
{
struct test_client_request *tcreq;
struct http_client_request *hreq;
const char *const *paths;
unsigned int count;
paths = array_get(&files, &count);
i_assert(client_files_first <= count);
i_assert(client_files_last <= count);
i_assert(client_files_first <= client_files_last);
for (; client_files_first < client_files_last &&
paths[client_files_first] == NULL; client_files_first++)
if (debug) {
i_debug("test client: download: "
"received until [%u]",
client_files_first-1);
}
if (client_files_first >= count) {
io_loop_stop(current_ioloop);
return;
}
for (; client_files_last < count &&
(client_files_last - client_files_first) < test_max_pending;
client_files_last++) {
const char *path = paths[client_files_last];
tcreq = test_client_request_new();
tcreq->files_idx = client_files_last;
if (debug) {
i_debug("test client: download: "
"retrieving %s [%u]",
path, tcreq->files_idx);
}
hreq = tcreq->hreq = http_client_request(http_client,
"GET", net_ip2addr(&bind_ip),
t_strconcat("/download/", path, NULL),
test_client_download_response, tcreq);
http_client_request_set_port(hreq, bind_port);
http_client_request_set_destroy_callback(hreq,
test_client_request_destroy, tcreq);
http_client_request_submit(hreq);
}
}
static void
test_client_download(const struct http_client_settings *client_set)
{
/* create client */
http_client = http_client_init(client_set);
/* start querying server */
client_files_first = client_files_last = 0;
test_client_download_continue();
}
/* echo */
static void test_client_echo_continue(void);
static void
test_client_echo_finished(unsigned int files_idx)
{
const char **paths;
unsigned int count;
paths = array_get_modifiable(&files, &count);
i_assert(files_idx < count);
i_assert(client_files_first < count);
i_assert(paths[files_idx] != NULL);
paths[files_idx] = NULL;
test_client_echo_continue();
}
static void
test_client_echo_payload_input(struct test_client_request *tcreq)
{
struct istream *payload = tcreq->payload;
const unsigned char *pdata, *fdata;
size_t psize, fsize, pleft;
unsigned int files_idx = tcreq->files_idx;
off_t ret;
/* read payload */
while ((ret=i_stream_read_more(payload, &pdata, &psize)) > 0) {
if (debug) {
i_debug("test client: echo: "
"got data for [%u] (size=%d)",
tcreq->files_idx, (int)psize);
}
/* compare with file on disk */
pleft = psize;
while ((ret=i_stream_read_more
(tcreq->file, &fdata, &fsize)) > 0 && pleft > 0) {
fsize = (fsize > pleft ? pleft : fsize);
if (memcmp(pdata, fdata, fsize) != 0) {
i_fatal("test client: echo: "
"received data does not match file "
"(%"PRIuUOFF_T":%"PRIuUOFF_T")",
payload->v_offset, tcreq->file->v_offset);
}
i_stream_skip(tcreq->file, fsize);
pleft -= fsize;
pdata += fsize;
}
if (ret < 0 && tcreq->file->stream_errno != 0) {
i_fatal("test client: echo: "
"failed to read file: %s", i_stream_get_error(tcreq->file));
}
i_stream_skip(payload, psize);
}
if (ret == 0) {
if (debug) {
i_debug("test client: echo: "
"need more data for [%u]",
tcreq->files_idx);
}
/* we will be called again for this request */
} else {
(void)i_stream_read(tcreq->file);
if (payload->stream_errno != 0) {
i_fatal("test client: echo: "
"failed to read request payload: %s",
i_stream_get_error(payload));
} if (i_stream_have_bytes_left(tcreq->file)) {
if (i_stream_read_more(tcreq->file, &fdata, &fsize) <= 0)
fsize = 0;
i_fatal("test client: echo: "
"payload ended prematurely "
"(at least %"PRIuSIZE_T" bytes left)", fsize);
} else if (debug) {
i_debug("test client: echo: "
"finished request for [%u]",
tcreq->files_idx);
}
/* dereference payload stream; finishes the request */
tcreq->payload = NULL;
io_remove(&tcreq->io); /* holds a reference too */
i_stream_unref(&payload);
/* finished */
test_client_echo_finished(files_idx);
}
}
static void
test_client_echo_response(const struct http_response *resp,
struct test_client_request *tcreq)
{
const char **paths;
const char *path;
unsigned int count, status;
struct istream *fstream;
if (debug) {
i_debug("test client: echo: "
"got response for [%u]",
tcreq->files_idx);
}
paths = array_get_modifiable(&files, &count);
i_assert(tcreq->files_idx < count);
i_assert(client_files_first < count);
path = paths[tcreq->files_idx];
i_assert(path != NULL);
if (debug) {
i_debug("test client: echo: "
"path for [%u]: %s",
tcreq->files_idx, path);
}
if (resp->status / 100 != 2) {
i_fatal("test client: echo: "
"HTTP request for %s failed: %u %s",
path, resp->status, resp->reason);
}
fstream = test_file_open(path, &status, NULL);
if (fstream == NULL) {
i_fatal("test client: echo: "
"failed to open %s", path);
}
if (read_server_partial > 0) {
struct istream *partial = i_stream_create_limit
(fstream, read_server_partial);
i_stream_unref(&fstream);
fstream = partial;
}
if (resp->payload == NULL) {
// FIXME: check file is empty
if (debug) {
i_debug("test client: echo: "
"no payload for %s [%u]",
path, tcreq->files_idx);
}
i_stream_unref(&fstream);
test_client_echo_finished(tcreq->files_idx);
return;
}
i_assert(fstream != NULL);
tcreq->file = fstream;
i_stream_ref(resp->payload);
tcreq->payload = resp->payload;
tcreq->io = io_add_istream(resp->payload,
test_client_echo_payload_input, tcreq);
test_client_echo_payload_input(tcreq);
}
static void test_client_echo_continue(void)
{
struct test_client_request *tcreq;
struct http_client_request *hreq;
const char **paths;
unsigned int count, first_submitted;
paths = array_get_modifiable(&files, &count);
i_assert(client_files_first <= count);
i_assert(client_files_last <= count);
i_assert(client_files_first <= client_files_last);
for (; client_files_first < client_files_last &&
paths[client_files_first] == NULL; client_files_first++);
if (debug) {
i_debug("test client: echo: "
"received until [%u/%u]", client_files_first-1, count);
}
if (debug && client_files_first < count) {
const char *path = paths[client_files_first];
i_debug("test client: echo: "
"next blocking: %s [%d]",
(path == NULL ? "none" : path),
client_files_first);
}
if (client_files_first >= count) {
io_loop_stop(current_ioloop);
return;
}
first_submitted = client_files_last;
for (; client_files_last < count &&
(client_files_last - client_files_first) < test_max_pending;
client_files_last++) {
struct istream *fstream;
const char *path = paths[client_files_last];
fstream = test_file_open(path, NULL, NULL);
if (fstream == NULL) {
paths[client_files_last] = NULL;
if (debug) {
i_debug("test client: echo: "
"skipping %s [%u]",
path, client_files_last);
}
continue;
}
if (debug) {
i_debug("test client: echo: "
"retrieving %s [%u]",
path, client_files_last);
}
tcreq = test_client_request_new();
tcreq->files_idx = client_files_last;
hreq = tcreq->hreq = http_client_request(http_client,
"PUT", net_ip2addr(&bind_ip),
t_strconcat("/echo/", path, NULL),
test_client_echo_response, tcreq);
http_client_request_set_port(hreq, bind_port);
http_client_request_set_payload
(hreq, fstream, request_100_continue);
http_client_request_set_destroy_callback(hreq,
test_client_request_destroy, tcreq);
http_client_request_submit(hreq);
i_stream_unref(&fstream);
}
/* run nested ioloop (if requested) if new requests cross a nesting
boundary */
if (ioloop_nested != NULL) {
unsigned int i;
i_assert(ioloop_nested_first <= count);
i_assert(ioloop_nested_last <= count);
for (i = ioloop_nested_first; i < ioloop_nested_last; i++) {
if (paths[i] != NULL) {
if (debug) {
i_debug("test client: "
"not leaving ioloop [%u]", i);
}
break;
}
}
if (i == ioloop_nested_last)
io_loop_stop(ioloop_nested);
} else if (client_ioloop_nesting > 0 &&
((client_files_last / client_ioloop_nesting) !=
(first_submitted / client_ioloop_nesting)) ) {
struct ioloop *prev_ioloop = current_ioloop;
ioloop_nested_first = first_submitted;
ioloop_nested_last = first_submitted + client_ioloop_nesting;
if (ioloop_nested_last > client_files_last)
ioloop_nested_last = client_files_last;
if (debug) {
i_debug("test client: echo: entering ioloop for %u...%u (depth=%u)",
ioloop_nested_first, ioloop_nested_last, ioloop_nested_depth);
}
ioloop_nested_depth++;
ioloop_nested = io_loop_create();
http_client_switch_ioloop(http_client);
test_client_requests_switch_ioloop();
io_loop_run(ioloop_nested);
io_loop_set_current(prev_ioloop);
http_client_switch_ioloop(http_client);
test_client_requests_switch_ioloop();
io_loop_set_current(ioloop_nested);
io_loop_destroy(&ioloop_nested);
ioloop_nested = NULL;
ioloop_nested_depth--;
if (debug) {
i_debug("test client: echo: leaving ioloop for %u...%u (depth=%u)",
ioloop_nested_first, ioloop_nested_last, ioloop_nested_depth);
}
ioloop_nested_first = ioloop_nested_last = 0;
if (client_files_first >= count) {
io_loop_stop(current_ioloop);
return;
}
}
}
static void
test_client_echo(const struct http_client_settings *client_set)
{
/* create client */
http_client = http_client_init(client_set);
/* start querying server */
client_files_first = client_files_last = 0;
test_client_echo_continue();
}
/* cleanup */
static void test_client_deinit(void)
{
http_client_deinit(&http_client);
}
/*
* Tests
*/
static void test_open_server_fd(void)
{
if (fd_listen != -1)
i_close_fd(&fd_listen);
fd_listen = net_listen(&bind_ip, &bind_port, 128);
if (fd_listen == -1) {
i_fatal("listen(%s:%u) failed: %m",
net_ip2addr(&bind_ip), bind_port);
}
}
static void test_server_kill(void)
{
if (server_pid != (pid_t)-1) {
(void)kill(server_pid, SIGKILL);
(void)waitpid(server_pid, NULL, 0);
}
server_pid = (pid_t)-1;
}
static void test_run_client_server(
const struct http_client_settings *client_set,
const struct http_server_settings *server_set,
void (*client_init)(const struct http_client_settings *client_set))
{
struct ioloop *ioloop;
test_open_server_fd();
if ((server_pid = fork()) == (pid_t)-1)
i_fatal("fork() failed: %m");
if (server_pid == 0) {
server_pid = (pid_t)-1;
hostpid_init();
if (debug)
i_debug("server: PID=%s", my_pid);
/* child: server */
ioloop_nested = NULL;
ioloop_nested_depth = 0;
ioloop = io_loop_create();
test_server_init(server_set);
io_loop_run(ioloop);
test_server_deinit();
io_loop_destroy(&ioloop);
i_close_fd(&fd_listen);
} else {
if (debug)
i_debug("client: PID=%s", my_pid);
i_close_fd(&fd_listen);
/* parent: client */
ioloop_nested = NULL;
ioloop_nested_depth = 0;
ioloop = io_loop_create();
client_init(client_set);
io_loop_run(ioloop);
test_client_deinit();
io_loop_destroy(&ioloop);
test_server_kill();
}
}
static void test_run_sequential(
void (*client_init)(const struct http_client_settings *client_set))
{
struct http_server_settings http_server_set;
struct http_client_settings http_client_set;
/* download files from blocking server */
/* server settings */
i_zero(&http_server_set);
http_server_set.max_pipelined_requests = 0;
http_server_set.debug = debug;
http_server_set.request_limits.max_payload_size = (uoff_t)-1;
/* client settings */
i_zero(&http_client_set);
http_client_set.max_idle_time_msecs = 5*1000;
http_client_set.max_parallel_connections = 1;
http_client_set.max_pipelined_requests = 1;
http_client_set.max_redirects = 0;
http_client_set.max_attempts = 1;
http_client_set.debug = debug;
test_files_init();
test_run_client_server
(&http_client_set, &http_server_set, client_init);
test_files_deinit();
test_out("sequential", TRUE);
}
static void test_run_pipeline(
void (*client_init)(const struct http_client_settings *client_set))
{
struct http_server_settings http_server_set;
struct http_client_settings http_client_set;
/* download files from blocking server */
/* server settings */
i_zero(&http_server_set);
http_server_set.max_pipelined_requests = 4;
http_server_set.debug = debug;
http_server_set.request_limits.max_payload_size = (uoff_t)-1;
/* client settings */
i_zero(&http_client_set);
http_client_set.max_idle_time_msecs = 5*1000;
http_client_set.max_parallel_connections = 1;
http_client_set.max_pipelined_requests = 8;
http_client_set.max_redirects = 0;
http_client_set.max_attempts = 1;
http_client_set.debug = debug;
test_files_init();
test_run_client_server
(&http_client_set, &http_server_set, client_init);
test_files_deinit();
test_out("pipeline", TRUE);
}
static void test_run_parallel(
void (*client_init)(const struct http_client_settings *client_set))
{
struct http_server_settings http_server_set;
struct http_client_settings http_client_set;
/* download files from blocking server */
/* server settings */
i_zero(&http_server_set);
http_server_set.max_pipelined_requests = 4;
http_server_set.debug = debug;
http_server_set.request_limits.max_payload_size = (uoff_t)-1;
/* client settings */
i_zero(&http_client_set);
http_client_set.max_idle_time_msecs = 5*1000;
http_client_set.max_parallel_connections = 40;
http_client_set.max_pipelined_requests = 8;
http_client_set.max_redirects = 0;
http_client_set.max_attempts = 1;
http_client_set.debug = debug;
test_files_init();
test_run_client_server
(&http_client_set, &http_server_set, client_init);
test_files_deinit();
test_out("parallel", TRUE);
}
static void test_download_server_nonblocking(void)
{
test_begin("http payload download (server non-blocking)");
blocking = FALSE;
request_100_continue = FALSE;
read_server_partial = 0;
client_ioloop_nesting = 0;
server_payload_handling = PAYLOAD_HANDLING_FORWARD;
test_run_sequential(test_client_download);
test_run_pipeline(test_client_download);
test_run_parallel(test_client_download);
test_end();
}
static void test_download_server_blocking(void)
{
test_begin("http payload download (server blocking)");
blocking = TRUE;
request_100_continue = FALSE;
read_server_partial = 0;
client_ioloop_nesting = 0;
test_run_sequential(test_client_download);
test_run_pipeline(test_client_download);
test_run_parallel(test_client_download);
test_end();
}
static void test_echo_server_nonblocking(void)
{
test_begin("http payload echo (server non-blocking)");
blocking = FALSE;
request_100_continue = FALSE;
read_server_partial = 0;
client_ioloop_nesting = 0;
server_payload_handling = PAYLOAD_HANDLING_FORWARD;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
test_end();
test_begin("http payload echo (server non-blocking; low-level)");
blocking = FALSE;
request_100_continue = FALSE;
read_server_partial = 0;
client_ioloop_nesting = 0;
server_payload_handling = PAYLOAD_HANDLING_LOW_LEVEL;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
test_end();
test_begin("http payload echo (server non-blocking; handler)");
blocking = FALSE;
request_100_continue = FALSE;
read_server_partial = 0;
client_ioloop_nesting = 0;
server_payload_handling = PAYLOAD_HANDLING_HANDLER;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
test_end();
}
static void test_echo_server_blocking(void)
{
test_begin("http payload echo (server blocking)");
blocking = TRUE;
request_100_continue = FALSE;
read_server_partial = 0;
client_ioloop_nesting = 0;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
test_end();
}
static void test_echo_server_nonblocking_sync(void)
{
test_begin("http payload echo (server non-blocking; 100-continue)");
blocking = FALSE;
request_100_continue = TRUE;
read_server_partial = 0;
client_ioloop_nesting = 0;
server_payload_handling = PAYLOAD_HANDLING_FORWARD;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
test_end();
test_begin("http payload echo (server non-blocking; 100-continue; low-level)");
blocking = FALSE;
request_100_continue = TRUE;
read_server_partial = 0;
client_ioloop_nesting = 0;
server_payload_handling = PAYLOAD_HANDLING_LOW_LEVEL;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
test_end();
test_begin("http payload echo (server non-blocking; 100-continue; handler)");
blocking = FALSE;
request_100_continue = TRUE;
read_server_partial = 0;
client_ioloop_nesting = 0;
server_payload_handling = PAYLOAD_HANDLING_HANDLER;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
test_end();
}
static void test_echo_server_blocking_sync(void)
{
test_begin("http payload echo (server blocking; 100-continue)");
blocking = TRUE;
request_100_continue = TRUE;
read_server_partial = 0;
client_ioloop_nesting = 0;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
test_end();
}
static void test_echo_server_nonblocking_partial(void)
{
test_begin("http payload echo (server non-blocking; partial short)");
blocking = FALSE;
request_100_continue = FALSE;
read_server_partial = 1024;
client_ioloop_nesting = 0;
server_payload_handling = PAYLOAD_HANDLING_FORWARD;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
test_end();
test_begin("http payload echo (server non-blocking; partial long)");
read_server_partial = IO_BLOCK_SIZE + 1024;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
test_end();
test_begin("http payload echo (server non-blocking; partial short; low-level)");
blocking = FALSE;
request_100_continue = FALSE;
read_server_partial = 1024;
client_ioloop_nesting = 0;
server_payload_handling = PAYLOAD_HANDLING_LOW_LEVEL;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
test_end();
test_begin("http payload echo (server non-blocking; partial long; low-level)");
read_server_partial = IO_BLOCK_SIZE + 1024;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
test_end();
test_begin("http payload echo (server non-blocking; partial short; handler)");
blocking = FALSE;
request_100_continue = FALSE;
read_server_partial = 1024;
client_ioloop_nesting = 0;
server_payload_handling = PAYLOAD_HANDLING_HANDLER;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
test_end();
test_begin("http payload echo (server non-blocking; partial long; handler)");
read_server_partial = IO_BLOCK_SIZE + 1024;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
test_end();
}
static void test_echo_server_blocking_partial(void)
{
test_begin("http payload echo (server blocking; partial short)");
blocking = TRUE;
request_100_continue = FALSE;
read_server_partial = 1024;
client_ioloop_nesting = 0;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
test_end();
test_begin("http payload echo (server blocking; partial long)");
read_server_partial = IO_BLOCK_SIZE + 1024;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
test_end();
}
static void test_download_client_partial(void)
{
test_begin("http payload download (client partial)");
blocking = FALSE;
request_100_continue = FALSE;
read_server_partial = 0;
read_client_partial = 1024;
client_ioloop_nesting = 0;
server_payload_handling = PAYLOAD_HANDLING_FORWARD;
test_run_sequential(test_client_download);
test_run_pipeline(test_client_download);
test_run_parallel(test_client_download);
test_end();
test_begin("http payload download (client partial long)");
blocking = FALSE;
request_100_continue = FALSE;
read_server_partial = 0;
read_client_partial = IO_BLOCK_SIZE + 1024;
server_payload_handling = PAYLOAD_HANDLING_FORWARD;
test_run_sequential(test_client_download);
test_run_pipeline(test_client_download);
test_run_parallel(test_client_download);
test_end();
}
static void test_download_client_nested_ioloop(void)
{
test_begin("http payload echo (client nested ioloop)");
blocking = FALSE;
request_100_continue = FALSE;
read_server_partial = 0;
read_client_partial = 0;
client_ioloop_nesting = 10;
server_payload_handling = PAYLOAD_HANDLING_FORWARD;
test_run_parallel(test_client_echo);
test_end();
}
static void (*const test_functions[])(void) = {
test_download_server_nonblocking,
test_download_server_blocking,
test_echo_server_nonblocking,
test_echo_server_blocking,
test_echo_server_nonblocking_sync,
test_echo_server_blocking_sync,
test_echo_server_nonblocking_partial,
test_echo_server_blocking_partial,
test_download_client_partial,
test_download_client_nested_ioloop,
NULL
};
/*
* Main
*/
volatile sig_atomic_t terminating = 0;
static void
test_signal_handler(int signo)
{
if (terminating != 0)
raise(signo);
terminating = 1;
/* make sure we don't leave any pesky children alive */
test_server_kill();
(void)signal(signo, SIG_DFL);
raise(signo);
}
static void test_atexit(void)
{
test_server_kill();
}
int main(int argc, char *argv[])
{
int c;
atexit(test_atexit);
(void)signal(SIGCHLD, SIG_IGN);
(void)signal(SIGTERM, test_signal_handler);
(void)signal(SIGQUIT, test_signal_handler);
(void)signal(SIGINT, test_signal_handler);
(void)signal(SIGSEGV, test_signal_handler);
(void)signal(SIGABRT, test_signal_handler);
while ((c = getopt(argc, argv, "D")) > 0) {
switch (c) {
case 'D':
debug = TRUE;
break;
default:
i_fatal("Usage: %s [-D]", argv[0]);
}
}
/* listen on localhost */
i_zero(&bind_ip);
bind_ip.family = AF_INET;
bind_ip.u.ip4.s_addr = htonl(INADDR_LOOPBACK);
test_run(test_functions);
}