/* This file is part of libmicrohttpd (C) 2007 Christian Grothoff (C) 2012 Bertrand Baudet libmicrohttpd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. libmicrohttpd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with libmicrohttpd; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** * @file daemontest_upgrade.c * @brief Testcase for libmicrohttpd 'Connection: Upgrade' header field. * @author Bertrand Baudet * @author Christian Grothoff */ #include "MHD_config.h" #include "platform.h" #include #include #include #include #include #ifndef WINDOWS #include #endif static int oneone; struct CBC { char *buf; size_t pos; size_t size; }; /** */ static size_t copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) { struct CBC *cbc = ctx; if (cbc->pos + size * nmemb > cbc->size) return 0; /* overflow */ memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); cbc->pos += size * nmemb; return size * nmemb; } /** */ static int mhd_socket = -1; static struct MHD_Daemon *mhd_daemon; /** */ static curl_socket_t curl_open_function(void *clientp, curlsocktype purpose, struct curl_sockaddr *address) { printf("%s\n", __FUNCTION__); return mhd_socket; } /** */ static curl_socket_t curl_sockopt(void *clientp, curlsocktype purpose, struct curl_sockaddr *address) { printf("%s\n", __FUNCTION__); return CURL_SOCKOPT_ALREADY_CONNECTED; } /** */ static int test_socket(const char *fnct, int sock) { int ret; ret = fcntl(sock, F_GETFL); printf("%s: fcntl (socket[%d]) = %d\n", fnct, sock, ret); if (ret == -1) { printf("Failed to get socket status\n"); return -1; } else { if (ret & O_RDONLY) printf("Socket is READ ONLY\n"); else if (ret & O_WRONLY) printf("Socket is WRITE ONLY\n"); else if (ret & O_RDWR) printf("Socket is READ WRITE\n"); } return 0; } /** */ static int upgrade_cb (void *cls, struct MHD_Connection *connection, void **con_cls, int socket) { CURLcode errornum; CURL *c; int ret; printf("%s: cls: %p -- connection: %p -- con_cls: %p -- socket: %d\n", __FUNCTION__, cls, connection, con_cls, socket); mhd_socket = socket; test_socket(__FUNCTION__, mhd_socket); ret = MHD_add_connection(mhd_daemon, mhd_socket, NULL, 0); if (ret == MHD_NO) { printf("Failed to add connection\n"); } c = curl_easy_init (); curl_easy_setopt (c, CURLOPT_OPENSOCKETFUNCTION, &curl_open_function); curl_easy_setopt (c, CURLOPT_SOCKOPTFUNCTION, &curl_sockopt); curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:12345/event"); if (oneone) curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); else curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); errornum = curl_easy_perform (c); if (CURLE_OK != errornum) { fprintf (stderr, "%s: curl_easy_perform failed: [%d]:'%s'\n", __FUNCTION__, errornum, curl_easy_strerror (errornum)); } return MHD_YES; } /** * Connection upgrade requested? * Check for headers: * Connection: Upgrade * Upgrade: * * @param connection connection to test * @param proto returns the protocol value from the 'Upgrade' header */ static int connection_upgrade_requested (struct MHD_Connection *connection, char **proto) { const char *h_value; h_value = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONNECTION); if (h_value == NULL) return 0; if (0 != strcasecmp (h_value, MHD_HTTP_HEADER_UPGRADE)) return 0; h_value = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_UPGRADE); if (h_value == NULL) return 0; *proto = (char*)h_value; return 1; } /** */ static int ahc_echo (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) { static int aptr; struct MHD_Response *response; char *proto = NULL; int upgrade; int ret; printf("%s: %s %s\n", __FUNCTION__, method, url); // if (0 != strcmp (method, MHD_HTTP_METHOD_POST)) // { // printf ("METHOD: %s\n", method); // return MHD_NO; /* unexpected method */ // } if (&aptr != *ptr) { *ptr = &aptr; return MHD_YES; } *ptr = NULL; /* reset when done */ upgrade = connection_upgrade_requested(connection, &proto); if (upgrade == 1) { response = MHD_create_response_for_upgrade(upgrade_cb, NULL); ret = MHD_add_response_header(response, MHD_HTTP_HEADER_CONNECTION, MHD_HTTP_HEADER_UPGRADE); ret = MHD_add_response_header(response, MHD_HTTP_HEADER_UPGRADE, proto); //ret = MHD_queue_response (connection, MHD_HTTP_SWITCHING_PROTOCOLS, response); ret = MHD_queue_response (connection, MHD_HTTP_OK, response); } else { response = MHD_create_response_from_buffer (0, (void *) url, MHD_RESPMEM_MUST_COPY); ret = MHD_queue_response (connection, MHD_HTTP_OK, response); } MHD_destroy_response (response); return MHD_YES; } /** */ static int testUpgrade () { CURL *c; char buf[2048]; struct CBC cbc; CURLcode errornum; struct curl_slist *header = NULL; int http_code; int ret = 0; cbc.buf = buf; cbc.size = 2048; cbc.pos = 0; // mhd_daemon = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, mhd_daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, 12345, NULL, NULL, &ahc_echo, NULL, // MHD_OPTION_CONNECTION_UPGRADE, &upgrade_cb, NULL, MHD_OPTION_END); if (mhd_daemon == NULL) return 1; c = curl_easy_init (); header = curl_slist_append (header, "Upgrade: PTTH/1.0"); header = curl_slist_append (header, "Connection: Upgrade"); curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:12345/upgrade"); curl_easy_setopt (c, CURLOPT_HTTPHEADER, header); curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, 0); curl_easy_setopt (c, CURLOPT_NOBODY, 1); curl_easy_setopt (c, CURLOPT_POST, 1L); curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); if (oneone) curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); else curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L); // NOTE: use of CONNECTTIMEOUT without also // setting NOSIGNAL results in really weird // crashes on my system! curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); errornum = curl_easy_perform (c); curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &http_code); printf("HTTP code: %d\n", http_code); if (CURLE_OK != errornum) { fprintf (stderr, "curl_easy_perform failed: [%d]:'%s'\n", errornum, curl_easy_strerror (errornum)); ret = 2; } printf("Cleanup\n"); curl_slist_free_all (header); curl_easy_cleanup (c); MHD_stop_daemon (mhd_daemon); return ret; } int main (int argc, char *const *argv) { unsigned int errorCount = 0; oneone = NULL != strstr (argv[0], "11"); if (0 != curl_global_init (CURL_GLOBAL_WIN32)) return 2; errorCount += testUpgrade (); if (errorCount != 0) fprintf (stderr, "Error (code: %u)\n", errorCount); curl_global_cleanup (); return errorCount != 0; /* 0 == pass */ }